feat(nakui-ui): atajo Esc para cancelar el modal de delete

`capture_key_down` en el root div: si event.keystroke.key=="escape" y
hay pending_delete, lo limpia y emite toast "delete cancelado (X)
[esc]". Capture phase (no bubble) intercepta el Esc antes de que
cualquier TextInput descendiente lo consuma. Sin pending el handler
es no-op, el evento sigue su flujo.

Hint visual en el banner: subtítulo amber tenue
"Esc para cancelar · click [Confirmar] para borrar" para que el
usuario descubra el atajo sin RTFM.

35 tests verdes. El handler son 8 líneas no-testeables sin GPUI cx;
type-check garantiza wireup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-09 21:58:15 +00:00
parent bcccf3b953
commit 9951307fd9
2 changed files with 53 additions and 2 deletions
+25 -2
View File
@@ -28,7 +28,7 @@ use std::sync::{Arc, Mutex};
use gpui::{
div, prelude::*, px, App, Application, Bounds, ClickEvent, Context, Entity, IntoElement,
Render, SharedString, Window, WindowBounds, WindowOptions,
KeyDownEvent, Render, SharedString, Window, WindowBounds, WindowOptions,
};
use nakui_core::delta::{FieldOp, FieldPath};
use nakui_core::event_log::{
@@ -1037,6 +1037,21 @@ impl Render for MetaUi {
.flex_col()
.size_full()
.bg(bg)
// Capture phase: el Esc llega al root ANTES que cualquier
// TextInput descendiente. Si hay un delete pendiente, lo
// cancelamos. Sin pending no hacemos nada (el evento sigue
// su flujo normal y el TextInput recibe el Esc bubble).
.capture_key_down(cx.listener(|this, event: &KeyDownEvent, _w, cx| {
if event.keystroke.key != "escape" {
return;
}
if let Some((entity, _id)) = this.pending_delete.take() {
this.toast = Some(SharedString::from(format!(
"delete cancelado ({entity}) [esc]"
)));
cx.notify();
}
}))
.when_some(error_banner, |d, b| d.child(b))
.when_some(confirm_banner, |d, b| d.child(b))
.child(
@@ -1089,7 +1104,15 @@ impl MetaUi {
.child(
div()
.flex_grow()
.child(format!("¿Borrar {entity_owned} {id_short}?")),
.flex()
.flex_col()
.child(format!("¿Borrar {entity_owned} {id_short}?"))
.child(
div()
.text_size(px(10.))
.text_color(gpui::rgb(0xc0a070))
.child("Esc para cancelar · click [Confirmar] para borrar"),
),
)
.child(
div()