Tray-Menü: Aktionswörter raus — Status-Punkt (grün = läuft) ganz links im Menü-Icon, daneben Projekt-Icon, Label nur der Name
This commit is contained in:
+43
-17
@@ -598,12 +598,33 @@ fn project_icon(project: String) -> Result<Option<String>, String> {
|
||||
Ok(Some(format!("data:image/png;base64,{}", STANDARD.encode(png))))
|
||||
}
|
||||
|
||||
/// Projekt-Icon fürs Tray-Menü: sips konvertiert (auch ICNS) und skaliert auf
|
||||
/// 36 px; muda rendert Menü-Icons auf macOS mit 18 pt.
|
||||
fn menu_icon(paths: &Paths, project: &str) -> Result<Option<tauri::image::Image<'static>>, String> {
|
||||
let Some(icon) = read_project_config_in(paths, project)?.terminal.icon else {
|
||||
return Ok(None);
|
||||
};
|
||||
/// Menü-Icon fürs Tray: ganz links der Status-Punkt (grün = läuft), daneben
|
||||
/// das Projekt-Icon (sips konvertiert, auch ICNS). Ein RGBA-Bild 56×36 px;
|
||||
/// muda rendert Menü-Icons mit 18 pt Höhe und proportionaler Breite.
|
||||
fn menu_icon(
|
||||
paths: &Paths,
|
||||
project: &str,
|
||||
running: bool,
|
||||
) -> Result<tauri::image::Image<'static>, String> {
|
||||
const H: usize = 36;
|
||||
const DOT_W: usize = 20;
|
||||
const W: usize = DOT_W + H;
|
||||
let mut canvas = vec![0u8; W * H * 4];
|
||||
if running {
|
||||
// macOS-Systemgrün, weicher Rand über Alpha.
|
||||
let (cx, cy, r) = (10.0f32, 18.0f32, 7.0f32);
|
||||
for y in 0..H {
|
||||
for x in 0..DOT_W {
|
||||
let d = ((x as f32 + 0.5 - cx).powi(2) + (y as f32 + 0.5 - cy).powi(2)).sqrt();
|
||||
let a = (r - d + 0.5).clamp(0.0, 1.0);
|
||||
if a > 0.0 {
|
||||
let i = (y * W + x) * 4;
|
||||
canvas[i..i + 4].copy_from_slice(&[52, 199, 89, (a * 255.0) as u8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(icon) = read_project_config_in(paths, project)?.terminal.icon {
|
||||
let src = resolve_icon_path(paths, project, &icon);
|
||||
let tmp = std::env::temp_dir().join(format!("ai-control-tray-icon-{project}.png"));
|
||||
let out = Command::new("sips")
|
||||
@@ -619,7 +640,17 @@ fn menu_icon(paths: &Paths, project: &str) -> Result<Option<tauri::image::Image<
|
||||
let bytes = fs::read(&tmp).map_err(|e| format!("{}: {e}", tmp.display()))?;
|
||||
let _ = fs::remove_file(&tmp);
|
||||
let img = tauri::image::Image::from_bytes(&bytes).map_err(|e| e.to_string())?;
|
||||
Ok(Some(img))
|
||||
let (iw, ih) = (img.width() as usize, img.height() as usize);
|
||||
let rgba = img.rgba();
|
||||
for y in 0..ih.min(H) {
|
||||
for x in 0..iw.min(H) {
|
||||
let src_i = (y * iw + x) * 4;
|
||||
let dst_i = (y * W + DOT_W + x) * 4;
|
||||
canvas[dst_i..dst_i + 4].copy_from_slice(&rgba[src_i..src_i + 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(tauri::image::Image::new_owned(canvas, W as u32, H as u32))
|
||||
}
|
||||
|
||||
/// Terminal-Einstellungen eines Projekts, für den Terminal-Prozess.
|
||||
@@ -1511,8 +1542,8 @@ pub fn run() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tray-Menü: pro Projekt ein Eintrag mit Icon; das Label nennt die Aktion —
|
||||
/// „starten" öffnet das Terminal, „zeigen" holt das laufende nach vorn.
|
||||
/// Tray-Menü: pro Projekt ein Eintrag — Status-Punkt, Projekt-Icon, Name.
|
||||
/// Klick startet das Projekt bzw. holt das laufende Terminal nach vorn.
|
||||
fn tray_menu(
|
||||
app: &tauri::AppHandle,
|
||||
) -> Result<tauri::menu::Menu<tauri::Wry>, Box<dyn std::error::Error>> {
|
||||
@@ -1524,18 +1555,13 @@ fn tray_menu(
|
||||
let mut projects = list_projects_in(&paths)?;
|
||||
projects.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
for p in projects {
|
||||
let label = if p.running {
|
||||
format!("{} zeigen", p.name)
|
||||
} else {
|
||||
format!("{} starten", p.name)
|
||||
};
|
||||
let icon = menu_icon(&paths, &p.name)?;
|
||||
let icon = menu_icon(&paths, &p.name, p.running)?;
|
||||
menu.append(&IconMenuItem::with_id(
|
||||
app,
|
||||
format!("project:{}", p.name),
|
||||
label,
|
||||
&p.name,
|
||||
true,
|
||||
icon,
|
||||
Some(icon),
|
||||
None::<&str>,
|
||||
)?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user