- ChartCache + ChartCacheHandle (Arc<Mutex<...>>) cacheable entre
frames. El Render host crea uno con chart_cache() y lo pasa al
Element con .with_cache(handle). Sin handle, cada frame rebuild
completo (correcto pero sin la optimización).
- Hash estructural: plot rect + viewport.span (no x_min/y_min) +
per-series (data.revision + data.len + stroke). 5 tests cubren
estabilidad, pan no invalida, zoom invalida, data revision
invalida, plot rect invalida.
- En paint: si el hash matches, pan-blit = copia las coords
cacheadas con offset (dx_px, dy_px) calculado del diff entre
viewport.x_min cached vs actual. Salteamos LTTB + projection.
- LineSeries::compute_projected expone el pipe LTTB + project_buffer
como método público para que el Element pueda cachear sin pasar
por paint().
- Demo multi-series usa el cache; header muestra "cache: N
pan-blits / M rebuilds" en vivo para que se vea la métrica al
draguear (pan-blits crece) y al zoomear (rebuilds crece).
Limitación v0.1 anotada en código: el doc canónico (sección 4.4)
usa una textura offscreen blitable; GPUI 0.2 no expone esa primitiva
directa. La impl actual cachea coords proyectadas y emite las
polilíneas con offset — mismo ahorro de CPU (saltea LTTB) sin GPU
texture cache.
51 tests verdes (28 cartesian incluyendo 5 nuevos del structural_hash,
20 core, 3 render).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- lapaloma-phosphor: feature `gpui` (default). LapalomaPhosphorElement
divide el RingBuffer en N segmentos (default 16, configurable) y
pinta cada uno como una stroke_polyline con alpha = (k+1)/N. El
segmento más nuevo va con alpha 1.0, el más viejo casi
transparente — efecto fósforo persistente.
- Cada segmento incluye el primer punto del siguiente para evitar
gaps visibles entre tramos.
- Wraparound se parte en dos sub-polilíneas (no concatenadas) para
no introducir la línea horizontal "del slot cap-1 al slot 0".
- Glow opcional: pasada adicional con width × glow_width_mult y
alpha × glow_alpha — efecto halo CRT.
- crates/apps/lapaloma-phosphor-demo: misma señal sintética que
stream-demo, paleta verde Tektronix (#9bff8c sobre #050805),
trail 24 segs + glow 4× α 0.18.
Limitación v0.1: el doc canónico usa triangle strip con per-vertex
color (sección 4.3); GPUI 0.2 no expone esa API directa. La impl
actual es funcionalmente equivalente con N draw calls en lugar de 1.
Cuando wgpu directo esté disponible, swap inmediato sin tocar las
API públicas.
46 tests verdes (sin cambios; phosphor se valida via demo).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- lapaloma-stream: feature `gpui` (default). LapalomaStreamElement
pinta un RingBuffer en modo sweep — dos polilíneas split-at-head
(segmento [head..cap) viejo + [0..head) nuevo) para evitar la
línea horizontal del wraparound. Pre-fill (count < cap) sólo
pinta [0, head) para evitar el flat-line del 1.0.2 fix.
- y_range configurable, background opcional, padding.
- crates/apps/lapaloma-stream-demo: osciloscopio sintético con
RingBuffer cap=512. Timer en cx.background_executor que hace
push(synthesize(t)) + cx.notify() cada 16ms (60 Hz). Señal =
suma de dos sinusoides desfasadas + jitter determinístico.
Header muestra cap / head / filled% / t / revision en vivo.
- Workspace: registrada la app lapaloma-stream-demo.
Showcase del P2 zero-alloc: push(v) son 2 writes + 2 increments,
zero allocations per frame, ningún Vec se reasigna.
46 tests verdes (sin cambios; el stream se valida en runtime via demo).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Element ahora mantiene Vec<ChartSeriesItem> con DataBuffer +
StrokeStyle + nombre opcional por serie. Builder add_series y
add_series_named.
- En paint(), una pasada por cada serie reusando el mismo scratch.
N series = N paint_path (no N × por punto). Cumple P3 del
ARCHITECTURE.md por serie.
- `lapaloma_chart(data, vp, stroke)` queda como helper retrocompat
para el caso una-serie.
- Demo: 3 series simultáneas (sin, cos, mix) con colores nórdicos
+ leyenda textual en el header.
46 tests verdes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- viewport.rs: `pan_fraction(fx, fy)` — pan en fracción del viewport
independiente del plot_rect. Útil cuando el handler GPUI trabaja
en coords de window y no conoce el rect interno del chart.
- lapaloma-demo: state machine de drag (DragAnchor con snapshot del
viewport al click) + handlers on_mouse_down/move/up para pan,
on_scroll_wheel con sensitivity exponencial 0.0015 para zoom
anchor-preserving al cursor, on_click con click_count >= 2 para
reset al viewport inicial. El header muestra estado dragging.
- Maneja ScrollDelta::Pixels (trackpad) y ::Lines (mouse wheel
tradicional) unificando con line-height 16px.
46 tests verdes en lapaloma-{core,cartesian,render}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- axis.rs: ticks_nice (Wilkinson sobre lapaloma_core::scale::nice_step),
decimate_labels con min_spacing_px, format_tick con decimales según
step, AxisStyle config. 8 tests.
- gpui_backend::draw_text: shape_line via window.text_system() + iterate
glyphs con paint_glyph. Sin dep en App context (sólo &mut Window).
- LapalomaChartElement.paint_axes: línea base + tick marks + labels
centrados (X) / right-aligned (Y) con decimación. Margins por defecto
reservan 32px izq + 24px abajo.
45 tests verdes en lapaloma-{core,cartesian,render}.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cadena end-to-end DataBuffer → LineSeries → Canvas → gpui::Window
funcionando. cargo run -p lapaloma-demo abre una ventana con sin(x)
sobre 1024 muestras y una sola paint_path por frame.
- lapaloma-render: feature `gpui` opcional. WindowCanvas adapter
traduce el trait Canvas a paint_quad/paint_path de gpui 0.2.
Conversión RGB→HSL para integrar con el sistema de colores Hsla
del resto del codebase yahweh. 3 tests de conversión.
- lapaloma-cartesian: feature `gpui` (default). element::LapalomaChartElement
con impl Element + IntoElement. Arma WindowCanvas en paint() y
delega a LineSeries — un solo paint_path por chart.
- crates/apps/lapaloma-demo registrado en workspace.
Limitaciones conocidas v0.1: clip stack, triangle strips y draw_text
no implementados (los necesitan phosphor / Sankey / axes; se
agregan en sus fases).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ChartViewport: pan/zoom anchor-preserving en coords de dominio.
- CoordinateSystem: proyección dominio→pixel + project_buffer zero-alloc.
- trait Series + LineSeries que emite una sola stroke_polyline por frame
(valida P3 del ARCHITECTURE.md). LTTB se dispara cuando data.len()
excede 3× el ancho del plot.
- hit_test sobre coords sorted-by-X con binary search + threshold 8px.
- 14 tests cubren pan, zoom, projection, downsample y hit-test.
Element GPUI queda para la siguiente fase (requiere pionear paint custom
sobre PaintContext — el monorepo no tiene precedente todavía).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Iter 15. El patrón "tarjeta de dashboard con border-l accent +
label + value grande + descripción + listing opcional" tenía 2
consumers (minga-explorer + brahman-broker-explorer). Ahora vale
extraer al stack yahweh.
crates/modules/ui_engine/widgets/stat-card/:
- pub fn stat_card(cx, label, value: impl Into<SharedString>,
description, accent, text, text_dim, recent_items: &[String])
-> impl IntoElement.
- Compone yahweh-widget-card::card_themed; sin theme directo
(caller pasa text/text_dim ya resueltos).
- 3 tests #[gpui::test] con TestAppContext + theme: smoke con/sin
items, type-check value.
minga-explorer:
- Borra fn stat_card local (~60 líneas).
- Borra dep yahweh-widget-card.
- 3 callsites pasan value.to_string() (widget acepta
Into<SharedString>).
brahman-broker-explorer:
- fn state_card refactorizada como wrap del stat_card compartido,
preservando la traducción ProbeState→(accent,value,description)
como helper local app-specific.
- Borra dep yahweh-widget-card.
Sub-header del listing: pasa de "recent (N de TOTAL):" a
"recent (N):" — el widget no conoce TOTAL; el caller lo pone en
label si quiere ("Nodos AST (5 de 247)"). Trade-off aceptable
por reusabilidad genérica.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iters 8-9 combinadas. Tres mejoras pequeñas que cierran la
integración del theme:
1) text-input caret blinking: caret_visible bool toggea cada 500ms
via cx.spawn loop. _blink_task se mantiene en self para que
drop cancele. render dibuja | sólo si focused && caret_visible.
2) yahweh-theme: 5 slots ornament secundario como methods (no
fields) derivados de is_dark via ornament_slots() helper:
bg_input/bg_button/bg_button_hover/accent_destructive/
bg_destructive_hover. No requiere modificar los 6 presets.
3) MetaApp ornament cleanup: 11 rgb(0x...) hardcoded → slots del
theme. Sidebar menu items, list row separator/buttons, icon ✕
delete y su hover, EntityRef selector hovers, form submit
button + fallback input bg, confirm modal hint y hovers.
Pattern: let X = theme.slot() antes de las closures + move |d|
d.bg(X) en hover/when para tomar ownership.
Antes MetaApp tenía la paleta principal themed (iter 5) pero el
ornament secundario seguía hardcoded. Ahora el theme switcher
cambia absolutamente todo el chrome.
Tests: 117 verdes. Downstream compila. Smoke nakui-ui: bootstrap
OK.
Limitación: nouser-explorer todavía hardcoded — próxima iter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iter 7 (mini-iter — el text-input ya estaba themed, faltaba el
polish de focus visibility). Antes el border era siempre
accent_strong y el caret | siempre presente — imposible distinguir
cuál input está activo en un form con varios fields.
- Border focus-aware: focused → accent_strong; blur → border (slot
tenue del theme).
- Caret | sólo on focus; sin focus se muestra texto plano (reduce
ruido visual).
- render usa window.is_focused(focus_handle) para chequear.
Sin cambios en API pública. Tests downstream verdes.
Limitación: caret estático (no parpadea). Iter futura si emerge
la necesidad de animation timer via cx.spawn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
crates/modules/ui_engine/widgets/theme-switcher/:
- pub fn theme_switcher(cx: &mut App) -> impl IntoElement: botón
clickable con bg=panel_alt, hover=row_hover, label "Tema: <name> ▸".
Al click hace Theme::set(cx, Theme::next_after(current.name)).
- 2 tests #[gpui::test]: smoke + verificación de cambio de global.
- Dev-dep gpui con test-support.
Migración:
- nakui-explorer: header pasa a flex_row con label flex_grow + switcher
alineado derecha.
- yahweh-widget-meta-form (sidebar): mismo pattern en el header
"Nakui" del sidebar.
Tests stack: 115 → 117.
Beneficio: click cambia toda la paleta en vivo. 6 presets disponibles
(Nebula, Aurora, Sunset, Flat Dark, Solarized Light, High Contrast)
ciclables circularmente.
Limitación: TextInput entities tienen colors hardcoded; migrar
text_input al theme es iter futura.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Iter 5 de integración. MetaApp::render tenía 7 vars con colors
hardcoded (bg/panel/border/text/text_dim/accent/accent_active);
ahora salen de Theme::global(cx) que el shell instala al boot.
confirm_delete_banner usa themed_colors(Warning/Error) para sus
colors base.
render:
- 7 let X = rgb(0x...) → theme.bg_app/bg_panel/border/fg_text/
fg_muted/accent/accent_strong.
- toast_div / error_banner: banner() → banner_themed(cx).
Firmas internas:
- render_sidebar/main/list/entity_ref_selector/form cambian
Rgba → Hsla (Background donde aplica para panel). gpui::Div
acepta ambos via Into, uso interno no cambia.
confirm_delete_banner:
- 6 colors hardcoded → themed_colors(Warning) para banner base,
themed_colors(Error) para Confirm, theme.bg_panel_alt+fg_text
para Cancel.
NO migra esta iter (ornament hardcoded para una pasada futura):
row hovers, bordes sutiles entre filas, bg de inputs custom,
bg de botones del EntityRef selector, color del icon ✕ de delete.
Smoke test del binario verificado: bootstrap completo OK, panic
esperado en open_window sin display. 115 tests verdes (sin
cambio: los tests del widget no acceden al render).
Beneficio: theme switcher (cuando llegue) cambia toda la paleta
con 1 sola llamada Theme::set(cx, ...).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Patrón visual común a yahweh-widget-meta-form (toast success +
error banner) y nakui-explorer (error banner): div con bg + text
colored según severidad. Antes duplicado con colores hardcoded en
cada consumer.
Crate nuevo crates/modules/ui_engine/widgets/banner:
- pub enum Banner { Info, Success, Warning, Error } con bg()/fg()
hardcoded por variant.
- pub fn banner(kind, message) -> Div: padding/text_size defaults;
caller compone con .child()/.px()/.on_click()/etc.
- 2 tests sanity (no color collisions).
Migración:
- yahweh-widget-meta-form: 12 líneas hardcoded → 2 llamadas a
banner().
- nakui-explorer: error banner usa banner() + override de
padding custom del header.
Tests stack: 109 → 111 (+2). Cada crate compila individualmente.
Próximo natural: confirm_delete_banner (Warning + botones) puede
extraerse como modal-banner cuando emerja segundo consumer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cierra el ciclo de testabilidad del widget metainterfaz. Hasta
ahora los tests del MetaBackend trait vivían como impl privada en
backend.rs; el widget no podía testear handlers sin levantar
NakuiBackend (que depende de event log + Rhai).
yahweh-meta-runtime:
- Nuevo `pub mod testing` con MockBackend (renombre del MemBackend
privado, ahora público). Constructores: new(), with_records(iter),
with_morphism(name, handler) builder. Métodos de inspección
total_records / records_for. Bajo `pub mod testing` (no cfg test)
para que crates downstream lo usen en sus dev tests.
- Tests del trait en backend.rs simplificados: usan MockBackend en
vez del MemBackend duplicado. 8 backend.rs + 9 nuevos del mock.
yahweh-widget-meta-form:
- Dev-dep nueva: gpui con feature "test-support" (TestAppContext).
- MetaApp::apply_action ahora pub (era privado). Necesario para
invocar handlers desde tests E2E.
- Nuevo tests/widget_with_mock_backend.rs con 4 tests #[gpui::test]:
meta_app_constructs, open_view_action_does_not_panic,
backend_state_visible_from_widget_perspective,
morphism_handler_can_be_registered_and_called_via_widget.
Tests: 47→56 yahweh-meta-runtime, 3→7 yahweh-widget-meta-form.
Total stack 109 verdes.
Limitación: render() no se invoca (requiere window context más
rico). Tests verifican state machine, no pixels. Snapshot tests
serían scope futuro.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cierra el refactor de UI. El widget render (forms, lists, modal de
delete, EntityRef selector, sidebar, key handlers) deja de vivir en
nakui-ui y pasa a un crate yahweh nuevo, genérico sobre MetaBackend.
crates/modules/ui_engine/widgets/meta-form/ (yahweh-widget-meta-form):
- pub MetaApp<B: MetaBackend> con todo el state UI + impl Render
+ handlers + render_*. El bound `B: MetaBackend` se propaga.
- pub fn new(modules, backend, initial_toast, initial_error, cx):
constructor sin bootstrap. Caller pre-construye todo.
- Helpers locales del widget: lookup_field, append_compact_msg,
format_seed_toast.
- Cero deps a nakui o brahman-cards. Reusable por cualquier app.
- 3 tests funcionales puros (lookup_field, append_compact_msg,
format_seed_toast).
nakui-ui (shell):
- main.rs: 1959 → 424 líneas (78% reducción). Sólo bootstrap:
load_ui_modules + load executors + NakuiBackend::open +
cx.open_window con MetaApp::<NakuiBackend>::new como root view.
- Dep nueva yahweh-widget-meta-form.
- Tests E2E quedan: event_log_replay, morphism_pipeline real
sales, load_ui_modules x3 (4 shell). NakuiBackend tests siguen
en backend.rs (8). Widget tests en su crate.
Distribución total tests refactor yahweh:
- yahweh-meta-schema: 8
- yahweh-meta-runtime: 42
- yahweh-widget-meta-form: 3
- brahman-cards: 26
- nakui-ui: 12 (4 shell + 8 backend)
Total: 91 tests cubriendo el área.
Cada crate compila individualmente. Fase 3 (shell mínimo) era
implícita en F2c — el shell ya quedó en 424 líneas.
Pendientes restantes: KCL → Nickel, eliminar card.k.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>