feat(mirada): API de acciones — mirada-ctl + HUD interactivo
Toda acción de escritorio converge en Desktop::apply(DesktopAction); el keymap era sólo un front-end. Esta tanda añade los otros tres. - DesktopAction::FocusWindow(WindowId): direccionamiento directo de una ventana (no sólo ciclar); si está en otro escritorio, salta a él. DesktopAction pasa a ser Serialize/Deserialize (postcard) además de Display/FromStr. - mirada-brain::ctl: el API de control externo. CtlRequest/CtlReply (marco postcard), CtlServer/CtlConn no bloqueantes y send_request. El Cerebro abre el socket y atiende en su bucle: la app mirada siempre, mirada-compositor sólo con el Cerebro embebido. - mirada-ctl: CLI de control estilo swaymsg/hyprctl — `mirada-ctl focus-next | focus-window 5 | workspace 3 | windows`. Parsea la acción de los argumentos vía FromStr. - HUD interactivo en la app mirada: pips de escritorio y ventanas del lienzo clicables (SwitchWorkspace / FocusWindow). - Ejemplo headless-ctl: un Cerebro sin gráficos para probar mirada-ctl en modo desatendido. Verificado end-to-end. mirada-brain: 29 -> 37 tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,18 +11,27 @@
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use mirada_layout::LayoutMode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use mirada_layout::{LayoutMode, WindowId};
|
||||
|
||||
/// Número de escritorios virtuales que mantiene el `Desktop`.
|
||||
pub const WORKSPACE_COUNT: usize = 9;
|
||||
|
||||
/// Una orden de escritorio de alto nivel.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
///
|
||||
/// Es serializable (`postcard`) para viajar por el API de control
|
||||
/// ([`crate::ctl`]) y tiene una forma textual estable ([`Display`] /
|
||||
/// [`FromStr`]) para el keymap y `mirada-ctl`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DesktopAction {
|
||||
/// Mueve el foco a la ventana siguiente del escritorio activo.
|
||||
FocusNext,
|
||||
/// Mueve el foco a la ventana anterior.
|
||||
FocusPrev,
|
||||
/// Enfoca una ventana concreta por su id; si está en otro escritorio,
|
||||
/// salta a él. Para clics de taskbar o `mirada-ctl focus-window`.
|
||||
FocusWindow(WindowId),
|
||||
/// Adelanta la ventana enfocada en el orden de teselado.
|
||||
MoveForward,
|
||||
/// Atrasa la ventana enfocada en el orden de teselado.
|
||||
@@ -69,6 +78,7 @@ impl fmt::Display for DesktopAction {
|
||||
match self {
|
||||
DesktopAction::FocusNext => f.write_str("focus-next"),
|
||||
DesktopAction::FocusPrev => f.write_str("focus-prev"),
|
||||
DesktopAction::FocusWindow(id) => write!(f, "focus-window:{id}"),
|
||||
DesktopAction::MoveForward => f.write_str("move-forward"),
|
||||
DesktopAction::MoveBackward => f.write_str("move-backward"),
|
||||
DesktopAction::CloseFocused => f.write_str("close-focused"),
|
||||
@@ -102,6 +112,12 @@ impl FromStr for DesktopAction {
|
||||
layout_from_slug(slug)
|
||||
.ok_or_else(|| format!("modo de teselado desconocido: '{slug}'"))?,
|
||||
)
|
||||
} else if let Some(id) = s.strip_prefix("focus-window:") {
|
||||
Self::FocusWindow(
|
||||
id.trim()
|
||||
.parse()
|
||||
.map_err(|_| format!("id de ventana inválido: '{id}'"))?,
|
||||
)
|
||||
} else if let Some(n) = s.strip_prefix("send-to-workspace:") {
|
||||
Self::SendToWorkspace(parse_workspace(n)?)
|
||||
} else if let Some(n) = s.strip_prefix("workspace:") {
|
||||
@@ -227,6 +243,14 @@ mod tests {
|
||||
assert!("workspace:0".parse::<DesktopAction>().is_err());
|
||||
assert!("workspace:99".parse::<DesktopAction>().is_err());
|
||||
assert!("layout:fractal".parse::<DesktopAction>().is_err());
|
||||
assert!("focus-window:abc".parse::<DesktopAction>().is_err());
|
||||
assert!("teleport".parse::<DesktopAction>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn focus_window_round_trips_with_its_id() {
|
||||
let a = DesktopAction::FocusWindow(42);
|
||||
assert_eq!(a.to_string(), "focus-window:42");
|
||||
assert_eq!("focus-window:42".parse::<DesktopAction>().unwrap(), a);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user