Validación inline: al fallar un submit por campos required vacíos, el form los marca (label destructivo + mensaje debajo), no sólo un toast. MetaApp.form_errors + validate_required_fields. Secciones de formulario: FieldSpec.section agrupa campos bajo encabezados; abrir_form del CRM las usa. Campos condicionales y pulido puramente visual: scope-out conciente. El plan docs/nakui-erp-masterplan.md queda completo (7/7 fases). Tests verdes (meta-schema 16, meta-runtime 70, meta-form 8, nakui-ui 14); clippy limpio en las libs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
54 KiB
Changelog — nahual
Motor GPUI: libs + widgets. Renombrado de yahweh el 2026-05-19.
feat(meta-form): pulido de formularios (Fase 7 del ERP nakui)
Cierre del plan ERP. Pulido de la metainterfaz:
- Validación inline — al fallar un submit por campos
requiredvacíos, el form ya no sólo muestra un toast genérico: marca cada campo faltante (label en color destructivo + mensaje debajo).MetaApp.form_errors+validate_required_fields.AutoIdse exime (se autogenera). - Secciones de formulario —
FieldSpec.sectionagrupa campos consecutivos bajo un encabezado en el render del formulario.
feat(meta-form): export CSV de listas (Fase 6 del ERP nakui)
- Toda vista de lista gana un botón «⬇ CSV» que exporta las filas
filtradas y ordenadas (todas, no sólo la página) a un archivo
<entity>-<timestamp>.csven el directorio de trabajo; avisa por toast con la ruta. - Las celdas exportadas usan el mismo render que la lista — referencias resueltas a su nombre, montos formateados.
- Serializador
to_csv(RFC 4180, con escape de comas/comillas/saltos) en el módulo nuevometa-runtime/csv.rs, con tests. - Refactor:
list_filtered_sortedextraído como helper compartido entre el render de la lista y el export.
feat(meta-*): tablero de KPIs (Fase 5 del ERP nakui)
Cuarta clase de vista de la metainterfaz:
View::Dashboard(DashboardView)— grilla de tarjetas de KPI. CadaDashboardCarddeclara suentity, sumetricy unfilteropcional.Metric—Count,Sum { field }yGroupBy { field }.compute_metric(nuevo módulometa-runtime/metric.rs) los computa:MetricResult::Scalarpara conteos/sumas,Breakdown(ranking) para agrupaciones.CardFilterrestringe los records contados.meta-form:render_dashboard— tarjetas con el número grande (formateado víaformat_value) o un breakdown con barras de texto.
Tests de compute_metric en meta-runtime; verificación del tablero
del CRM en nakui-ui.
feat(meta-form): listas profesionales — orden, búsqueda, paginación
Fase 4 del ERP nakui. Las vistas de lista de meta-form ganan:
- Orden por columna — clic en un header cicla ascendente →
descendente → sin orden (indicador ▲/▼). Comparación de valores con
cmp_values(nuevo enmeta-runtime): números por valor, strings case-insensitive,nullprimero. - Búsqueda en vivo — caja 🔍 que filtra por substring contra las
columnas de
search_inmientras se teclea (víacx.observedelTextInput).search_inya existía en el schema; ahora se renderiza. - Paginación — 25 filas por página, controles ◀ ▶ y «página N/M».
Sin cambios de schema: orden y página son estado del widget, se
reinician al navegar. Helpers puros cmp_values y next_sort con
tests.
feat(meta-*): ficha de detalle (Fase 3 del ERP nakui)
La metainterfaz gana una tercera clase de vista:
View::Detail(DetailView)— ficha de un record: susfields(reusanColumn, con resolución de refs y formato) +related(listas de back-references) + botones «← Volver» / «✎ Editar».RelatedList— declara una lista de records relacionados porvia_field: el runtime filtra los records de otra entity cuyo campo apunta al record que se ve (las oportunidades de un cliente, etc.).ListView.row_detail— enlaza lista → ficha: cada fila gana un botón 👁 que abre la ficha del record.Module::validateexige que apunte a una vistaDetail.meta-form:render_detail+render_related, navegaciónselect_detailcon retorno a la lista de origen.
Tests en meta-schema y nakui-ui.
feat(meta-*): relaciones legibles + formato (Fase 2 del ERP nakui)
Column.ref_entity— una columna de lista con esto resuelve su valor (un UUID) al label legible del record referido, en vez de mostrar el UUID crudo.meta-formcarga el record vía el backend y usahuman_label_for_record.Column.format(ValueFormat::{Plain, Number, Currency}) — formato de la celda: separador de miles, símbolo de moneda (12000→$12,000). Helperformat_valueenmeta-runtime.- El campo
entity_refen formularios ahora muestra el label del record elegido (read-only) + el selector, no el UUID crudo. human_label_for_recordreconoce campos de nombre en español (nombre,titulo), no sólo inglés.
Tests nuevos en meta-runtime (format_value, labels ES) y
meta-schema. Ver el changelog de nakui para el plan maestro.
feat(meta-*): FieldKind Select y AutoId (Fase 1 del ERP nakui)
La metainterfaz declarativa gana dos tipos de campo:
Select— valor de un conjunto cerrado.FieldSpec.options(valor + etiqueta opcional);Module::validateexigeoptionsno vacío.meta-formlo renderiza como chips clickables (el chip elegido resaltado), no texto libre.AutoId— UUID v4 autogenerado.meta-formlo rellena al abrir el formulario y lo muestra read-only; el usuario no teclea ids de idempotencia. En modo edición conserva el id del record.
parse_field_value trata ambos como string passthrough. Tests nuevos
en meta-schema (validación de Select) y meta-runtime (parseo).
feat(nahual-widget-text-input): modo enmascarado para contraseñas
TextInput::with_mask() dibuja el contenido como puntos (•, uno por
carácter Unicode) en vez del texto real; text() sigue devolviendo el
contenido crudo. Lo usa el campo de contraseña de mirada-greeter.
Lógica de enmascarado en la función pura display_text, con tests.
feat(nahual-theme): exportación del tema a GTK (módulo toolkit)
Módulo nuevo nahual-theme/src/toolkit.rs: traduce el Theme activo a
~/.config/gtk-3.0/gtk.css y gtk-4.0/gtk.css con overrides
@define-color. Las apps GTK adoptan el acento exacto del tema + un
neutro claro/oscuro coherente. Los fondos en gradiente de nahual no se
pueden reproducir en ventanas GTK sólidas, así que el neutro se sintetiza
con un ramp de luminancia tintado por el matiz del borde del tema.
gtk4_css/gtk3_css— generadores puros (nombres de color de libadwaita y de Adwaita 3 respectivamente).export_toolkit_configs+export_toolkit_configs_to(base)— escritura; el segundo con directorio base explícito para tests.- Guarda de no-pisar: si un
gtk.cssya existe sin la marca de nahual, es del usuario y se respeta (ExportReport.skipped). Theme::setyTheme::install_defaultexportan best-effort: cambiar de tema en cualquier app GPUI actualiza GTK al instante.config_pathrefactorizado sobre un nuevoconfig_home().- Ejemplo
dump-toolkit-csspara inspeccionar el CSS generado. - 8 tests nuevos en
toolkit.
feat(yahweh-launcher): F3 — extracción del shell standard de explorers
Iter 19. Patrón con 4 consumers idénticos (nakui-explorer, nouser-explorer, minga-explorer, brahman-broker-explorer) declaraban ~20 líneas de boot: `Application::new + Theme::install_default + cx.open_window
- WindowOptions{ window_bounds, titlebar } + cx.activate(true)`. Todo idéntico salvo título, tamaño, y root entity factory.
Crate nuevo crates/modules/ui_engine/libs/launcher/
(yahweh-launcher):
- Deps:
gpui,yahweh-theme. Sin más. pub fn launch_app(title, size, root_factory): 1-line boot.pub fn launch_app_with(config, root_factory): variante conAppLaunchConfigarmado afuera, para casos donde título/tamaño se computan condicionalmente (env var, etc).AppLaunchConfig::new(title, size): builder normalizador.- 2 tests
#[test]cubren la normalización de config (no se testealaunch_appper se porque bloquea el thread main hasta que la ventana se cierra).
Migración 4 consumers:
main()pasa de 20 líneas a 1:launch_app("Title", (W, H), Explorer::new).- Imports de gpui se podan: ya no necesitan
App, Application, Bounds, WindowBounds, WindowOptionsniSharedStringniprelude::*. - Cada consumer agrega dep
yahweh-launcher(path local).
Naming: el slot natural era yahweh-shell, pero ya existe un crate
yahweh-shell en crates/apps/ (bootstrap heavyweight con file
explorer + DB explorer + viewers). El helper es liviano y específico
al patrón de launch, así que yahweh-launcher evita confusión.
Total ahorro: ~75 líneas hardcoded de boilerplate UI a 4 líneas en total. Cambios de boot (window opts, theme, etc) ahora viven en un solo lugar.
Tests stack: 2 nuevos en launcher; suites de los 4 consumers intactas. Todo verde.
feat(yahweh-widget-app-header): promover el header standard de explorers
Iter 16. Patrón con 4 consumers idénticos: nakui-explorer,
nouser-explorer, minga-explorer, brahman-broker-explorer
todos declaraban un header flex_row + flex_grow(label) + theme_switcher + bg(panel) + border-bottom + text_size(14) + padding(16/12). Ahora es 1 línea.
Crate nuevo crates/modules/ui_engine/widgets/app-header/
(yahweh-widget-app-header):
- Deps:
gpui,yahweh-theme,yahweh-widget-theme-switcher. El switcher se incluye automáticamente. pub fn app_header(cx: &mut App, label: impl Into<SharedString>) -> impl IntoElement: caso simple con texto plano.pub fn app_header_with(cx, label_child: impl IntoElement): variante para cuando el lado izquierdo no es texto plano (icon- text, múltiples spans, etc.).
- 3 tests
#[gpui::test]: smoke con string label, con custom child IntoElement, type-check de label con literal/owned/format!.
Migración de los 4 consumers:
- Cada uno reemplaza un bloque
let header = div().flex().flex_row()... .child(theme_switcher(cx))(~13 líneas) porlet header = app_header(cx, header_text)(~1 línea). - Cada uno borra dep
yahweh-widget-theme-switcher(ya no la necesita directo — elapp_headerla incluye internamente). - Cada uno reemplaza
use yahweh_widget_theme_switcher::theme_switcherporuse yahweh_widget_app_header::app_header.
Total ahorro: ~50 líneas de código UI hardcoded en consumers. Cambios visuales en el header (padding, border, text_size) ahora viven en un solo lugar.
Tests stack: 3 nuevos en app-header; suites de los 4 consumers intactas. Todo verde.
Decisión: el sidebar header del MetaApp (que también incluye
theme_switcher) NO se migra — es un header de sidebar, no de app
top, y tiene styling distinto (px(12/10/13), sin bg/border-bottom
porque ya está dentro del panel). Diferente patrón → diferente
widget si emerge segundo consumer.
feat(yahweh-widget-stat-card): promover el patrón stat card como widget
Iter 15. El patrón "tarjeta de dashboard con border-l accent +
label + valor grande + descripción + listing opcional" tenía 2
consumers (minga-explorer y brahman-broker-explorer); ahora vale
extraer al stack yahweh para reusabilidad y mantenimiento single-place.
Crate nuevo crates/modules/ui_engine/widgets/stat-card/
(yahweh-widget-stat-card):
- Deps:
gpui+yahweh-widget-card(componecard_themed). Sin theme directo — el caller pasatextytext_dimya resueltos del theme. pub fn stat_card(cx, label, value, description, accent, text, text_dim, recent_items):cx: &App(acepta&Context<T>por deref auto-coerce).value: impl Into<SharedString>— sirve para counts ("3"), status text ("UP"), o cualquier label corto.recent_items: &[String]— si no vacío, agrega sub-header"recent (N):"+ una linea por item.
- 3 tests
#[gpui::test]con TestAppContext: smoke con/sin recent_items, type-check devaluecon literal/format/owned. - Dev-deps: gpui con
test-support+ yahweh-theme para construir el cx con un theme global.
Cambios consumer:
minga-explorer: sustituye sufn stat_cardlocal (~60 líneas) poruse yahweh_widget_stat_card::stat_card. Borra depyahweh-widget-card(ya no se usa directo). Adapta los 3 callsites para pasarvalue.to_string()(el widget aceptaInto<SharedString>).brahman-broker-explorer: refactoriza sufn state_cardpara que sea un wrap destat_cardcon la traducciónProbeState → (accent, value, description). La función queda como helper local porque la mapping del enum es app-specific, pero el rendering pasa por el widget compartido. Borra depyahweh-widget-card.
Tests stack: nuevos 3 del widget. Suites de los 2 consumers intactas (4 minga-explorer, 2 broker-explorer). Stack total ~120 verdes (varía por compilation cache).
Beneficio operativo:
- Cualquier app nueva que necesite cards de dashboard usa
stat_card(...)directo; no re-implementa el pattern. - Cambios visuales (text sizes, padding, sub-header format) ahora viven en un solo lugar.
value: impl Into<SharedString>es más expressive que elusizerígido del original local.
Pequeña simplificación documentada: el sub-header del listing
pasa de "recent (N de TOTAL):" a "recent (N):". El "TOTAL"
ya no se calcula porque el widget no lo conoce — el caller que
quiera mostrarlo lo formatea en el label/value (ej. label "Nodos AST (5 de 247)"). Acceptable trade-off por la reusabilidad
genérica.
feat(yahweh-theme): persistencia de la preferencia de theme entre runs
Iter 13. El theme switcher ya cambiaba el chrome en runtime, pero
al cerrar y reabrir la app el theme volvía a Nebula default. Ahora
el name del theme se persiste en $XDG_CONFIG_HOME/yahweh/theme
(default ~/.config/yahweh/theme) y se restaura al boot.
Cambios en yahweh-theme:
pub fn config_path() -> Option<PathBuf>: resuelve el path XDG. DevuelveNonesi niXDG_CONFIG_HOMEniHOMEestán set (sandbox/CI).pub fn load_persisted() -> Option<Theme>: lee el archivo, trim, busca el theme por name víaTheme::by_name.Nonesi el file no existe, lectura falla, o el name no matchea ningún preset (e.g. preset renombrado entre versiones).pub fn persist(theme: &Theme) -> io::Result<()>: escribe el name al config file. Crea el dir parent si no existe.pub fn load_from_pathypub fn persist_to_path: variantes con path explícito — útiles para tests con tempfile y para apps que quieren un path custom (multi-user, staging, etc.).Theme::install_default(cx)cambia: antes hardcodednebula(). Ahora intentaload_persisted(), fallback a Nebula.Theme::set(cx, theme)cambia: antes sólocx.set_global. Ahora tambiénpersist(&theme)antes (best-effort: ignora io errors). Eltheme_switcherwidget ya consumeTheme::set, así que sin cambios en su código el switching ahora persiste.
5 tests nuevos (persistence_tests):
persist_then_load_round_trip— escribir + leer Aurora.load_from_missing_file_returns_none— no rebota.load_from_unknown_name_returns_none— name desconocido →None(degrada a default cuando se usa).persist_creates_parent_dir_if_missing— crea~/.config/yahweh/si no existe.config_path_uses_xdg_config_home_when_set— respeta el env.
Tests stack: ~5 nuevos en yahweh-theme. Todos los downstream
(nakui-ui, *-explorer) compilan sin tocar nada — la API pública
de Theme::install_default y Theme::set no cambió shape.
Smoke run del binario verificado: bootstrap OK, panic esperado
sin display.
Beneficio operativo:
- Usuario abre
nakui-ui, cicla a Aurora con el switcher, cierra app. Próxima apertura: Aurora cargado del disco. Todas las apps yahweh-themed (4 del repo) comparten la misma preferencia. - Failure mode benigno: sin home dir o sin permisos de write, el theme cambia in-memory pero no se persiste — el switcher sigue usable, sólo no sobrevive al close.
- Path canónico documentado: usuarios que quieran preset el
theme antes de abrir la app pueden hacer
echo Aurora > ~/.config/yahweh/theme.
feat(yahweh): caret blinking + slots ornament en theme + MetaApp full themed
Iters 8-9 combinadas. Tres mejoras pequeñas que cierran la integración del theme:
1. Caret blinking en text-input (yahweh-widget-text-input):
- Nuevo field
caret_visible: boolque toggea cada 500ms. - Nuevo field
_blink_task: Task<()>mantiene el loop de blink vivo y lo cancela al drop del widget. - En
new(),cx.spawn(...)arranca el loop:timer.timer(500ms)this.update(...)que toggea +cx.notify(). Si el update falla (entity drop), break.
- En
render(), caret|se dibuja sólo siis_focused && self.caret_visible. Familiar feel del SO.
2. Slots ornament en yahweh-theme (5 nuevos):
bg_input() -> Hsla— bg sutil para fields editables.bg_button() -> Hsla+bg_button_hover() -> Hsla— controls clickable secundarios.accent_destructive() -> Hsla— rojo para acciones peligrosas.bg_destructive_hover() -> Hsla— bg de hover sobre destructive.- Implementados como methods del
Theme(no fields del struct), derivados viaornament_slots(self.is_dark). Esto evita modificar los 6 presets — el slot vive donde uno lo invoca.
3. MetaApp ornament cleanup (yahweh-widget-meta-form):
- 11 colores hardcoded
gpui::rgb(0x...)migrados a slots del theme:- Sidebar menu items (selected/hover) →
bg_row_active/bg_row_hover. - List row separator + button bgs →
bg_row_active/bg_button()/bg_button_hover(). - Icon ✕ delete + hover →
accent_destructive()/bg_destructive_hover(). - EntityRef selector hover/selected →
bg_row_active/bg_row_hover. - EntityRef selector border →
theme.border(slot existente). - Form fallback input bg + submit button →
bg_input()/bg_button()/bg_button_hover(). - Confirm modal hint subtitle + hovers de Cancel/Confirm →
theme.fg_muted/bg_button_hover()/bg_destructive_hover().
- Sidebar menu items (selected/hover) →
- Pattern:
let X = theme.slot()antes de las closures +move |d| d.bg(X)en hover/when para que el cierre tome ownership.
Antes de este commit MetaApp tenía la paleta principal themed (iter 5) pero el ornament secundario (hovers, separators, botones inline) seguía hardcoded. Ahora el theme switcher cambia absolutamente todo el chrome del MetaApp en runtime.
Tests: 117 verdes (sin cambios numéricos, pero downstream sigue compilando). Smoke run de nakui-ui: bootstrap completo OK.
Limitación restante: nouser-explorer todavía no migra al stack
yahweh themed — patrón idéntico a nakui-explorer aplicado pero
más nuevo. Próxima iter.
feat(yahweh-widget-text-input): focus-aware border + caret sólo on focus
Iter 7 (mini-iter — el text-input ya estaba themed, faltaba sólo
el polish de focus visibility). Antes el border era siempre
accent_strong y el caret | siempre estaba presente — imposible
distinguir cuál input está activo en un form con varios fields.
Cambios en yahweh-widget-text-input:
- Border focus-aware: cuando el input está focused, border =
theme.accent_strong(color vivo). Cuando no, border =theme.border(color tenue del chrome). Se obtiene viaself.focus_handle.is_focused(window). - Caret
|sólo on focus: cuando el input no tiene focus, se muestra el texto plano sin caret. Reduce el "ruido visual" en forms con muchos fields. renderahora usa elWindowarg (antes_w) para chequear focus.
Sin cambios en API pública — todo es interno al render. El
binario no requiere migración.
Tests: sin cambios (los tests del crate son struct constructors,
no rendering). Tests downstream del widget (yahweh-widget-meta-form,
nakui-ui) siguen verdes — el cambio es backward compatible.
Beneficio operativo:
- Forms con 5+ fields ahora son navegables: el usuario ve cuál input recibe sus teclas via el border highlighted.
- Cambio de theme afecta también a inputs (ya estaban themed; ahora
además respetan el
accent_strongespecífico del preset cuando focused, vs elbordercuando no).
Limitación pendiente: el caret | literal no parpadea (no hay
animation timer). Cuando emerja la necesidad, agregar via
cx.spawn con un loop de toggle. Por ahora el caret estático on
focus es suficiente signal.
feat(yahweh-widget-theme-switcher): control para ciclar themes en runtime
Iter 6. Cierra el ciclo del theme: ya teníamos paleta themed +
widgets que la consumen, faltaba el control UI para rotar entre
presets en vivo. Ahora hay un botón yahweh que muestra el theme
actual y al click avanza al siguiente. nakui-ui y nakui-explorer
lo incrustan en sus headers — un click cambia toda la paleta.
Crate nuevo: crates/modules/ui_engine/widgets/theme-switcher/
(yahweh-widget-theme-switcher):
- Deps:
gpui+yahweh-theme. Sin nada más. pub fn theme_switcher(cx: &mut App) -> impl IntoElement: botón clickable conid, padding consistente (px(8/4)), bg =theme.bg_panel_alt, hover =bg_row_hover. Muestra"Tema: <name> ▸"y al click haceTheme::set(cx, Theme::next_after(current.name)).- 2 tests
#[gpui::test]:switcher_constructs_with_theme_installed— smoke: el constructor lee el global y devuelve un IntoElement sin panic.theme_set_changes_global— verifica queTheme::setreemplaza el global y que el siguienteTheme::globaldevuelve el nuevo.
- Dev-dep
gpuicontest-supportpara habilitar TestAppContext.
Migración de consumers:
nakui-explorer: nueva depyahweh-widget-theme-switcher. El header pasa dediv().px().py()...child(text)adiv().flex_row().child(div().flex_grow().child(text)).child(theme_switcher(cx)). El switcher queda alineado a la derecha víaflex_growdel label.yahweh-widget-meta-form: nueva dep. El sidebar header ("Nakui" + 12px padding) gana el switcher con el mismo patrón flex_row + flex_grow.
Tests stack: 115 → 117 (+2 del switcher). Cada crate compila individualmente.
Beneficio operativo:
- Click en el switcher cambia toda la paleta en vivo: bg del app,
panels, banners (los que usan
_themed), confirm modal, todo. - 6 presets disponibles via
Theme::all()(Nebula, Aurora, Sunset, Flat Dark, Solarized Light, High Contrast). El switcher cicla circularmente. - Apps adoptantes del
Themeheredan el switch sin esfuerzo.
Decisión técnica: el handler usa Theme::set(cx, ...) que
invalida el global. GPUI marca todos los views como dirty y
re-renderea — los widgets que leen Theme::global en su render
ven el nuevo automáticamente. No requiere cx.observe_global
explícito en cada widget consumidor.
Limitación: TextInput entities ya creadas no se actualizan visualmente
si el theme cambia los colors del input bg/border (esos colors
están hardcoded en yahweh-widget-text-input). Migrar text_input
al theme es una iter futura — bajo scope porque actualmente vive
suficientemente bien con sus defaults dark.
feat(yahweh-widget-meta-form): paleta del chrome migrada a Theme::global(cx)
Iter 5 de integración. El MetaApp::render tenía 7 vars locales
con colors hardcoded (bg/panel/border/text/text_dim/accent/ accent_active) que se pasaban a las funciones internas
(render_sidebar/render_main/render_list/render_form/
render_entity_ref_selector). Ahora salen del Theme::global(cx)
que el binario shell instala al boot. El confirm_delete_banner
también usa themed_colors(Banner::Warning) / themed_colors(Banner::Error)
para sus colors base.
Cambios en MetaApp::render:
- 7
let X = gpui::rgb(0x...)→ derive del theme:bg←theme.bg_app(Background, soporta gradientes).panel←theme.bg_panel.border←theme.border(Hsla).text←theme.fg_text.text_dim←theme.fg_muted.accent←theme.accent.accent_active←theme.accent_strong.
toast_divyerror_banner:banner(...)→banner_themed(cx, ...).
Cambios de firma (internas, no API público):
render_sidebar/render_main/render_list/render_entity_ref_selector/render_formcambian Rgba → Hsla en sus parámetros de color (Background donde aplica parapanel). Los métodosbg/text_color/border_colorde gpui::Div aceptan ambos viaInto, así que el uso interno no cambia.
Cambios en render_confirm_delete_banner:
- 6 colors hardcoded amber/red/gray →
themed_colors(Warning)para banner base,themed_colors(Error)para botón Confirm,theme.bg_panel_alt + fg_textpara botón Cancel. - Cambiar de Theme ahora cambia toda la paleta del modal.
Lo que NO migra esta iter (queda como ornament hardcoded; iter futura si emerge la necesidad):
- Row hovers misceláneos en
render_list(px 0x232a36 / 0x1f2630 para selected/hover de filas). - Borders sutiles entre filas (px 0x232a36).
- Bg de inputs custom (px 0x171a20).
- Bg de botones en
render_entity_ref_selector(px 0x2c3540). - Color rojo del icon
✕de delete (px 0xd07070) y su hover (px 0x4a2020).
Estos son detalles ornamentales que un theme switcher real
querría integrar; los aislo para una pasada futura cuando esté
claro qué slots semánticos del theme conviene agregar (ej.
bg_row_selected distinto de bg_row_hover, accent_destructive,
etc.).
nakui-ui shell ya instalaba Theme::install_default(cx) desde la
iter pasada — sigue siendo el contract entre el shell y el widget.
Smoke test del binario verificado: bootstrap completo OK, panic
esperado en open_window sin display.
Tests stack: 115 verdes (sin cambio — los tests del widget no acceden al render).
Beneficio operativo:
- El theme switcher (cuando llegue) cambia toda la paleta principal
de
MetaAppcon 1 sola llamadaTheme::set(cx, ...). MetaAppynakui-explorercomparten el mismo theme global en un mismo proceso (si llegan a vivir juntos).- Los
confirm_delete_bannery los toasts del MetaApp respetan is_dark: el contrast ajusta automatic.
feat(yahweh): theme integration en banner + card + nakui-explorer consume themed
Iter 4 de la integración. Los widgets banner y card ahora
ofrecen variants _themed(cx, ...) que leen Theme::global(cx).
Las versiones sin theme se preservan para apps sin theme global.
nakui-explorer migra a versiones themed + Theme::install_default
al boot — el chrome hardcoded del explorer (5 variables let bg = rgb(...)) sale del theme.
Cambios en yahweh-widget-card:
- Nueva dep:
yahweh-theme. pub fn card_themed(cx: &App) -> Div: devuelve [card] pre-aplicado conbg(theme.bg_panel). El caller sigue componiendo con borders, accents, children.
Cambios en yahweh-widget-banner:
- Nueva dep:
yahweh-theme. pub fn banner_themed(cx: &App, kind, message) -> Div: deriva(bg, fg)segúnkind+theme.is_dark:Info:theme.bg_panel_alt+theme.accent.Success/Warning/Error: hue fijo (verde/amber/rojo)- lightness flippeada según
is_dark(dark = bg low, fg high; light = invertido).
- lightness flippeada según
pub fn themed_colors(kind, theme) -> (Background, Hsla): helper público para callers que quieren computar el par sin construir el div.- 3 tests nuevos del derivation: dark/light lightness contrast, kinds distinguidos por hue.
Migración de nakui-explorer:
- Nueva dep
yahweh-theme. main()llamaTheme::install_default(cx)antes de open_window (el theme default es Nebula).render:- 5
let bg/text/text_dim/card_bg/border = rgb(...)colors locales →theme.bg_app/fg_text/fg_muted/bg_panel/border. card().bg(card_bg)→card_themed(cx)(borra los locales).banner(Banner::Error, ...)→banner_themed(cx, Banner::Error, ...).- Los accents
accent_seed/accent_morphismse preservan locales: son señales semánticas del log (azul=seed, verde=morphism), no chrome del app.
- 5
Distribución de tests: 112 → 115 (+3 del banner derivation). Workspace stack pasó por la migración sin errores.
Beneficio operativo:
- Cambiar de Theme (Nebula → Aurora → Solarized Light, etc.) ahora
refleja en
nakui-explorerautomáticamente. Antes había que buscar y reemplazar los hex codes uno a uno. - Apps que adopten el patrón
_themedheredan el switcher de theme cuando emerja.
Decisiones:
- Hue fijo por kind: Success siempre verde, Error siempre rojo, etc. La lightness se ajusta al theme; el hue se mantiene como invariante semántico cross-theme.
- API dual:
banner(defaults) +banner_themed(theme). Apps sin theme global pueden seguir con la versión simple. - Acentos semánticos del explorer (seed/morphism) NO migran: pertenecen al dominio del log, no al chrome.
Próximas integraciones pendientes:
MetaApp(enyahweh-widget-meta-form) tiene su propia paleta hardcoded de 6 colors que podría migrarse al theme. Scope mayor que esta iter; queda como candidato.- Theme switcher widget (botón/menú en chrome para ciclar themes). Cuando emerja la necesidad real.
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 conflex_col+px(12)py(8)+mb(4)+rounded(4)+gap(2). Sin colores aplicados.
- El return es
DivGPUI — 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:
a:
div().flex().flex_col().px(12).py(8).mb(4).bg(card_bg) .rounded(4).border_l_4().border_color(accent).gap(2)...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
MetaAppo 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
- text colored según severidad. Antes vivía duplicado con colores hardcoded en cada consumer; ahora hay un widget yahweh con presets consistentes.
Crate nuevo: crates/modules/ui_engine/widgets/banner/
(yahweh-widget-banner):
- Dep: solo
gpui(sin nakui, sin runtime). Reusable por cualquier app GPUI que necesite tiras de status. pub enum Bannercon 4 variants:Info(azul tenue, mensajes neutros).Success(verde, confirmaciones).Warning(amber, llamadas de atención).Error(rojo, errores fatales).
- Métodos
Banner::bg()yBanner::fg(): paleta hardcoded por variant (sin tema dinámico todavía — cuando emerja, se inyecta víayahweh-theme). pub fn banner(kind, message) -> Div: constructor que devuelve el div ya con padding/text_size defaults; el caller puede agregar children, override pads/sizes, attach handlers.- 2 tests sanity: ningún kind comparte bg, ningún kind comparte fg.
Migración de consumers:
yahweh-widget-meta-form: nueva depyahweh-widget-banner. Eltoast_div(Success) yerror_banner(Error) enMetaApp::renderpasan de 2x6 líneas hardcoded a una llamada abanner(...)cada uno (~12 líneas → 2).nakui-explorer: nueva dep. El error banner local pasa abanner(Banner::Error, e).px(16).py(8).text_size(12)— preserva el padding/size custom del header del explorer via override builder.
Tests stack: 109 → 111 verdes (+2 del crate banner).
Beneficio operativo:
- Si emerge un tercer consumer, importa la dep + 1 llamada.
- Cambiar la paleta de un kind = un cambio en un solo lugar (ej. ajustar tono del Error o el contraste del Warning).
- Composición preservada: el
banner()devuelve unDivdirecto, el caller modifica con builder calls (.child(),.px(),.on_click(), etc.) sin rewrap.
Próximo candidato natural: el confirm_delete_banner de MetaApp
es Banner::Warning + 2 botones embedded. Cuando emerja un segundo
consumer de modal-style banners, extraer un widget compositivo
arriba del Banner base.
feat(yahweh): MockBackend público + tests E2E del widget con gpui::TestAppContext
Cierra el ciclo de testabilidad del widget metainterfaz. Hasta
ahora los tests del trait MetaBackend vivían como impl privada
en backend.rs; el widget no tenía forma de testear handlers
reales sin levantar NakuiBackend (que depende de event log +
Rhai + nakui-core). Ahora el mock es público y los tests del widget
lo consumen con TestAppContext.
Cambios en yahweh-meta-runtime:
- Nuevo módulo
pub mod testingconpub struct MockBackend. Exporta:MockBackend::new()— vacío.MockBackend::with_records(iter)— pre-poblado con(entity, uuid, value)tuples.MockBackend::with_morphism(name, |inputs, params| -> Result<usize>)— builder para registrar handlers callable de morphism (sin handler,morphism()rebota con error claro).- Métodos de inspección
total_records()/records_for(entity)(último devuelveVec<(Uuid, &Value)>sin clones). impl MetaBackendcompleto: seed/load/list/update/delete con semantica documentada.
- Tests del trait en
backend.rssimplificados: elMemBackendduplicado se borra; los tests pasan a usarMockBackend::new()importado decrate::testing. 8 tests del backend.rs intactos + 9 tests propios del mock entesting.rs. - Bajo
pub mod testing(no#[cfg(test)]) deliberadamente: los crates downstream pueden importarlo en sus dev/integ tests víayahweh_meta_runtime::testing::MockBackend.
Cambios en yahweh-widget-meta-form:
- Dev-dep nueva:
gpui = { workspace = true, features = ["test-support"] }. HabilitaTestAppContextpara tests sin abrir window real. MetaApp::apply_actionahorapub(era privado). Necesario para que los tests E2E lo invoquen desde fuera. La function ya era el entry point de los click handlers internos; exponerla no cambia el contract.- Nuevo archivo
tests/widget_with_mock_backend.rscon 4 tests#[gpui::test]:meta_app_constructs_with_mock_backend_and_initial_state: instanciaMetaApp<MockBackend>con records pre-poblados + toast inicial; valida que la window construye sin panic.open_view_action_does_not_panic: invocaapply_action(OpenView)real a través dewindow.update(cx, |meta, _, cx| ...)→ state machine corre sin crash.backend_state_visible_from_widget_perspective: demuestra el patrón "backend pre-poblado para fixtures" (typical para screenshots / demos).morphism_handler_can_be_registered_and_called_via_widget:MockBackend::with_morphismregistra un counter callback;apply_action(Morphism)lo dispara viacommit_morphismsin tocar nakui-core / Rhai.
Helpers de tests:
customers_module(): fixture local de unModulecon entity Customer + view list + view form. Reusable cross-test.
Distribución de tests:
yahweh-meta-runtime: 47 → 56 (+9 del nuevo testing module).yahweh-widget-meta-form: 3 → 7 (+4 E2E reales).- Total stack: 109 tests verdes (56 runtime + 31 cards + 12 nakui-ui + 3 explorer + 7 widget).
Beneficio operativo:
- El widget tiene cobertura runtime real, no sólo type-check.
- Cualquier app que tome
B: MetaBackendpuede testarse conMockBackenden sus dev-deps sin re-implementar el mock. - Fixtures pre-pobladas habilitan demos/screenshots/CI con state conocido.
Limitaciones:
render()no se invoca en los tests (requiere window context más rico). Los tests verifican state machine, no pixels. Pixel comparison (snapshot tests) es scope futuro si emerge la necesidad.apply_action(Morphism)con un module que no declaranakui_module_dirrebota antes de llamar al mock handler. El 4to test acepta ambos outcomes (counter 0 o 1) — si en el futuro agregamos un módulo de fixture con nakui_module_dir poblado, el test puede aserta exactamente.
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 excedemaxchars. Edge case:max < 3devuelve los primerosmaxchars 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-runtimeen 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.
refactor(yahweh): Fase 2c — extracción del widget al crate yahweh-widget-meta-form
Cierra el refactor de UI: el widget render (forms, lists, modal de
delete, EntityRef selector, sidebar, key handlers) deja de vivir en
el binario nakui-ui y pasa a un crate yahweh nuevo, genérico sobre
MetaBackend. nakui-ui queda como un shell de bootstrap de 424
líneas.
Crate nuevo: crates/modules/ui_engine/widgets/meta-form/
(yahweh-widget-meta-form):
- Deps: gpui, yahweh-meta-schema, yahweh-meta-runtime, yahweh-theme, yahweh-widget-text-input, serde_json, uuid. Cero deps a nakui o brahman-cards — reusable por cualquier app.
MetaApp<B: MetaBackend>público: estructura genérica conmodules,backend: B,active,form_inputs,editing,pending_delete,toast,load_error. El boundB: MetaBackendse propaga a todos losimpl MetaApp<B>y alimpl Render for MetaApp<B>.MetaApp::new(modules, backend, initial_toast, initial_error, cx): constructor sin lógica de bootstrap. El caller pre-construye modules + backend + cualquier mensaje inicial. La active view default es la primera entry del menú del primer módulo.- Methods preservados del original (rename simbólico): select_view, open_edit, commit_seed, commit_morphism, commit_delete, apply_action, list_rows, render_*, tick interno via WriteOutcome.post_status.
- Helpers locales del widget:
lookup_field(path walker JSON por la lista renderer),append_compact_msg(concatenador del toast),format_seed_toast(decide "creado/actualizado/sin cambios" segúnWriteOutcome). - 3 tests funcionales puros:
lookup_field,append_compact_msg,format_seed_toast. Tests con GPUI cx no son posibles sin un TestAppContext setup; quedan implícitos vía type-check del trait bound.
Cambios en nakui-ui (shell):
- main.rs: 1959 → 424 líneas (78% reducción). Ahora sólo:
- Carga modules via
brahman_cards::load_cards_from_dir+load_ui_modules(filtra UiModule body, valida, dedup). - Carga executors para módulos con
nakui_module_dir. NakuiBackend::open(...)para inicializar el backend.cx.open_window(...)conMetaApp::<NakuiBackend>::new(...)como root view.
- Carga modules via
use yahweh_widget_meta_form::MetaApp+ dep nueva en Cargo.toml. Los imports de yahweh-meta-runtime/schema desaparecen de main (los consume el widget internamente).- Tests del shell: 4 tests E2E que tocan nakui-core directamente
(event_log_replay, morphism_pipeline_real_sales_vender,
load_ui_modules x3). Los tests del NakuiBackend impl quedan en
backend.rs(8 tests). Los tests del widget viven en su propio crate. backend.rs: sin cambios (NakuiBackend ya estaba aislado en Fase 2b).
Distribución final del refactor yahweh:
yahweh-meta-schema: 8 tests (data puro).yahweh-meta-runtime: 42 tests (helpers + trait MetaBackend).yahweh-widget-meta-form: 3 tests (widget genérico).brahman-cards: 26 tests (loader unificado).nakui-ui: 12 tests (4 shell + 8 backend impl).- Total: 91 tests cubriendo el área.
Cada crate compila individualmente. El widget consume el trait sin
saber qué backend hay debajo; nakui-ui provee el trait wireado a
nakui-core; cualquier futuro shell (mock para tests, otro stack de
storage) puede reusar el widget sin cambio.
Lo que NO hace Fase 2c:
- No mueve
format_seed_toast/append_compact_msg/lookup_fieldayahweh-meta-runtime. Son lo bastante widget-flavored (SharedStringde gpui, decisiones de UX del toast, etc.) que preferí dejarlos al lado del render. - No introduce un
MetaApp::with_statusbuilder pattern. La signature denewcon 5 args es manejable; si crece, se refactor después. - No expone configuración del widget (theme override, layout custom, etc.). Cuando emerja una segunda app que use el widget con preferencias distintas, se agregan opts.
Pendientes:
- KCL → Nickel: kcl_wrapper en nakui-core reemplazado por evaluación de Nickel contracts. Migrar los 3 schemas .k de sales/inventory/treasury a .ncl.
card.keliminado (REFERENCE ONLY documentado en su header).
refactor(yahweh): Fase 2b — MetaBackend trait + NakuiBackend + MetaUi consume el backend
Materialización del trait que diseñamos en charla. Tres pasos combinados en un solo commit:
Step A — trait + WriteOutcome en yahweh-meta-runtime:
- Nuevo módulo
backend.rscon:pub trait MetaBackend: 'staticcon 6 métodos:list_records,load_record,seed,update,delete,morphism. Convención de ids comoUuidcanónico (los backends que internamente usan otros tipos mapean),set+clearpre-computados por el caller (no double-roundtrip al store), threshold'staticsin Send/Sync (suficiente para handlers GPUI single-threaded).pub struct WriteOutcome { id, changed, post_status }con constructorno_change(id). La UI usachanged = 0para "sin cambios",post_statuspara concatenar mensajes auto-emitidos por el backend (compact, etc.).
- 9 tests con un
MemBackendmínimo (HashMap por(entity, uuid)): seed/load round-trip, list/filter/order, update set/clear/no-op, delete/missing, object-safety check.
Step B — NakuiBackend en nakui-ui/src/backend.rs:
- Estructura que ownea
Arc<Mutex<MemoryStore>>,Option<Arc<Mutex<EventLog>>>,BTreeMap<id, Arc<Executor>>,snap_path,snapshot_threshold,writes_since_compact. NakuiBackend::open(log_path, threshold, executors) -> (Self, OpenStatus): abre log, carga snapshot, replay, auto-compact si threshold cruzado; devuelveOpenStatus { init_toast, load_error }para que el caller agregue al banner.tick_compact()privado que cada write public method invoca tras éxito; devuelveOption<String>que se mete enWriteOutcome.post_status.impl MetaBackend for NakuiBackend:seed: WAL order (log first, store after),tick_compact, devuelveWriteOutcome { id: Some(uuid), changed: 1, post_status }.update: siset+clearvacíos devuelveWriteOutcome::no_change; si no construyeFieldOp::Set+FieldOp::Clear, log Morphismui.edit_recordconparams.fields/cleared, store.apply, tick.delete:FieldOp::Delete, log Morphismui.delete_record, store.apply, tick.morphism: locks log + store,execute_and_log_with_recovery, tick.WriteOutcome { id: None, changed: ops.len(), post_status }.
- Funciones
snapshot_path_forymaybe_compact_logmovidas acá desde main.rs (ahora son detalle del backend). - 7 tests del impl: round-trip via trait, set+clear, no-op edit no escribe, delete/load, list_records, morphism sin executor da error claro, threshold dispara snapshot.
Step C — MetaUi consume el backend:
- Reemplaza fields
store/event_log/executors/snap_path/snapshot_threshold/writes_since_compactpor un únicobackend: NakuiBackend. MetaUi::newcolapsa el wiring de persistencia enNakuiBackend::open(...)— pasó de ~150 líneas a ~10 líneas.commit_seedya no construyeLogEntry/FieldOpdirectos:- SEED →
self.backend.seed(entity, obj). - EDIT →
self.backend.load_record + compute_field_delta + compute_clear_fields → self.backend.update(set, clear). - Devuelve
WriteOutcome(reemplaza el viejo enumCommitOutcome).
- SEED →
commit_morphismparsea inputs/params del form y delega aself.backend.morphism(...).commit_deletees one-liner:self.backend.delete(entity, id).tick_runtime_compacteliminado (ahora interno al backend; el msg viaja enWriteOutcome.post_status).list_rowsqueda como proxyself.backend.list_records(entity).validate_entity_refscallsite usa cierre sobrebackend.load_record(en vez de&Store).- Nuevo helper
format_seed_toast(entity, was_editing, &outcome)reemplaza el match sobreCommitOutcome. - Imports limpiados: no más
nakui_core::delta::FieldOp/FieldPath, no másnakui_core::event_log::*en main.rs (sólo en tests E2E). No másArc/Mutex(vive en backend).
Distribución de tests post-refactor:
yahweh-meta-runtime: 33 → 42 (+9 trait tests con MemBackend).nakui-ui: 14 → 21 (+7 tests del NakuiBackend impl).yahweh-meta-schema: 8 (sin cambio).brahman-cards: 26 (sin cambio).- Total: 97.
Build: cada crate compila individualmente.
Nota sobre Fase 2b/c estado:
- ✅ Backend trait + impl + MetaUi usa backend.
- ⏭ Falta extraer los widgets render (form/list/modal/EntityRef
selector) de nakui-ui a un crate yahweh nuevo
(sugerencia:
yahweh-widget-meta-form). Esa extracción ahora es trivial: el render code ya consume sólo&self.modules+self.backend(vía trait). Lo dejo para próximo commit.
Pendientes:
- Fase 2c: extraer widget render al crate yahweh
(
yahweh-widget-meta-formo similar) —MetaApp<B: MetaBackend>genérico,nakui-uiqueda como ~50 líneas de shell conMetaApp::<NakuiBackend>::new(...). - KCL → Nickel: kcl_wrapper reemplazado por evaluación de Nickel contracts.
card.keliminado (REFERENCE ONLY).
refactor(yahweh): Fase 2 — extraer helpers puros a yahweh-meta-runtime
Sigue de la Fase 1 (lift del schema a yahweh). Ahora extraemos los helpers puros que cualquier widget renderer o backend ejecutor necesita sobre el schema: parse, delta, validation, format. Sin GPUI, sin acoplamiento a un backend específico.
Crate nuevo: crates/modules/ui_engine/libs/meta-runtime/
(yahweh-meta-runtime):
- Deps:
serde_json,thiserror,uuid,yahweh-meta-schema. NO GPUI, NO nakui. - Módulos:
parse.rs—parse_field_value(kind, raw),infer_param_value(raw),resolve_param_value(name, raw, spec).delta.rs—compute_field_delta(current, proposed),compute_clear_fields(current, to_clear).refs.rs—validate_entity_refs(load: F, refs)dondeFes un cierreFn(&str, Uuid) -> Option<Value>. Decoupling vía closure en lugar de trait — evita atar el crate a cualquier backend específico (no hayStoretrait acá), y los callers pasan|e, id| store.load(e, id)trivialmente.format.rs—human_label_for_record(value, id),render_value(opt_value),value_to_input_text(value),short_uuid(id).
- 33 tests propios en el crate nuevo (cubren todos los helpers movidos + edge cases).
Cambios en nakui-ui:
- Nueva dep
yahweh-meta-runtimeenCargo.toml. - Imports: agrega
use yahweh_meta_runtime::{...}con todos los helpers extraídos. Borrado el código local equivalente (~200 líneas). validate_entity_refscallsite: pasa devalidate_entity_refs(&*store, &refs)avalidate_entity_refs(|e, id| store.load(e, id), &refs)— el closure es ergonómico sobre cualquierStore.- Tests duplicados borrados (~34 tests que ahora viven en
yahweh-meta-runtime):parse_field_*(text/number/boolean variants)infer_param_value_*delta_*(5 tests)clear_fields_*(3 tests)validate_entity_refs_*(5 tests)resolve_param_*(6 tests)parse_field_entity_ref_*(4 tests)human_label_*(3 tests),render_value_*,value_to_input_text_inverse_of_parse
- Tests que se quedan en nakui-ui (runtime-específicos):
lookup_field_simple_and_nested— helper local del list renderer.append_compact_msg_handles_both_branches,runtime_compact_cycle_resets_counter_after_threshold,snapshot_path_for_replaces_extension,maybe_compact_log_*(3) — wiring de persistencia a EventLog.load_ui_modules_via_brahman_cards_*(3) — integración con el brazo de cards.value_to_input_then_parse_round_trip— round-trip del parvalue_to_input_text + parse_field_value(toca ambos lados).event_log_replay_restores_memory_store,morphism_pipeline_executes_real_sales_vender,event_log_replay_handles_full_crud_cycle— E2E nakui-core.
Distribución de tests:
nakui-ui: 48 → 14 (los 34 movidos viven en runtime).yahweh-meta-runtime: 33 (nuevos).yahweh-meta-schema: 8 (sin cambio).brahman-cards: 26 (sin cambio).- Total cubriendo el área: 81.
Build: cada crate afectado compila y testea limpio individualmente.
Workspace build full no se completó esta corrida por OOM al
compilar surrealdb-core (problema ambiental no relacionado al
refactor).
Lo que NO hace Fase 2:
- No mueve los widgets render (
render_form/render_list/render_entity_ref_selector/render_confirm_delete_banner) a yahweh — eso es Fase 2b/3, requiere diseñar elMetaBackendtrait porque las render functions tocan el state deMetaUi(form_inputs, pending_delete, executors).
Pendientes (orden):
- Fase 2b: extraer widget render a un crate yahweh nuevo
(sugerencia:
yahweh-widget-meta-form). Requiere diseñarMetaBackendtrait. - Fase 3: thin shell —
nakui-uiqueda reducido a una impl de backend wireada anakui-core. - KCL → Nickel + card.k eliminado.
refactor(yahweh): Fase 1 — nakui-ui-schema → yahweh-meta-schema
Primer paso del refactor yahweh. El schema de UI declarativa
(entities, menús, listas, formularios, acciones) vivía bajo
crates/modules/nakui/ui-schema/ y se llamaba nakui-ui-schema —
un nombre que sugería acoplamiento con Nakui que en realidad no
existe (el crate sólo depende de serde/serde_json/thiserror).
Lo movemos a yahweh para que sea consumible por cualquier app de UI
metadata-driven sin hacer pasar la dep "rara" por nakui.
Cambios mecánicos:
git mv:crates/modules/nakui/ui-schema/→crates/modules/ui_engine/libs/meta-schema/.- Cargo.toml del crate movido:
name = "nakui-ui-schema"→name = "yahweh-meta-schema".- Description actualizada: "Yahweh — meta-schema: descriptores declarativos de UI ... independiente del backend".
- Workspace
Cargo.toml: la entry del members[] pasa decrates/modules/nakui/ui-schemaacrates/modules/ui_engine/libs/meta-schema(en su sección yahweh, no en la sección nakui). brahman-cards:- Cargo.toml: dep path/name a
yahweh-meta-schema. - lib.rs:
pub use nakui_ui_schema::Module→pub use yahweh_meta_schema::Module. - readers.rs: comment + doc-link al nuevo nombre.
- Cargo.toml: dep path/name a
nakui-ui:- Cargo.toml: dep path/name a
yahweh-meta-schema. - main.rs:
use nakui_ui_schema::{...}→use yahweh_meta_schema::{...}.
- Cargo.toml: dep path/name a
- Self-test del crate movido
(
tests/example_modules.rs):nakui_ui_schema→yahweh_meta_schema, y se rebasa el path del repo root (5 niveles arriba ahora, era 4).
Cambios documentales:
- Doc de crate (
lib.rs): "Schema declarativo de la metainterfaz Nakui" → "Schema declarativo de la metainterfaz (yahweh meta-schema)" + "backend-agnostic" en la filosofía. La sección Persistencia universal pasa de "el runtime conecta cada vista alnakui_core::store::Store" a un wording neutro: "el runtime que consume este schema conecta vistas a su backend". - Doc del field
Module.nakui_module_dir: ahora marcado como "path opaco al backend, lo interpreta el runtime concreto". Se describe la convención actual de Nakui (nsmc.json + KCL + Rhai) como ejemplo, no como contrato del schema. El nombre del campo se mantiene por compat con módulos ya escritos; agregado#[serde(alias = "backend_module_dir")]para que un futuro rename no rompa los actuales.
Tests:
- yahweh-meta-schema (crate movido): 13 tests propios siguen verdes tras el path rebase.
- brahman-cards: 26/26 verdes (17 integration + 9 nickel).
- nakui-ui: 48/48 verdes.
- Workspace build verde.
Lo que NO hace Fase 1:
- No mueve los widgets de UI (form/list/modal/EntityRef selector) a yahweh — eso es Fase 2.
- No introduce un trait
MetaBackendpara desacoplar la lógica de runtime de Nakui — eso es Fase 3. - No renombra el field
nakui_module_dir. Se hará cuando aparezca un segundo backend que también lo necesite.
Pendientes (orden):
- Fase 2: extraer widgets render (form/list/modal/EntityRef
selector + helpers parse_field_value/render_value/etc.) a un
nuevo crate
yahweh-widget-meta-form(o nombre similar). - Fase 3: trait
MetaBackend+ thin shell —nakui-uiqueda reducido a una impl de backend wireada anakui-core. - KCL → Nickel: kcl_wrapper reemplazado por evaluación de Nickel contracts.
- card.k eliminado (REFERENCE ONLY).