Ashiedu's picture
Sync unified workbench
0490201 verified

A newer version of the Gradio SDK is available: 6.14.0

Upgrade

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

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<Vec<MonitorInfo>, 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:

"synesthesia:allow-window-set-always-on-top",
"synesthesia:allow-window-get-monitors",
"synesthesia:allow-window-move-to-monitor"

Add to src/ipc/commands.ts:

export const windowSetAlwaysOnTop = (enable: boolean): Promise<void> =>
  invoke('window_set_always_on_top', { enable });

export const windowGetMonitors = (): Promise<MonitorInfo[]> =>
  invoke('window_get_monitors');

export const windowMoveToMonitor = (index: number): Promise<void> =>
  invoke('window_move_to_monitor', { index });

GitHub CLI

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