106 lines
3.4 KiB
Rust
106 lines
3.4 KiB
Rust
//! Tray icon management — swap icon based on connection/agent status.
|
|
|
|
use crate::state::AgentStatus;
|
|
use tauri::image::Image;
|
|
|
|
/// Embedded tray icon PNGs (22x22, RGBA).
|
|
const ICON_IDLE: &[u8] = include_bytes!("../../icons/tray-idle.png");
|
|
const ICON_WORKING: &[u8] = include_bytes!("../../icons/tray-working.png");
|
|
const ICON_ERROR: &[u8] = include_bytes!("../../icons/tray-error.png");
|
|
const ICON_DISCONNECTED: &[u8] = include_bytes!("../../icons/tray-disconnected.png");
|
|
|
|
/// Select the appropriate tray icon for the current state.
|
|
pub fn icon_for_state(connected: bool, status: AgentStatus) -> Image<'static> {
|
|
let bytes: &[u8] = if !connected {
|
|
ICON_DISCONNECTED
|
|
} else {
|
|
match status {
|
|
AgentStatus::Idle => ICON_IDLE,
|
|
AgentStatus::Working => ICON_WORKING,
|
|
AgentStatus::Error => ICON_ERROR,
|
|
}
|
|
};
|
|
Image::from_bytes(bytes).expect("embedded tray icon is a valid PNG")
|
|
}
|
|
|
|
/// Tooltip text for the current state.
|
|
pub fn tooltip_for_state(connected: bool, status: AgentStatus) -> &'static str {
|
|
if !connected {
|
|
return "ZeroClaw — Disconnected";
|
|
}
|
|
match status {
|
|
AgentStatus::Idle => "ZeroClaw — Idle",
|
|
AgentStatus::Working => "ZeroClaw — Working",
|
|
AgentStatus::Error => "ZeroClaw — Error",
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn icon_disconnected_when_not_connected() {
|
|
// Should not panic — icon bytes are valid PNGs.
|
|
let _img = icon_for_state(false, AgentStatus::Idle);
|
|
let _img = icon_for_state(false, AgentStatus::Working);
|
|
let _img = icon_for_state(false, AgentStatus::Error);
|
|
}
|
|
|
|
#[test]
|
|
fn icon_connected_variants() {
|
|
let _idle = icon_for_state(true, AgentStatus::Idle);
|
|
let _working = icon_for_state(true, AgentStatus::Working);
|
|
let _error = icon_for_state(true, AgentStatus::Error);
|
|
}
|
|
|
|
#[test]
|
|
fn tooltip_disconnected() {
|
|
assert_eq!(
|
|
tooltip_for_state(false, AgentStatus::Idle),
|
|
"ZeroClaw — Disconnected"
|
|
);
|
|
// Agent status is irrelevant when disconnected.
|
|
assert_eq!(
|
|
tooltip_for_state(false, AgentStatus::Working),
|
|
"ZeroClaw — Disconnected"
|
|
);
|
|
assert_eq!(
|
|
tooltip_for_state(false, AgentStatus::Error),
|
|
"ZeroClaw — Disconnected"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn tooltip_connected_variants() {
|
|
assert_eq!(
|
|
tooltip_for_state(true, AgentStatus::Idle),
|
|
"ZeroClaw — Idle"
|
|
);
|
|
assert_eq!(
|
|
tooltip_for_state(true, AgentStatus::Working),
|
|
"ZeroClaw — Working"
|
|
);
|
|
assert_eq!(
|
|
tooltip_for_state(true, AgentStatus::Error),
|
|
"ZeroClaw — Error"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn embedded_icons_are_valid_png() {
|
|
// Verify the PNG signature (first 8 bytes) of each embedded icon.
|
|
let png_sig: &[u8] = &[0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
|
|
assert!(ICON_IDLE.starts_with(png_sig), "idle icon not valid PNG");
|
|
assert!(
|
|
ICON_WORKING.starts_with(png_sig),
|
|
"working icon not valid PNG"
|
|
);
|
|
assert!(ICON_ERROR.starts_with(png_sig), "error icon not valid PNG");
|
|
assert!(
|
|
ICON_DISCONNECTED.starts_with(png_sig),
|
|
"disconnected icon not valid PNG"
|
|
);
|
|
}
|
|
}
|