From 22ba91e0dc3df0eb78b3addd537ee2d6135ac1bf Mon Sep 17 00:00:00 2001 From: xCss <10877162+xCss@users.noreply.github.com> Date: Sun, 2 Aug 2026 22:06:16 +0800 Subject: [PATCH] feat: browser notification when agent session ends Closes #339. Ref discussion #317. When the tab is in the background, fires a Web Notifications API popup on agent completion. The notification title uses the current session name (falls back to i18n "Session complete"); the body is the i18n "Task finished." string, so both en and zh-CN are covered. Permission is requested lazily on the first background completion; denied or unsupported browsers are silently skipped. --- components/AppShell.tsx | 16 +++++++++++++++- lib/i18n/messages/en.ts | 2 ++ lib/i18n/messages/zh-CN.ts | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/components/AppShell.tsx b/components/AppShell.tsx index d4d827fa7..f31c2e705 100644 --- a/components/AppShell.tsx +++ b/components/AppShell.tsx @@ -413,7 +413,21 @@ export function AppShell() { const handleAgentEnd = useCallback(() => { setRefreshKey((k) => k + 1); setExplorerRefreshKey((k) => k + 1); - }, []); + + if (document.visibilityState === "visible") return; + if (!("Notification" in window)) return; + + const fire = () => { + const title = selectedSession?.name ?? translate("i18n.sessionComplete"); + new Notification(title, { body: translate("i18n.taskFinished") }); + }; + + if (Notification.permission === "granted") { + fire(); + } else if (Notification.permission === "default") { + void Notification.requestPermission().then((p) => { if (p === "granted") fire(); }); + } + }, [selectedSession, translate]); const handleAutoName = useCallback(async () => { const sessionId = selectedSession?.id; diff --git a/lib/i18n/messages/en.ts b/lib/i18n/messages/en.ts index 156cb961c..c9508af18 100644 --- a/lib/i18n/messages/en.ts +++ b/lib/i18n/messages/en.ts @@ -433,5 +433,7 @@ export const enLocale: LocalePlugin = { "i18n.thinkingUnavailable": "Thinking content unavailable", "i18n.before": "Before", "i18n.after": "After", + "i18n.sessionComplete": "Session complete", + "i18n.taskFinished": "Task finished.", }, }; diff --git a/lib/i18n/messages/zh-CN.ts b/lib/i18n/messages/zh-CN.ts index 7854f8142..745272ad4 100644 --- a/lib/i18n/messages/zh-CN.ts +++ b/lib/i18n/messages/zh-CN.ts @@ -433,5 +433,7 @@ export const zhCNLocale: LocalePlugin = { "i18n.thinkingUnavailable": "思考内容不可用", "i18n.before": "之前", "i18n.after": "之后", + "i18n.sessionComplete": "任务完成", + "i18n.taskFinished": "任务已完成。", }, };