//! Tauri 应用模块 //! //! 增强诊断日志版本 - 详细记录窗口创建、前端加载和托盘初始化过程 use anyhow::Result; use tauri::{ menu::{Menu, MenuItem, Submenu}, tray::TrayIconBuilder, Manager, Emitter, }; use tracing::{info, warn}; pub mod commands; pub mod state; /// 应用状态 pub use state::AppState; pub use state::AppTheme; /// 运行 Tauri 应用 pub fn run() -> Result<()> { eprintln!(); eprintln!(" [应用] 开始构建 Tauri 应用..."); info!("启动 Tauri 应用..."); // 检查构建上下文 eprintln!(" [检查] 验证 Tauri 上下文..."); let context = tauri::generate_context!(); eprintln!(" ✓ 上下文生成成功"); eprintln!(" - 应用标识:{}", context.config().identifier); eprintln!(" - 应用名称:{:?}", context.config().product_name); let app = tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .manage(AppState::default()) .invoke_handler(tauri::generate_handler![ commands::start_recording, commands::stop_recording, commands::recognize_audio, commands::get_config_cmd, commands::save_config, commands::get_history, commands::clear_history, commands::get_theme, commands::set_theme, commands::select_model_file, ]) .setup(|app| { info!("[设置] 初始化应用插件..."); info!(" - tauri_plugin_shell: 已加载"); info!(" - tauri_plugin_dialog: 已加载"); info!(" - tauri_plugin_fs: 已加载"); // 获取主窗口(由 tauri.conf.json 自动创建) info!("[设置] 获取主窗口..."); if let Some(window) = app.get_webview_window("main") { info!(" ✓ 主窗口已存在 (由配置文件创建)"); // 获取窗口位置和大小 if let Ok(position) = window.outer_position() { info!(" - 窗口位置:({}, {})", position.x, position.y); } if let Ok(size) = window.outer_size() { info!(" - 窗口大小:{}x{}", size.width, size.height); } // 确保窗口可见并获得焦点 info!(" - 显示窗口并聚焦..."); let _ = window.show(); let _ = window.set_focus(); info!(" ✓ 窗口已显示并聚焦"); // 再次检查状态 match window.is_visible() { Ok(true) => info!(" ✓ 窗口确认可见"), Ok(false) => info!(" ⚠ 窗口仍然隐藏"), Err(e) => info!(" ⚠ 可见性检查失败:{}", e), } // 获取前端 URL if let Ok(url) = window.url() { info!(" - 前端 URL: {}", url); } } else { info!(" ⚠ 主窗口未找到,手动创建..."); let window_result = tauri::WebviewWindowBuilder::new( app, "main", tauri::WebviewUrl::App("index.html".into()), ) .title("impress ASR Input") .inner_size(1000.0, 700.0) .min_inner_size(800.0, 600.0) .center() .visible(true) .build(); match window_result { Ok(_window) => { info!(" ✓ 窗口手动创建成功"); } Err(e) => { info!(" ❌ 窗口创建失败:{}", e); warn!("窗口创建失败:{}", e); } } } // 设置系统托盘 info!(" [设置] 配置系统托盘..."); match setup_tray(app) { Ok(_) => info!(" ✓ 系统托盘设置完成"), Err(e) => { info!(" ⚠ 系统托盘设置失败:{}", e); warn!("托盘设置失败:{}", e); } } // 在 setup 结束时检查窗口状态 info!(" [设置] setup 结束前检查窗口..."); let all_windows = app.webview_windows(); info!(" - 当前窗口数量:{}", all_windows.len()); for (label, _) in all_windows.iter() { info!(" - 窗口:label='{}'", label); if let Some(win) = app.get_webview_window(label) { let visible = win.is_visible().unwrap_or(false); let pos = win.outer_position().ok(); let size = win.outer_size().ok(); let url = win.url().ok(); info!(" - 可见性:{}", if visible { "可见" } else { "隐藏" }); if let Some(p) = pos { info!(" - 位置:({}, {})", p.x, p.y); } if let Some(s) = size { info!(" - 大小:{}x{}", s.width, s.height); } if let Some(u) = url { info!(" - URL: {}", u); } } } info!("Tauri 应用设置完成"); Ok(()) }) .on_window_event(|window, event| { // 处理窗口事件 match event { tauri::WindowEvent::CloseRequested { api, .. } => { info!(" [窗口] 关闭请求 - 隐藏窗口到托盘"); // 隐藏窗口而不是关闭 let _ = window.hide(); api.prevent_close(); } tauri::WindowEvent::Focused(focused) => { if *focused { info!(" [窗口] 获得焦点"); } else { info!(" [窗口] 失去焦点"); } } _ => {} } }) .on_page_load(|_window, payload| { info!("[页面加载] URL: {}", payload.url()); match payload.event() { tauri::webview::PageLoadEvent::Started => { info!(" - 页面开始加载"); } tauri::webview::PageLoadEvent::Finished => { info!(" - 页面加载完成 ✓"); } } }) .build(context) .expect("构建 Tauri 应用失败"); eprintln!(" ✓ Tauri 应用构建成功"); info!("Tauri 应用启动成功"); // 立即检查窗口 - 在 run() 之前 info!("========================================"); info!("[窗口] build 后立即检查窗口..."); let windows = app.webview_windows(); info!(" - 窗口数量:{}", windows.len()); for (label, _) in windows.iter() { info!(" - 窗口标签:label='{}'", label); } // 尝试获取主窗口 if let Some(window) = app.get_webview_window("main") { info!("[窗口] 主窗口信息:"); match window.is_visible() { Ok(true) => info!(" - 可见性:可见 ✓"), Ok(false) => info!(" - 可见性:隐藏 ⚠"), Err(e) => info!(" - 可见性:检查失败 ({})", e), } match window.is_minimized() { Ok(true) => info!(" - 最小化:是 ⚠"), Ok(false) => info!(" - 最小化:否 ✓"), Err(e) => info!(" - 最小化:检查失败 ({})", e), } info!(" - 焦点:可设置 ✓"); } else { info!(" ⚠ [窗口] 主窗口 (label='main') 未找到!"); warn!("主窗口未找到"); // 尝试获取任意窗口 if let Some(first_window) = windows.keys().next() { info!(" - 但找到其他窗口:label='{}'", first_window); if let Some(win) = app.get_webview_window(first_window) { let _ = win.show(); let _ = win.set_focus(); info!(" ✓ 已显示该窗口"); } } } eprintln!(); eprintln!(" [运行] 进入事件循环..."); info!("进入应用事件循环"); app.run(|app_handle, event| { // 处理全局事件 match event { tauri::RunEvent::Exit => { info!(" [事件] 应用退出"); } tauri::RunEvent::ExitRequested { api, .. } => { // 检查是否允许完全退出 let state = app_handle.state::(); if state.is_exit_allowed() { info!(" [事件] 退出请求 - 允许完全退出"); } else { info!(" [事件] 退出请求 - 阻止退出(隐藏窗口到托盘)"); api.prevent_exit(); } } tauri::RunEvent::Ready => { info!(" [事件] 应用已就绪"); } _ => {} } }); Ok(()) } /// 设置系统托盘 fn setup_tray(app: &tauri::App) -> Result<()> { info!(" [托盘] 创建菜单项..."); let show = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?; info!(" - '显示窗口' 菜单项已创建"); let record = MenuItem::with_id(app, "record", "开始录音", true, None::<&str>)?; info!(" - '开始录音' 菜单项已创建"); let settings = MenuItem::with_id(app, "settings", "设置", true, None::<&str>)?; info!(" - '设置' 菜单项已创建"); // 主题子菜单 let theme_light = MenuItem::with_id(app, "theme_light", "浅色主题", true, None::<&str>)?; let theme_dark = MenuItem::with_id(app, "theme_dark", "深色主题", true, None::<&str>)?; let theme_system = MenuItem::with_id(app, "theme_system", "跟随系统", true, None::<&str>)?; let theme_submenu = Submenu::with_items(app, "主题", true, &[&theme_light, &theme_dark, &theme_system])?; info!(" - '主题' 子菜单已创建(浅色/深色/跟随系统)"); // 完全退出选项 let quit_now = MenuItem::with_id(app, "quit_now", "完全退出", true, None::<&str>)?; info!(" - '完全退出' 菜单项已创建"); info!(" [托盘] 组合菜单..."); let menu = Menu::with_items(app, &[&show, &record, &settings, &theme_submenu, &quit_now])?; info!(" ✓ 菜单创建成功 (5 项 + 主题子菜单)"); info!(" [托盘] 加载图标..."); let icon = app.default_window_icon().cloned(); match icon { Some(_) => info!(" ✓ 窗口图标加载成功"), None => { info!(" ⚠ 窗口图标未找到,使用默认图标"); warn!("窗口图标未找到"); } } info!(" [托盘] 创建托盘图标..."); let _tray = TrayIconBuilder::new() .icon(app.default_window_icon().cloned().unwrap_or_else(|| { warn!("使用空图标"); // 创建一个 1x1 的透明像素作为默认图标 tauri::image::Image::new_owned(vec![0u8; 4], 1, 1) })) .menu(&menu) .show_menu_on_left_click(false) .on_menu_event(|app, event| { info!("托盘菜单事件:{:?}", event.id); match event.id.as_ref() { "show" => { info!(" [托盘] '显示窗口' 被点击"); if let Some(window) = app.get_webview_window("main") { let _ = window.show(); let _ = window.set_focus(); info!(" ✓ 窗口已显示并聚焦"); } } "record" => { info!(" [托盘] '开始录音' 被点击"); info!("从托盘启动录音"); } "settings" => { info!(" [托盘] '设置' 被点击"); if let Some(window) = app.get_webview_window("main") { let _ = window.show(); let _ = window.set_focus(); info!(" ✓ 窗口已显示并聚焦"); } } "theme_light" => { info!(" [托盘] '浅色主题' 被点击"); let state = app.state::(); state.set_theme(AppTheme::Light); // 通知前端切换主题 if let Some(window) = app.get_webview_window("main") { let _ = window.emit("theme-change", "light"); } } "theme_dark" => { info!(" [托盘] '深色主题' 被点击"); let state = app.state::(); state.set_theme(AppTheme::Dark); if let Some(window) = app.get_webview_window("main") { let _ = window.emit("theme-change", "dark"); } } "theme_system" => { info!(" [托盘] '跟随系统' 被点击"); let state = app.state::(); state.set_theme(AppTheme::System); if let Some(window) = app.get_webview_window("main") { let _ = window.emit("theme-change", "system"); } } "quit_now" => { info!(" [托盘] '完全退出' 被点击"); // 允许完全退出 let state = app.state::(); state.allow_exit(); // 关闭窗口 if let Some(window) = app.get_webview_window("main") { let _ = window.close(); } // 退出应用 app.exit(0); } _ => {} } }) .build(app)?; info!(" ✓ 托盘图标创建成功"); info!("系统托盘设置完成"); Ok(()) }