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:
+57
-31
@@ -598,28 +598,59 @@ fn project_icon(project: String) -> Result<Option<String>, String> {
|
|||||||
Ok(Some(format!("data:image/png;base64,{}", STANDARD.encode(png))))
|
Ok(Some(format!("data:image/png;base64,{}", STANDARD.encode(png))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Projekt-Icon fürs Tray-Menü: sips konvertiert (auch ICNS) und skaliert auf
|
/// Menü-Icon fürs Tray: ganz links der Status-Punkt (grün = läuft), daneben
|
||||||
/// 36 px; muda rendert Menü-Icons auf macOS mit 18 pt.
|
/// das Projekt-Icon (sips konvertiert, auch ICNS). Ein RGBA-Bild 56×36 px;
|
||||||
fn menu_icon(paths: &Paths, project: &str) -> Result<Option<tauri::image::Image<'static>>, String> {
|
/// muda rendert Menü-Icons mit 18 pt Höhe und proportionaler Breite.
|
||||||
let Some(icon) = read_project_config_in(paths, project)?.terminal.icon else {
|
fn menu_icon(
|
||||||
return Ok(None);
|
paths: &Paths,
|
||||||
};
|
project: &str,
|
||||||
let src = resolve_icon_path(paths, project, &icon);
|
running: bool,
|
||||||
let tmp = std::env::temp_dir().join(format!("ai-control-tray-icon-{project}.png"));
|
) -> Result<tauri::image::Image<'static>, String> {
|
||||||
let out = Command::new("sips")
|
const H: usize = 36;
|
||||||
.args(["-s", "format", "png", "-z", "36", "36"])
|
const DOT_W: usize = 20;
|
||||||
.arg(&src)
|
const W: usize = DOT_W + H;
|
||||||
.arg("--out")
|
let mut canvas = vec![0u8; W * H * 4];
|
||||||
.arg(&tmp)
|
if running {
|
||||||
.output()
|
// macOS-Systemgrün, weicher Rand über Alpha.
|
||||||
.map_err(|e| format!("sips: {e}"))?;
|
let (cx, cy, r) = (10.0f32, 18.0f32, 7.0f32);
|
||||||
if !out.status.success() {
|
for y in 0..H {
|
||||||
return Err(format!("sips: {}", String::from_utf8_lossy(&out.stderr)));
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let bytes = fs::read(&tmp).map_err(|e| format!("{}: {e}", tmp.display()))?;
|
if let Some(icon) = read_project_config_in(paths, project)?.terminal.icon {
|
||||||
let _ = fs::remove_file(&tmp);
|
let src = resolve_icon_path(paths, project, &icon);
|
||||||
let img = tauri::image::Image::from_bytes(&bytes).map_err(|e| e.to_string())?;
|
let tmp = std::env::temp_dir().join(format!("ai-control-tray-icon-{project}.png"));
|
||||||
Ok(Some(img))
|
let out = Command::new("sips")
|
||||||
|
.args(["-s", "format", "png", "-z", "36", "36"])
|
||||||
|
.arg(&src)
|
||||||
|
.arg("--out")
|
||||||
|
.arg(&tmp)
|
||||||
|
.output()
|
||||||
|
.map_err(|e| format!("sips: {e}"))?;
|
||||||
|
if !out.status.success() {
|
||||||
|
return Err(format!("sips: {}", String::from_utf8_lossy(&out.stderr)));
|
||||||
|
}
|
||||||
|
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())?;
|
||||||
|
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.
|
/// 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 —
|
/// Tray-Menü: pro Projekt ein Eintrag — Status-Punkt, Projekt-Icon, Name.
|
||||||
/// „starten" öffnet das Terminal, „zeigen" holt das laufende nach vorn.
|
/// Klick startet das Projekt bzw. holt das laufende Terminal nach vorn.
|
||||||
fn tray_menu(
|
fn tray_menu(
|
||||||
app: &tauri::AppHandle,
|
app: &tauri::AppHandle,
|
||||||
) -> Result<tauri::menu::Menu<tauri::Wry>, Box<dyn std::error::Error>> {
|
) -> 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)?;
|
let mut projects = list_projects_in(&paths)?;
|
||||||
projects.sort_by(|a, b| a.name.cmp(&b.name));
|
projects.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
for p in projects {
|
for p in projects {
|
||||||
let label = if p.running {
|
let icon = menu_icon(&paths, &p.name, p.running)?;
|
||||||
format!("{} zeigen", p.name)
|
|
||||||
} else {
|
|
||||||
format!("{} starten", p.name)
|
|
||||||
};
|
|
||||||
let icon = menu_icon(&paths, &p.name)?;
|
|
||||||
menu.append(&IconMenuItem::with_id(
|
menu.append(&IconMenuItem::with_id(
|
||||||
app,
|
app,
|
||||||
format!("project:{}", p.name),
|
format!("project:{}", p.name),
|
||||||
label,
|
&p.name,
|
||||||
true,
|
true,
|
||||||
icon,
|
Some(icon),
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
)?)?;
|
)?)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user