# T-014: Window Management — Resize Constraints, Always-on-Top, Multi-Monitor **Type:** Task | **Phase:** 0 | **Autonomy:** `agent:autonomous` | **Stack:** `stack:rust` `stack:typescript` **Version:** v0.1 | **Iteration:** iter-1 | **Effort:** XS (1 hour) > ⚠️ **Scope:** Add Tauri window management commands callable from the UI: toggle always-on-top, move to monitor, get current monitor info. Min/max size constraints are already set in `tauri.conf.json` (T-001). This task adds the runtime IPC surface for window behaviour the frontend needs. --- ## Prerequisites - [ ] T-001 merged — window exists with correct min/max constraints in `tauri.conf.json` - [ ] T-002 merged — IPC command pattern established ## Acceptance Criteria - [ ] `window_set_always_on_top(enable: bool)` IPC command works - [ ] `window_get_monitors()` returns list of connected monitors with position/size - [ ] `window_move_to_monitor(index: u32)` centres the window on the specified monitor - [ ] All three commands registered in `tauri::generate_handler![]` and capability file - [ ] TypeScript wrappers added to `src/ipc/commands.ts` - [ ] `cargo check --workspace` — zero errors ## `kansas/src/commands/window.rs` ```rust use tauri::{AppHandle, Manager, Window}; use serde::Serialize; use crate::ipc::error::IpcError; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct MonitorInfo { pub index: u32, pub name: String, pub width: u32, pub height: u32, pub x: i32, pub y: i32, pub scale_factor: f64, pub is_primary: bool, } #[tauri::command] pub fn window_set_always_on_top( enable: bool, window: Window, ) -> Result<(), IpcError> { window.set_always_on_top(enable) .map_err(|e| IpcError::Internal(e.to_string())) } #[tauri::command] pub fn window_get_monitors( app: AppHandle, ) -> Result, IpcError> { let monitors = app.available_monitors() .map_err(|e| IpcError::Internal(e.to_string()))?; let primary = app.primary_monitor() .ok().flatten() .and_then(|m| m.name().map(|s| s.to_string())); Ok(monitors.into_iter().enumerate().map(|(i, m)| { let pos = m.position(); let size = m.size(); MonitorInfo { index: i as u32, name: m.name().unwrap_or("Unknown").to_string(), width: size.width, height: size.height, x: pos.x, y: pos.y, scale_factor: m.scale_factor(), is_primary: primary.as_deref() == m.name(), } }).collect()) } #[tauri::command] pub fn window_move_to_monitor( index: u32, window: Window, app: AppHandle, ) -> Result<(), IpcError> { let monitors = app.available_monitors() .map_err(|e| IpcError::Internal(e.to_string()))?; let monitor = monitors.get(index as usize) .ok_or_else(|| IpcError::InvalidParam(format!("Monitor index {index} out of range")))?; let pos = monitor.position(); let size = monitor.size(); let win_size = window.outer_size() .map_err(|e| IpcError::Internal(e.to_string()))?; let x = pos.x + ((size.width as i32 - win_size.width as i32) / 2); let y = pos.y + ((size.height as i32 - win_size.height as i32) / 2); window.set_position(tauri::PhysicalPosition::new(x, y)) .map_err(|e| IpcError::Internal(e.to_string())) } ``` Register in `main.rs` handler list and add to `capabilities/default.json`: ```json "synesthesia:allow-window-set-always-on-top", "synesthesia:allow-window-get-monitors", "synesthesia:allow-window-move-to-monitor" ``` Add to `src/ipc/commands.ts`: ```typescript export const windowSetAlwaysOnTop = (enable: boolean): Promise => invoke('window_set_always_on_top', { enable }); export const windowGetMonitors = (): Promise => invoke('window_get_monitors'); export const windowMoveToMonitor = (index: number): Promise => invoke('window_move_to_monitor', { index }); ``` --- ## GitHub CLI ```bash gh issue create \ --title "T-014: Window management — always-on-top, multi-monitor, resize constraints" \ --label "type:task,stack:rust,stack:typescript,agent:autonomous,priority:medium,status:ready,day:1" \ --body-file T-014.md ``` **Parent:** GENESIS | **Blocks:** none | **Blocked By:** T-001, T-002