feat(mirada): reglas de ventana — escritorio y flotante por app_id

mirada-brain::rules — config declarativa que decide qué hacer con una
ventana al abrirse, mismo patrón que el keymap.

- Rule casa por subcadena de app_id y/o title (sin distinguir
  mayúsculas; vacío = cualquiera) y aplica un destino: workspace (1..9)
  y/o floating. Gana la primera regla que case.
- Rules en RON (~/.config/mirada/rules.ron); la primera vez se escribe
  una plantilla con ejemplos comentados, si está corrupta se ignora.
- Desktop consulta Rules::resolve en cada WindowOpened — el evento ya
  trae app_id/title — y abre la ventana en su escritorio, flotando si
  toca. set_rules en Desktop; las apps cargan rules.ron al arrancar.

mirada-brain 42->51 tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 01:01:14 +00:00
parent 4719f7c9f9
commit 6dfd9e62ac
7 changed files with 311 additions and 8 deletions
+14 -2
View File
@@ -59,7 +59,9 @@ use smithay::{
};
use mirada_body::{BodyOp, BodyState};
use mirada_brain::{BodyEvent, BrainCommand, CtlReply, CtlRequest, CtlServer, Desktop, Keymap};
use mirada_brain::{
BodyEvent, BrainCommand, CtlReply, CtlRequest, CtlServer, Desktop, Keymap, Rules,
};
use mirada_link::BodyLink;
// ---------------------------------------------------------------------
@@ -469,6 +471,14 @@ fn send_frames_surface_tree(surface: &WlSurface, time: u32) {
// Bucle principal
// ---------------------------------------------------------------------
/// Carga las reglas de ventana del usuario, o ninguna si no hay archivo.
fn load_user_rules() -> Rules {
match Rules::default_path() {
Some(p) => Rules::load_or_default(&p),
None => Rules::default(),
}
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<App> = Display::new()?;
let dh = display.handle();
@@ -494,7 +504,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
Some(p) => Keymap::load_or_init(p),
None => Keymap::default(),
};
Brain::Embedded(Desktop::with_keymap(keymap))
let mut desktop = Desktop::with_keymap(keymap);
desktop.set_rules(load_user_rules());
Brain::Embedded(desktop)
}
};
+15 -2
View File
@@ -40,7 +40,7 @@ use gpui::{
};
use mirada_brain::{
BodyEvent, BrainCommand, CtlConn, CtlReply, CtlRequest, CtlServer, Desktop, DesktopAction,
Keymap, KeymapWatch, LayoutMode, WindowId, WindowPlacement,
Keymap, KeymapWatch, LayoutMode, Rules, WindowId, WindowPlacement,
};
use mirada_link::BrainLink;
use nahual_launcher::launch_app;
@@ -105,8 +105,13 @@ impl Mirada {
}
};
// Reglas de ventana (~/.config/mirada/rules.ron): a qué
// escritorio va cada ventana, si flota.
let mut desktop = Desktop::with_keymap(keymap);
desktop.set_rules(load_user_rules());
let mut app = Self {
desktop: Desktop::with_keymap(keymap),
desktop,
placements: Vec::new(),
next_id: 1,
link,
@@ -316,6 +321,14 @@ fn connect_body() -> Option<BrainLink> {
BrainLink::connect(&path).ok()
}
/// Carga las reglas de ventana del usuario, o ninguna si no hay archivo.
fn load_user_rules() -> Rules {
match Rules::default_path() {
Some(p) => Rules::load_or_default(&p),
None => Rules::default(),
}
}
/// Nombre legible de un modo de teselado.
fn mode_name(m: LayoutMode) -> &'static str {
match m {