From 687642a096001f0383e2a453d553049b804070ae Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Wed, 23 Sep 2026 21:32:39 -0400 Subject: [PATCH] feat(tray): "Restart to update" item and tooltip (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `set_tray_update_ready(version | null)` inserts a "Restart to update (vX)" item at the top of the tray menu and sets the tooltip to "Lotus Chat — update ready"; null removes both. The item only exists while an update is pending. Clicking it reveals the window and emits `lotus-tray-install-update`, and cinny runs its normal install flow (progress, retries, failure toast). No icon dot: the unread dot already uses that corner. Bump cinny to 6edfe700 (the web half). Closes #6 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA --- cinny | 2 +- src-tauri/src/lib.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/cinny b/cinny index d9e7f77..6edfe70 160000 --- a/cinny +++ b/cinny @@ -1 +1 @@ -Subproject commit d9e7f77402ddf26712d7717fccbc0ade16ffb5d6 +Subproject commit 6edfe700204a626d97c62ae2b1c3d0faca49933c diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 841191a..22723ed 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -564,6 +564,53 @@ fn set_tray_unread(unread: bool, state: tauri::State<'_, TrayUnreadState>) -> Re state.tray.set_icon(Some(icon)).map_err(|e| e.to_string()) } +/// cinny-desktop #6: the tray's "Restart to update" item. Inserted at the top +/// of the menu only while an update is available, so it isn't a dead entry the +/// rest of the time. +struct TrayUpdateState { + menu: Menu, + item: MenuItem, + shown: std::sync::Mutex, +} + +/// Show (`Some(version)`) or clear (`None`) the tray's update affordance: the +/// menu item and the tooltip. The web client drives it from its update status, +/// and clicking the item hands the install back to the web flow +/// (`lotus-tray-install-update`) so progress and failures are shown in-app. +#[tauri::command] +fn set_tray_update_ready(app: tauri::AppHandle, version: Option) -> Result<(), String> { + let Some(state) = app.try_state::() else { + return Ok(()); + }; + let unread = app.try_state::(); + let mut shown = state.shown.lock().map_err(|e| e.to_string())?; + match version { + Some(version) => { + state + .item + .set_text(format!("Restart to update (v{version})")) + .map_err(|e| e.to_string())?; + if !*shown { + state.menu.insert(&state.item, 0).map_err(|e| e.to_string())?; + *shown = true; + } + if let Some(u) = unread { + let _ = u.tray.set_tooltip(Some("Lotus Chat — update ready")); + } + } + None => { + if *shown { + state.menu.remove(&state.item).map_err(|e| e.to_string())?; + *shown = false; + } + if let Some(u) = unread { + let _ = u.tray.set_tooltip(Some("Lotus Chat")); + } + } + } + Ok(()) +} + /// Flash the taskbar button to draw attention (e.g. a new mention while the /// window is unfocused). Clears automatically once the window is focused. #[tauri::command] @@ -611,6 +658,7 @@ pub fn run() { set_badge_count, set_tray_unread, get_tray_dnd, + set_tray_update_ready, flash_window, focus_main_window, send_notification, @@ -695,6 +743,12 @@ pub fn run() { .on_menu_event(move |app, event| match event.id.as_ref() { "open" => show_main(app), "quit" => app.exit(0), + "update" => { + // Show the window so the download progress (and any + // failure) is visible; the web side runs the install. + show_main(app); + native::emit_to_web(app, "lotus-tray-install-update", "{}"); + } "dnd" => { let checked = dnd_for_event.is_checked().unwrap_or(false); native::emit_to_web( @@ -739,6 +793,12 @@ pub fn run() { // Keep a handle to the DND CheckMenuItem so `get_tray_dnd` can // report its live checkstate for web re-hydration after reload. app.manage(TrayDndState(dnd_item)); + + app.manage(TrayUpdateState { + menu: tray_menu.clone(), + item: MenuItem::with_id(app, "update", "Restart to update", true, None::<&str>)?, + shown: std::sync::Mutex::new(false), + }); } else { eprintln!("tray: no bundled window icon; skipping system tray setup"); }