diff --git a/package.json b/package.json index 2bb4ed2..3efe3e1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@tanstack/react-virtual": "^3.13.18", "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.6.0", + "@tauri-apps/plugin-notification": "^2.3.3", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-updater": "^2.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8d3be0..32370b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@tauri-apps/plugin-dialog': specifier: ^2.6.0 version: 2.6.0 + '@tauri-apps/plugin-notification': + specifier: ^2.3.3 + version: 2.3.3 '@tauri-apps/plugin-opener': specifier: ^2 version: 2.5.3 @@ -2530,6 +2533,9 @@ packages: '@tauri-apps/plugin-dialog@2.6.0': resolution: {integrity: sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==} + '@tauri-apps/plugin-notification@2.3.3': + resolution: {integrity: sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg==} + '@tauri-apps/plugin-opener@2.5.3': resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} @@ -9074,6 +9080,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.10.1 + '@tauri-apps/plugin-notification@2.3.3': + dependencies: + '@tauri-apps/api': 2.10.1 + '@tauri-apps/plugin-opener@2.5.3': dependencies: '@tauri-apps/api': 2.10.1 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ba557ef..be3fcad 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -818,6 +818,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-dialog", + "tauri-plugin-notification", "tauri-plugin-opener", "tauri-plugin-process", "tauri-plugin-updater", @@ -2960,6 +2961,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mac-notification-sys" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a16783dd1a47849b8c8133c9cd3eb2112cfbc6901670af3dba47c8bbfb07d3" +dependencies = [ + "cc", + "objc2", + "objc2-foundation", + "time", +] + [[package]] name = "markup5ever" version = "0.14.1" @@ -3219,6 +3232,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "notify-rust" +version = "4.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21af20a1b50be5ac5861f74af1a863da53a11c38684d9818d82f1c42f7fdc6c2" +dependencies = [ + "futures-lite", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3976,7 +4003,7 @@ checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", "indexmap 2.13.0", - "quick-xml", + "quick-xml 0.38.4", "serde", "time", ] @@ -4192,6 +4219,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.38.4" @@ -4247,6 +4283,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -4267,6 +4313,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -4285,6 +4341,15 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -6153,6 +6218,25 @@ dependencies = [ "url", ] +[[package]] +name = "tauri-plugin-notification" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fc2c5ff41105bd1f7242d8201fdf3efd70749b82fa013a17f2126357d194cc" +dependencies = [ + "log", + "notify-rust", + "rand 0.9.2", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "time", + "url", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.3" @@ -6334,6 +6418,18 @@ dependencies = [ "toml 0.9.11+spec-1.1.0", ] +[[package]] +name = "tauri-winrt-notification" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" +dependencies = [ + "quick-xml 0.37.5", + "thiserror 2.0.18", + "windows 0.61.3", + "windows-version", +] + [[package]] name = "tempfile" version = "3.24.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 24976df..b6d81c7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -55,6 +55,7 @@ keyring = { version = "3", features = ["apple-native", "windows-native", "sync-s tauri-plugin-window-state = "2" tauri-plugin-updater = "2" tauri-plugin-process = "2" +tauri-plugin-notification = "2" [target.'cfg(target_os = "windows")'.dependencies] diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index bddbbb4..9381994 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -22,6 +22,7 @@ }, "dialog:default", "updater:default", - "process:default" + "process:default", + "notification:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1f1de6b..6d4ca5f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -40,6 +40,7 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_process::init()) + .plugin(tauri_plugin_notification::init()) .manage(ConnectionManager::new()) .manage(TerminalManager::new()) .manage(windows::SettingsWindowState::new()) diff --git a/src/contexts/acp-connections-context.tsx b/src/contexts/acp-connections-context.tsx index 06fb1d1..8be3606 100644 --- a/src/contexts/acp-connections-context.tsx +++ b/src/contexts/acp-connections-context.tsx @@ -42,6 +42,7 @@ import { CONNECTION_IDLE_TIMEOUT_MS, IDLE_SWEEP_INTERVAL_MS, } from "@/lib/constants" +import { notifyTurnComplete } from "@/lib/notification" import { useAlertContext, type AlertAction } from "@/contexts/alert-context" // ── Shared types (re-exported for consumers) ── @@ -1545,6 +1546,17 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) { } } } + // Send OS notification when window is not focused + { + const nc = storeRef.current.connections.get(contextKey) + if (nc) { + const agentLabel = AGENT_LABELS[nc.agentType] + notifyTurnComplete( + "Codeg", + t("notificationTurnComplete", { agent: agentLabel }), + ).catch(() => {}) + } + } break } case "error": diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 2ed1c1b..bd95f1e 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "فشل فحص ما قبل التشغيل للربط التلقائي: {message}", "connectFailedTitle": "فشل اتصال {agent}", "toolFallbackTitle": "أداة", - "eventErrorTitle": "خطأ الوكيل" + "eventErrorTitle": "خطأ الوكيل", + "notificationTurnComplete": "{agent} أنهى الاستجابة" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 0deef4e..33b7820 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "Auto-Link-Preflight fehlgeschlagen: {message}", "connectFailedTitle": "{agent} Verbindung fehlgeschlagen", "toolFallbackTitle": "Werkzeug", - "eventErrorTitle": "Agentenfehler" + "eventErrorTitle": "Agentenfehler", + "notificationTurnComplete": "{agent} hat die Antwort abgeschlossen" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index edf8090..81eb4d1 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "Auto-link preflight failed: {message}", "connectFailedTitle": "{agent} connection failed", "toolFallbackTitle": "Tool", - "eventErrorTitle": "Agent Error" + "eventErrorTitle": "Agent Error", + "notificationTurnComplete": "{agent} has finished responding" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index d58d399..36c8470 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "Falló la verificación previa del autovínculo: {message}", "connectFailedTitle": "Falló la conexión de {agent}", "toolFallbackTitle": "Herramienta", - "eventErrorTitle": "Error del agente" + "eventErrorTitle": "Error del agente", + "notificationTurnComplete": "{agent} ha terminado de responder" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index e461549..38b130e 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "Échec de la vérification préalable de l'auto-liaison : {message}", "connectFailedTitle": "Échec de la connexion de {agent}", "toolFallbackTitle": "Outil", - "eventErrorTitle": "Erreur de l'agent" + "eventErrorTitle": "Erreur de l'agent", + "notificationTurnComplete": "{agent} a terminé de répondre" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index ecaf7a7..a6f58e7 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "自動リンクの事前チェックに失敗しました: {message}", "connectFailedTitle": "{agent} の接続に失敗しました", "toolFallbackTitle": "ツール", - "eventErrorTitle": "エージェントエラー" + "eventErrorTitle": "エージェントエラー", + "notificationTurnComplete": "{agent} の応答が完了しました" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 979127b..ddd26b3 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "자동 연결 사전 점검 실패: {message}", "connectFailedTitle": "{agent} 연결 실패", "toolFallbackTitle": "도구", - "eventErrorTitle": "에이전트 오류" + "eventErrorTitle": "에이전트 오류", + "notificationTurnComplete": "{agent} 응답이 완료되었습니다" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 52d65ad..d06b1a2 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "Pré-voo do vínculo automático falhou: {message}", "connectFailedTitle": "Falha na conexão de {agent}", "toolFallbackTitle": "Ferramenta", - "eventErrorTitle": "Erro do agente" + "eventErrorTitle": "Erro do agente", + "notificationTurnComplete": "{agent} terminou de responder" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 32488a1..0c707ec 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "自动链接预检查失败:{message}", "connectFailedTitle": "{agent} 连接失败", "toolFallbackTitle": "工具", - "eventErrorTitle": "Agent 错误" + "eventErrorTitle": "Agent 错误", + "notificationTurnComplete": "{agent} 已完成响应" }, "connectionLifecycle": { "tasks": { diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index fa92f31..acd5468 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -1311,7 +1311,8 @@ "autoLinkPreflightFailed": "自動連結預檢查失敗:{message}", "connectFailedTitle": "{agent} 連線失敗", "toolFallbackTitle": "工具", - "eventErrorTitle": "Agent 錯誤" + "eventErrorTitle": "Agent 錯誤", + "notificationTurnComplete": "{agent} 已完成回應" }, "connectionLifecycle": { "tasks": {