新增: 后台会话系统通知与托盘运行态
- 完成/失败/需介入三类系统通知,主窗聚焦静默 - 同类通知五秒合并,用户主动停止不通知 - 托盘菜单与悬浮提示实时显运行中会话数 - 关窗遇运行会话提示后台续跑,每进程一次 - 侧栏常驻徽章显后台运行会话数
This commit is contained in:
+44
-8
@@ -1,24 +1,34 @@
|
||||
//! 托盘常驻:图标 + 菜单[显示主窗口/退出]。关窗口不退出,托盘退出才真正结束进程。
|
||||
//! 托盘常驻:图标 + 菜单[运行态(纯展示)/显示主窗口/退出]。关窗口不退出,托盘退出才真正结束进程。
|
||||
//!
|
||||
//! - 主窗口 CloseRequested 被 lib.rs 拦截(prevent_close + hide),进程常驻托盘
|
||||
//! - 托盘菜单「显示主窗口」唤起 / 「退出」app.exit(0) 真正结束
|
||||
//! - TrayHandle 经 app.manage 保活,防托盘图标被 GC 移除
|
||||
//! - P1 bg 化:运行态菜单项 + tooltip 随 ConvState 迁移刷新(guard.transition 挂钩 update_tray_runtime)
|
||||
use tauri::{
|
||||
AppHandle, Manager,
|
||||
menu::{Menu, MenuItem},
|
||||
tray::TrayIconBuilder,
|
||||
};
|
||||
|
||||
/// managed state 保活,防 GC 移除托盘图标。
|
||||
/// 字段不读仅用于持有 TrayIcon(保活);零调用方≠垃圾,预留保留(对齐 state.rs releases 标法)。
|
||||
#[allow(dead_code)]
|
||||
pub struct TrayHandle(pub tauri::tray::TrayIcon);
|
||||
use crate::state::AppState;
|
||||
|
||||
/// 创建托盘:图标 + 菜单(显示主窗口/退出)。
|
||||
/// managed state 保活,防 GC 移除托盘图标。
|
||||
/// tray 持 TrayIcon 本体(保活为主,update_tray_runtime 借用刷 tooltip);
|
||||
/// status 为运行态纯展示项句柄,供 update_tray_runtime 刷文本。
|
||||
pub struct TrayHandle {
|
||||
/// TrayIcon 本体,主要职责是经 manage 保活防 GC。
|
||||
pub tray: tauri::tray::TrayIcon,
|
||||
/// 运行态菜单项(enabled=false 纯展示),文本由 update_tray_runtime 随会话运行数刷新。
|
||||
pub status: MenuItem<tauri::Wry>,
|
||||
}
|
||||
|
||||
/// 创建托盘:图标 + 菜单(运行态/显示主窗口/退出)。
|
||||
pub fn setup_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||
// 运行态纯展示项:enabled=false 置灰不响应点击,文本由 update_tray_runtime 维护
|
||||
let status = MenuItem::with_id(app, "status", "无运行中会话", false, None::<&str>)?;
|
||||
let show = MenuItem::with_id(app, "show", "显示主窗口", true, None::<&str>)?;
|
||||
let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
|
||||
let menu = Menu::with_items(app, &[&show, &quit])?;
|
||||
let menu = Menu::with_items(app, &[&status, &show, &quit])?;
|
||||
// 复用窗口图标(tauri.conf.json bundle.icon 已配置),缺则 panic 提示配置
|
||||
let icon = app
|
||||
.default_window_icon()
|
||||
@@ -35,10 +45,36 @@ pub fn setup_tray(app: &AppHandle) -> tauri::Result<()> {
|
||||
_ => {}
|
||||
})
|
||||
.build(app)?;
|
||||
app.manage(TrayHandle(tray));
|
||||
app.manage(TrayHandle { tray, status });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 同步托盘运行态:状态项文本 + tooltip 反映当前运行中 AI 会话数。
|
||||
///
|
||||
/// 挂钩点:guard.rs GeneratingGuard::transition(每次 ConvState 迁移必经且已持 app_handle)。
|
||||
/// set_text/set_tooltip 均为同步 API,本函数保持同步,同步上下文直调不引入 await。
|
||||
/// AppState / TrayHandle 任一未注入(setup 早期)静默跳过。
|
||||
pub fn update_tray_runtime(app: &AppHandle) {
|
||||
let (Some(handle), Some(state)) =
|
||||
(app.try_state::<TrayHandle>(), app.try_state::<AppState>())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let n = state.conv_states.active_convs().len();
|
||||
let text = if n > 0 {
|
||||
format!("运行中 {} 个会话", n)
|
||||
} else {
|
||||
"无运行中会话".to_string()
|
||||
};
|
||||
let tooltip = if n > 0 {
|
||||
format!("DevFlow (运行中 {} 个会话)", n)
|
||||
} else {
|
||||
"DevFlow (MCP server running)".to_string()
|
||||
};
|
||||
let _ = handle.status.set_text(text);
|
||||
let _ = handle.tray.set_tooltip(Some(tooltip));
|
||||
}
|
||||
|
||||
/// 唤起主窗口:show + unminimize + set_focus(单实例二次启动 / 托盘菜单共用)。
|
||||
pub(crate) fn show_main(app: &AppHandle) {
|
||||
if let Some(w) = app.get_webview_window("main") {
|
||||
|
||||
Reference in New Issue
Block a user