-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Add system notifications #6971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add system notifications #6971
Changes from all commits
75cd720
a3bffcd
26c6ffb
5926f20
faf1d1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,110 @@ | ||||||||||||||||
| import { execa } from "execa" | ||||||||||||||||
| import { platform } from "os" | ||||||||||||||||
| import * as vscode from "vscode" | ||||||||||||||||
|
|
||||||||||||||||
| interface NotificationOptions { | ||||||||||||||||
| title?: string | ||||||||||||||||
| subtitle?: string | ||||||||||||||||
| message: string | ||||||||||||||||
| force?: boolean // Force notification even if the window is focused | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async function showMacOSNotification(options: NotificationOptions): Promise<void> { | ||||||||||||||||
| const { title, subtitle = "", message } = options | ||||||||||||||||
|
|
||||||||||||||||
| const script = `display notification "${message}" with title "${title}" subtitle "${subtitle}" sound name "Tink"` | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| await execa("osascript", ["-e", script]) | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| throw new Error(`Failed to show macOS notification: ${error}`) | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async function showWindowsNotification(options: NotificationOptions): Promise<void> { | ||||||||||||||||
| const { subtitle, message } = options | ||||||||||||||||
|
|
||||||||||||||||
| const script = ` | ||||||||||||||||
| [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null | ||||||||||||||||
| [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null | ||||||||||||||||
|
|
||||||||||||||||
| $template = @" | ||||||||||||||||
| <toast> | ||||||||||||||||
| <visual> | ||||||||||||||||
| <binding template="ToastText02"> | ||||||||||||||||
| <text id="1">${subtitle}</text> | ||||||||||||||||
| <text id="2">${message}</text> | ||||||||||||||||
| </binding> | ||||||||||||||||
| </visual> | ||||||||||||||||
| </toast> | ||||||||||||||||
| "@ | ||||||||||||||||
|
|
||||||||||||||||
| $xml = New-Object Windows.Data.Xml.Dom.XmlDocument | ||||||||||||||||
| $xml.LoadXml($template) | ||||||||||||||||
| $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) | ||||||||||||||||
| [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Roo Code").Show($toast) | ||||||||||||||||
|
||||||||||||||||
| ` | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| await execa("powershell", ["-Command", script]) | ||||||||||||||||
| } catch (error) { | ||||||||||||||||
| throw new Error(`Failed to show Windows notification: ${error}`) | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| async function showLinuxNotification(options: NotificationOptions): Promise<void> { | ||||||||||||||||
| const { title = "", subtitle = "", message } = options | ||||||||||||||||
|
|
||||||||||||||||
| // Combine subtitle and message if subtitle exists | ||||||||||||||||
| const fullMessage = subtitle ? `${subtitle}\n${message}` : message | ||||||||||||||||
|
|
||||||||||||||||
| try { | ||||||||||||||||
| await execa("notify-send", [title, fullMessage]) | ||||||||||||||||
| } catch (error: any) { | ||||||||||||||||
| if (error.code === "ENOENT") { | ||||||||||||||||
| throw new Error( | ||||||||||||||||
| "notify-send is not installed. Please install libnotify-bin (apt install libnotify-bin on Debian/Ubuntu)", | ||||||||||||||||
| ) | ||||||||||||||||
| } | ||||||||||||||||
| throw new Error(`Failed to show Linux notification: ${error}`) | ||||||||||||||||
|
||||||||||||||||
| throw new Error(`Failed to show Linux notification: ${error}`) | |
| } catch (error: any) { | |
| if (error.code === 'ENOENT') { | |
| throw new Error('notify-send is not installed. Please install libnotify-bin (apt install libnotify-bin on Debian/Ubuntu)') | |
| } | |
| throw new Error(`Failed to show Linux notification: ${error}`) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling feedback to users. When notifications fail, users have no way to know. Consider implementing a fallback mechanism:
| console.error("Could not show system notification", error) | |
| } catch (error) { | |
| console.error("Could not show system notification", error); | |
| // Fallback to VSCode's built-in notification | |
| vscode.window.showInformationMessage(options.message); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,7 @@ import RooCloudCTA from "@src/components/welcome/RooCloudCTA" | |
| import { StandardTooltip } from "@src/components/ui" | ||
| import { useAutoApprovalState } from "@src/hooks/useAutoApprovalState" | ||
| import { useAutoApprovalToggles } from "@src/hooks/useAutoApprovalToggles" | ||
| import { showSystemNotification } from "@src/utils/showSystemNotification" | ||
|
|
||
| import TelemetryBanner from "../common/TelemetryBanner" | ||
| import VersionIndicator from "../common/VersionIndicator" | ||
|
|
@@ -56,7 +57,6 @@ import SystemPromptWarning from "./SystemPromptWarning" | |
| import ProfileViolationWarning from "./ProfileViolationWarning" | ||
| import { CheckpointWarning } from "./CheckpointWarning" | ||
| import { QueuedMessages } from "./QueuedMessages" | ||
|
|
||
| export interface ChatViewProps { | ||
| isHidden: boolean | ||
| showAnnouncement: boolean | ||
|
|
@@ -280,6 +280,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| switch (lastMessage.ask) { | ||
| case "api_req_failed": | ||
| playSound("progress_loop") | ||
| showSystemNotification(t("settings:notifications.system.apiReqFailed")) | ||
|
||
| setSendingDisabled(true) | ||
| setClineAsk("api_req_failed") | ||
| setEnableButtons(true) | ||
|
|
@@ -288,6 +289,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| break | ||
| case "mistake_limit_reached": | ||
| playSound("progress_loop") | ||
| showSystemNotification(t("settings:notifications.system.mistakeLimitReached")) | ||
| setSendingDisabled(false) | ||
| setClineAsk("mistake_limit_reached") | ||
| setEnableButtons(true) | ||
|
|
@@ -297,6 +299,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| case "followup": | ||
| if (!isPartial) { | ||
| playSound("notification") | ||
| showSystemNotification(t("settings:notifications.system.followup")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("followup") | ||
|
|
@@ -311,6 +314,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| case "tool": | ||
| if (!isAutoApproved(lastMessage) && !isPartial) { | ||
| playSound("notification") | ||
| showSystemNotification(t("settings:notifications.system.toolRequest")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("tool") | ||
|
|
@@ -347,6 +351,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| case "browser_action_launch": | ||
| if (!isAutoApproved(lastMessage) && !isPartial) { | ||
| playSound("notification") | ||
| showSystemNotification(t("settings:notifications.system.browserAction")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("browser_action_launch") | ||
|
|
@@ -357,6 +362,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| case "command": | ||
| if (!isAutoApproved(lastMessage) && !isPartial) { | ||
| playSound("notification") | ||
| showSystemNotification(t("settings:notifications.system.command")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("command") | ||
|
|
@@ -374,6 +380,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| case "use_mcp_server": | ||
| if (!isAutoApproved(lastMessage) && !isPartial) { | ||
| playSound("notification") | ||
| showSystemNotification(t("settings:notifications.system.useMcpServer")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("use_mcp_server") | ||
|
|
@@ -385,6 +392,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro | |
| // extension waiting for feedback. but we can just present a new task button | ||
| if (!isPartial) { | ||
| playSound("celebration") | ||
| showSystemNotification(t("settings:notifications.system.completionResult")) | ||
| } | ||
| setSendingDisabled(isPartial) | ||
| setClineAsk("completion_result") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NotificationOptions interface is quite basic. Based on the issue requirements, shouldn't it support priority levels and action buttons?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not supported yet