Fase 2 del plan «shell»: carmen reconoce la ventana del shell y le
reserva su sitio, en vez de teselarla como una más.
Una ventana cuyo `app_id` es `carmen.shell` no entra en el teselado:
carmen le reserva una franja de 40 px al pie de la salida, la dimensiona
y la fija ahí, y la compone sobre todas las demás. El Cerebro tesela el
resto de ventanas en el área que queda.
- `mirada-protocol`: nuevo `BodyEvent::OutputResized { id, w, h }` — el
Cerebro cambia el área útil de una salida **sin** perder el escritorio
que muestra (a diferencia de quitar y volver a añadir la salida — que,
de paso, era un bug latente al redimensionar la ventana winit).
- `mirada-brain`: `Desktop` atiende `OutputResized` (test nuevo).
- `mirada-body`: `BodyState::resize_output`.
- `mirada-compositor`: `ManagedWindow.is_shell`, `App.output_size`,
`dock_shell`/`output_changed`; `register_toplevel` no registra el
shell en el Cerebro; al cerrarse libera la franja. El shell se compone
y se enfoca con el ratón aunque no viva en el Cerebro; no lleva marco.
El backend winit usa ahora `resize_output` al redimensionar.
GPUI no habla `wlr-layer-shell`, así que el acople es por `app_id`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Un escritorio sin forma de abrir una terminal no es usable. Ahora el
keymap puede lanzar programas:
- `mirada-protocol`: nuevo `BrainCommand::Spawn(String)`.
- `mirada-brain`: `DesktopAction::Spawn(String)` con forma textual
`spawn:<comando>` (`Display`/`FromStr`); `Desktop::apply` la traduce
a `BrainCommand::Spawn`. El keymap por defecto trae
`Super+Shift+Return` → `spawn:foot`. `DesktopAction` deja de ser
`Copy` (lleva el comando) — `Keymap::lookup` clona en vez de copiar.
- `mirada-body`: `BodyOp::Spawn(String)`.
- `mirada-compositor`: `exec_op` ejecuta el spawn con un helper
`spawn_command` (`sh -c`, hereda `WAYLAND_DISPLAY`), que también
recoge el lanzamiento de `MIRADA_STARTUP` — antes duplicado.
`spawn:foot --title x` también funciona desde `mirada-ctl`. Tests
nuevos del round-trip textual y del flujo atajo→comando.
Nota: un keymap.ron ya existente no recibe el atajo nuevo; hay que
añadir la línea a mano o borrar el archivo para regenerarlo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ToggleFullscreen (Super+Shift+f) lleva la ventana enfocada a pantalla
completa: cubre toda la salida sin gap, oculta al resto y se lleva el
foco. Distinto del modo Monocle (un modo de teselado): es un estado
por ventana que ignora el layout.
- Workspace.fullscreen: Option<WindowId>; set_fullscreen / fullscreen();
remove() lo limpia si se cierra esa ventana.
- placements() da a la fullscreen el rect completo y marca al resto
visible: false. WindowPlacement y BodyOp::Configure llevan
fullscreen: bool.
- mirada-compositor fija el estado xdg_toplevel::Fullscreen en la
superficie, para que el cliente lo sepa.
- Cableado en keymap, HUD de mirada y mirada-ctl.
Verificado end-to-end con headless-ctl. mirada-protocol 10->11,
mirada-brain 51->52.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Una ventana puede salir del teselado y flotar: conserva su propio
rectángulo y se compone por encima de las teseladas.
- Workspace guarda las flotantes en un mapa aparte; layout() tesela
sólo las no-flotantes y añade las flotantes al final (orden de
pintado). set_floating / is_floating.
- WindowPlacement y BodyOp::Configure llevan floating: bool. BodyState
detecta el cambio de floating como cualquier otro reconfigure.
- DesktopAction::ToggleFloat (Super+f): saca la enfocada a un
rectángulo centrado al 60 % de la pantalla, o la devuelve al teselado.
En Monocle, una flotante sigue visible.
- mirada-compositor ordena las flotantes al frente de la lista
front-to-back de elementos → se pintan encima.
- HUD de mirada marca las flotantes; mirada-ctl toggle-float.
Verificado end-to-end con headless-ctl. mirada-layout 30->32,
mirada-protocol 9->10, mirada-body 13->14, mirada-brain 41->42.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BodyState agnóstico de smithay: lleva salidas + superficies, traduce
BrainCommand a BodyOp (sólo lo que cambia) y emite BodyEvent desde los
mutadores del backend. Ejemplo headless: Cuerpo sin gráficos guiado por
stdin para ejercitar el bucle Cerebro↔Cuerpo. 13 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>