From 75cd720c1f0a25fede772ee55122cc38faffd6e6 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Thu, 4 Sep 2025 14:18:21 +0800 Subject: [PATCH 1/5] system notification like kilocode --- packages/types/src/global-settings.ts | 2 + src/core/webview/ClineProvider.ts | 3 + src/core/webview/webviewMessageHandler.ts | 15 +++ src/integrations/notifications/index.ts | 97 +++++++++++++++++++ src/shared/ExtensionMessage.ts | 2 + src/shared/WebviewMessage.ts | 7 ++ webview-ui/src/components/chat/ChatView.tsx | 10 +- .../settings/NotificationSettings.tsx | 41 +++++++- .../src/components/settings/SettingsView.tsx | 4 + .../src/context/ExtensionStateContext.tsx | 3 + webview-ui/src/i18n/locales/ca/settings.json | 15 +++ webview-ui/src/i18n/locales/de/settings.json | 15 +++ webview-ui/src/i18n/locales/en/settings.json | 15 +++ webview-ui/src/i18n/locales/es/settings.json | 15 +++ webview-ui/src/i18n/locales/fr/settings.json | 15 +++ webview-ui/src/i18n/locales/hi/settings.json | 15 +++ webview-ui/src/i18n/locales/id/settings.json | 15 +++ webview-ui/src/i18n/locales/it/settings.json | 15 +++ webview-ui/src/i18n/locales/ja/settings.json | 15 +++ webview-ui/src/i18n/locales/ko/settings.json | 15 +++ webview-ui/src/i18n/locales/nl/settings.json | 15 +++ webview-ui/src/i18n/locales/pl/settings.json | 15 +++ .../src/i18n/locales/pt-BR/settings.json | 15 +++ webview-ui/src/i18n/locales/ru/settings.json | 15 +++ webview-ui/src/i18n/locales/tr/settings.json | 15 +++ webview-ui/src/i18n/locales/vi/settings.json | 15 +++ .../src/i18n/locales/zh-CN/settings.json | 15 +++ .../src/i18n/locales/zh-TW/settings.json | 15 +++ .../src/utils/showSystemNotification.ts | 11 +++ 29 files changed, 462 insertions(+), 3 deletions(-) create mode 100644 src/integrations/notifications/index.ts create mode 100644 webview-ui/src/utils/showSystemNotification.ts diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 81c6ae6dfe..d84ab64a60 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -101,6 +101,7 @@ export const globalSettingsSchema = z.object({ ttsSpeed: z.number().optional(), soundEnabled: z.boolean().optional(), soundVolume: z.number().optional(), + systemNotificationsEnabled: z.boolean().optional(), maxOpenTabsContext: z.number().optional(), maxWorkspaceFiles: z.number().optional(), @@ -281,6 +282,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { ttsSpeed: 1, soundEnabled: false, soundVolume: 0.5, + systemNotificationsEnabled: false, terminalOutputLineLimit: 500, terminalOutputCharacterLimit: DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 544922a187..18f83f204c 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1579,6 +1579,7 @@ export class ClineProvider soundEnabled, ttsEnabled, ttsSpeed, + systemNotificationsEnabled, diffEnabled, enableCheckpoints, taskHistory, @@ -1689,6 +1690,7 @@ export class ClineProvider soundEnabled: soundEnabled ?? false, ttsEnabled: ttsEnabled ?? false, ttsSpeed: ttsSpeed ?? 1.0, + systemNotificationsEnabled: systemNotificationsEnabled ?? false, diffEnabled: diffEnabled ?? true, enableCheckpoints: enableCheckpoints ?? true, shouldShowAnnouncement: @@ -1897,6 +1899,7 @@ export class ClineProvider soundEnabled: stateValues.soundEnabled ?? false, ttsEnabled: stateValues.ttsEnabled ?? false, ttsSpeed: stateValues.ttsSpeed ?? 1.0, + systemNotificationsEnabled: stateValues.systemNotificationsEnabled ?? false, diffEnabled: stateValues.diffEnabled ?? true, enableCheckpoints: stateValues.enableCheckpoints ?? true, soundVolume: stateValues.soundVolume, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index a495489cc1..822e06c582 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -33,6 +33,7 @@ import { checkExistKey } from "../../shared/checkExistApiConfig" import { experimentDefault } from "../../shared/experiments" import { Terminal } from "../../integrations/terminal/Terminal" import { openFile } from "../../integrations/misc/open-file" +import { showSystemNotification } from "../../integrations/notifications" import { openImage, saveImage } from "../../integrations/misc/image-handler" import { selectImages } from "../../integrations/misc/process-images" import { getTheme } from "../../integrations/theme/getTheme" @@ -992,6 +993,20 @@ export const webviewMessageHandler = async ( await updateGlobalState("soundVolume", soundVolume) await provider.postStateToWebview() break + case "showSystemNotification": + const isSystemNotificationsEnabled = getGlobalState("systemNotificationsEnabled") ?? false + if (!isSystemNotificationsEnabled) { + break + } + if (message.notificationOptions) { + showSystemNotification(message.notificationOptions) + } + break + case "systemNotificationsEnabled": + const systemNotificationsEnabled = message.bool ?? true + await updateGlobalState("systemNotificationsEnabled", systemNotificationsEnabled) + await provider.postStateToWebview() + break case "ttsEnabled": const ttsEnabled = message.bool ?? true await updateGlobalState("ttsEnabled", ttsEnabled) diff --git a/src/integrations/notifications/index.ts b/src/integrations/notifications/index.ts new file mode 100644 index 0000000000..8a26c1bf10 --- /dev/null +++ b/src/integrations/notifications/index.ts @@ -0,0 +1,97 @@ +import { execa } from "execa" +import { platform } from "os" + +interface NotificationOptions { + title?: string + subtitle?: string + message: string +} + +async function showMacOSNotification(options: NotificationOptions): Promise { + 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 { + 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 = @" + + + + ${subtitle} + ${message} + + + +"@ + + $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 { + 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) { + throw new Error(`Failed to show Linux notification: ${error}`) + } +} + +export async function showSystemNotification(options: NotificationOptions): Promise { + try { + const { title = "Roo Code", message } = options + + if (!message) { + throw new Error("Message is required") + } + + const escapedOptions = { + ...options, + title: title.replace(/"/g, '\\"'), + message: message.replace(/"/g, '\\"'), + subtitle: options.subtitle?.replace(/"/g, '\\"') || "", + } + + switch (platform()) { + case "darwin": + await showMacOSNotification(escapedOptions) + break + case "win32": + await showWindowsNotification(escapedOptions) + break + case "linux": + await showLinuxNotification(escapedOptions) + break + default: + throw new Error("Unsupported platform") + } + } catch (error) { + console.error("Could not show system notification", error) + } +} diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index d4caf2f674..e803008ff8 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -99,6 +99,7 @@ export interface ExtensionMessage { | "remoteBrowserEnabled" | "ttsStart" | "ttsStop" + | "showSystemNotification" | "maxReadFileLine" | "fileSearchResults" | "toggleApiConfigPin" @@ -240,6 +241,7 @@ export type ExtensionState = Pick< | "ttsSpeed" | "soundEnabled" | "soundVolume" + | "systemNotificationsEnabled" // | "maxOpenTabsContext" // Optional in GlobalSettings, required here. // | "maxWorkspaceFiles" // Optional in GlobalSettings, required here. // | "showRooIgnoredFiles" // Optional in GlobalSettings, required here. diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 1202f48a21..a29621f614 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -97,6 +97,8 @@ export interface WebviewMessage { | "ttsEnabled" | "ttsSpeed" | "soundVolume" + | "showSystemNotification" + | "systemNotificationsEnabled" | "diffEnabled" | "enableCheckpoints" | "browserViewportSize" @@ -291,6 +293,11 @@ export interface WebviewMessage { codebaseIndexMistralApiKey?: string codebaseIndexVercelAiGatewayApiKey?: string } + notificationOptions?: { + title?: string + subtitle?: string + message: string + } } export const checkoutDiffPayloadSchema = z.object({ diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index b6523e4399..8717d9559a 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -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 & { ttsEnabled?: boolean ttsSpeed?: number soundEnabled?: boolean soundVolume?: number - setCachedStateField: SetCachedStateField<"ttsEnabled" | "ttsSpeed" | "soundEnabled" | "soundVolume"> + systemNotificationsEnabled?: boolean + setCachedStateField: SetCachedStateField< + "ttsEnabled" | "ttsSpeed" | "soundEnabled" | "soundVolume" | "systemNotificationsEnabled" + > + isChangeDetected: boolean } export const NotificationSettings = ({ @@ -21,10 +26,22 @@ export const NotificationSettings = ({ ttsSpeed, soundEnabled, soundVolume, + systemNotificationsEnabled, + isChangeDetected, setCachedStateField, ...props }: NotificationSettingsProps) => { const { t } = useAppTranslation() + + const onTestNotificationClick = () => { + vscode.postMessage({ + type: "showSystemNotification", + notificationOptions: { + title: t("settings.notifications.system.testTitle"), + message: t("settings.notifications.system.testMessage"), + }, + }) + } return (
@@ -35,6 +52,26 @@ export const NotificationSettings = ({
+
+ setCachedStateField("systemNotificationsEnabled", e.target.checked)} + data-testid="system-notifications-enabled-checkbox"> + {t("settings:notifications.system.label")} + +
+ {t("settings:notifications.system.description")} +
+ {systemNotificationsEnabled && !isChangeDetected && ( + + {t("settings:notifications.system.testButton")} + + )} +
+
(({ onDone, t ttsEnabled, ttsSpeed, soundVolume, + systemNotificationsEnabled, telemetrySetting, terminalOutputLineLimit, terminalOutputCharacterLimit, @@ -319,6 +320,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "ttsEnabled", bool: ttsEnabled }) vscode.postMessage({ type: "ttsSpeed", value: ttsSpeed }) vscode.postMessage({ type: "soundVolume", value: soundVolume }) + vscode.postMessage({ type: "systemNotificationsEnabled", bool: systemNotificationsEnabled }) vscode.postMessage({ type: "diffEnabled", bool: diffEnabled }) vscode.postMessage({ type: "enableCheckpoints", bool: enableCheckpoints }) vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize }) @@ -695,7 +697,9 @@ const SettingsView = forwardRef(({ onDone, t ttsSpeed={ttsSpeed} soundEnabled={soundEnabled} soundVolume={soundVolume} + systemNotificationsEnabled={systemNotificationsEnabled} setCachedStateField={setCachedStateField} + isChangeDetected={isChangeDetected} /> )} diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 2f4af84f58..382429a23b 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -84,6 +84,7 @@ export interface ExtensionStateContextType extends ExtensionState { setTerminalZdotdir: (value: boolean) => void setTtsEnabled: (value: boolean) => void setTtsSpeed: (value: number) => void + setSystemNotificationsEnabled: (value: boolean) => void setDiffEnabled: (value: boolean) => void setEnableCheckpoints: (value: boolean) => void setBrowserViewportSize: (value: string) => void @@ -444,6 +445,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })), setTtsEnabled: (value) => setState((prevState) => ({ ...prevState, ttsEnabled: value })), setTtsSpeed: (value) => setState((prevState) => ({ ...prevState, ttsSpeed: value })), + setSystemNotificationsEnabled: (value) => + setState((prevState) => ({ ...prevState, systemNotificationsEnabled: value })), setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })), setEnableCheckpoints: (value) => setState((prevState) => ({ ...prevState, enableCheckpoints: value })), setBrowserViewportSize: (value: string) => diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 52d1ce9e45..c367ae75df 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -517,6 +517,21 @@ "label": "Habilitar text a veu", "description": "Quan està habilitat, Roo llegirà en veu alta les seves respostes utilitzant text a veu.", "speedLabel": "Velocitat" + }, + "system": { + "label": "Habilitar notificacions del sistema", + "description": "Quan està habilitat, Roo mostrarà notificacions del sistema per a esdeveniments i missatges importants.", + "testButton": "Provar notificació del sistema", + "testTitle": "Roo Code", + "testMessage": "Les notificacions del sistema funcionen correctament!", + "apiReqFailed": "Error de sol·licitud d'API", + "mistakeLimitReached": "Límit d'errors assolit", + "followup": "Esperant la teva resposta de seguiment", + "toolRequest": "Sol·licitud d'eina esperant aprovació", + "browserAction": "Acció del navegador esperant aprovació", + "command": "Ordre esperant aprovació", + "useMcpServer": "Utilitzar servidor MCP per a l'aprovació", + "completionResult": "Tasca completada" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 870e587ac9..7e13d165d0 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -517,6 +517,21 @@ "label": "Text-zu-Sprache aktivieren", "description": "Wenn aktiviert, liest Roo seine Antworten mit Text-zu-Sprache laut vor.", "speedLabel": "Geschwindigkeit" + }, + "system": { + "label": "Systembenachrichtigungen aktivieren", + "description": "Wenn aktiviert, zeigt Roo Systembenachrichtigungen für wichtige Ereignisse und Nachrichten an.", + "testButton": "Systembenachrichtigung testen", + "testTitle": "Roo Code", + "testMessage": "Systembenachrichtigungen funktionieren ordnungsgemäß!", + "apiReqFailed": "API-Anfragefehler", + "mistakeLimitReached": "Fehlerlimit erreicht", + "followup": "Warte auf deine Antwort", + "toolRequest": "Tool-Anfrage wartet auf Genehmigung", + "browserAction": "Browser-Aktion wartet auf Genehmigung", + "command": "Befehl wartet auf Genehmigung", + "useMcpServer": "MCP-Server für Genehmigung verwenden", + "completionResult": "Aufgabe abgeschlossen" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 8479be7793..ba141c7194 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -516,6 +516,21 @@ "label": "Enable text-to-speech", "description": "When enabled, Roo will read aloud its responses using text-to-speech.", "speedLabel": "Speed" + }, + "system": { + "label": "Enable system notifications", + "description": "When enabled, Roo will show system notifications for important events and messages.", + "testButton": "Test System Notification", + "testTitle": "Roo Code", + "testMessage": "System notifications are working properly!", + "apiReqFailed": "API request error", + "mistakeLimitReached": "Mistake limit reached", + "followup": "Waiting for your follow-up reply", + "toolRequest": "Tool request waiting for approval", + "browserAction": "Browser action waiting for approval", + "command": "Command waiting for approval", + "useMcpServer": "Use MCP server for approval", + "completionResult": "Task Complete" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index cb4c571cb4..195b6b022a 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -517,6 +517,21 @@ "label": "Habilitar texto a voz", "description": "Cuando está habilitado, Roo leerá en voz alta sus respuestas usando texto a voz.", "speedLabel": "Velocidad" + }, + "system": { + "label": "Habilitar notificaciones del sistema", + "description": "Cuando está habilitado, Roo mostrará notificaciones del sistema para eventos y mensajes importantes.", + "testButton": "Probar notificación del sistema", + "testTitle": "Roo Code", + "testMessage": "¡Las notificaciones del sistema funcionan correctamente!", + "apiReqFailed": "Error de solicitud de API", + "mistakeLimitReached": "Límite de errores alcanzado", + "followup": "Esperando tu respuesta de seguimiento", + "toolRequest": "Solicitud de herramienta esperando aprobación", + "browserAction": "Acción del navegador esperando aprobación", + "command": "Comando esperando aprobación", + "useMcpServer": "Usar servidor MCP para aprobación", + "completionResult": "Tarea completada" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 3b9f7cab5b..d8a694df96 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -517,6 +517,21 @@ "label": "Activer la synthèse vocale", "description": "Lorsque cette option est activée, Roo lira ses réponses à haute voix en utilisant la synthèse vocale.", "speedLabel": "Vitesse" + }, + "system": { + "label": "Activer les notifications système", + "description": "Lorsque cette option est activée, Roo affichera des notifications système pour les événements et messages importants.", + "testButton": "Tester la notification système", + "testTitle": "Roo Code", + "testMessage": "Les notifications système fonctionnent correctement !", + "apiReqFailed": "Erreur de requête API", + "mistakeLimitReached": "Limite d'erreurs atteinte", + "followup": "En attente de ta réponse de suivi", + "toolRequest": "Demande d'outil en attente d'approbation", + "browserAction": "Action du navigateur en attente d'approbation", + "command": "Commande en attente d'approbation", + "useMcpServer": "Utiliser le serveur MCP pour l'approbation", + "completionResult": "Tâche terminée" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index b2715c5df4..5f0a870979 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -517,6 +517,21 @@ "label": "टेक्स्ट-टू-स्पीच सक्षम करें", "description": "जब सक्षम होता है, तो Roo टेक्स्ट-टू-स्पीच का उपयोग करके अपनी प्रतिक्रियाओं को बोलकर पढ़ेगा।", "speedLabel": "गति" + }, + "system": { + "label": "सिस्टम सूचनाएं सक्षम करें", + "description": "जब सक्षम होता है, तो Roo महत्वपूर्ण घटनाओं और संदेशों के लिए सिस्टम सूचनाएं दिखाएगा।", + "testButton": "सिस्टम सूचना परीक्षण करें", + "testTitle": "Roo Code", + "testMessage": "सिस्टम सूचनाएं ठीक से काम कर रही हैं!", + "apiReqFailed": "API अनुरोध त्रुटि", + "mistakeLimitReached": "त्रुटि सीमा पहुंच गई", + "followup": "आपके फॉलो-अप उत्तर की प्रतीक्षा कर रहे हैं", + "toolRequest": "टूल अनुरोध अनुमोदन की प्रतीक्षा में", + "browserAction": "ब्राउज़र कार्य अनुमोदन की प्रतीक्षा में", + "command": "कमांड अनुमोदन की प्रतीक्षा में", + "useMcpServer": "अनुमोदन के लिए MCP सर्वर का उपयोग करें", + "completionResult": "कार्य पूर्ण" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 35b558ce3d..c35703fbb8 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -521,6 +521,21 @@ "label": "Aktifkan text-to-speech", "description": "Ketika diaktifkan, Roo akan membacakan responnya menggunakan text-to-speech.", "speedLabel": "Kecepatan" + }, + "system": { + "label": "Aktifkan notifikasi sistem", + "description": "Ketika diaktifkan, Roo akan menampilkan notifikasi sistem untuk event dan pesan penting.", + "testButton": "Tes notifikasi sistem", + "testTitle": "Roo Code", + "testMessage": "Notifikasi sistem berfungsi dengan baik!", + "apiReqFailed": "Kesalahan permintaan API", + "mistakeLimitReached": "Batas kesalahan tercapai", + "followup": "Menunggu balasan lanjutan Anda", + "toolRequest": "Permintaan tool menunggu persetujuan", + "browserAction": "Aksi browser menunggu persetujuan", + "command": "Perintah menunggu persetujuan", + "useMcpServer": "Gunakan server MCP untuk persetujuan", + "completionResult": "Tugas selesai" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index e430d9f6a8..109330e3b9 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -517,6 +517,21 @@ "label": "Abilita sintesi vocale", "description": "Quando abilitato, Roo leggerà ad alta voce le sue risposte utilizzando la sintesi vocale.", "speedLabel": "Velocità" + }, + "system": { + "label": "Abilita notifiche di sistema", + "description": "Quando abilitato, Roo mostrerà notifiche di sistema per eventi e messaggi importanti.", + "testButton": "Testa notifica di sistema", + "testTitle": "Roo Code", + "testMessage": "Le notifiche di sistema funzionano correttamente!", + "apiReqFailed": "Errore richiesta API", + "mistakeLimitReached": "Limite errori raggiunto", + "followup": "In attesa della tua risposta di follow-up", + "toolRequest": "Richiesta strumento in attesa di approvazione", + "browserAction": "Azione del browser in attesa di approvazione", + "command": "Comando in attesa di approvazione", + "useMcpServer": "Usa server MCP per approvazione", + "completionResult": "Attività completata" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 185abf598a..fe9c7f24e9 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -517,6 +517,21 @@ "label": "音声合成を有効化", "description": "有効にすると、Rooは音声合成を使用して応答を音声で読み上げます。", "speedLabel": "速度" + }, + "system": { + "label": "システム通知を有効化", + "description": "有効にすると、Rooは重要なイベントやメッセージのためにシステム通知を表示します。", + "testButton": "システム通知をテスト", + "testTitle": "Roo Code", + "testMessage": "システム通知が正常に動作しています!", + "apiReqFailed": "APIリクエストエラー", + "mistakeLimitReached": "エラー制限に達しました", + "followup": "フォローアップの返信をお待ちしています", + "toolRequest": "ツールリクエストが承認待ちです", + "browserAction": "ブラウザアクションが承認待ちです", + "command": "コマンドが承認待ちです", + "useMcpServer": "承認にMCPサーバーを使用", + "completionResult": "タスク完了" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 794896e464..b33ae34021 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -517,6 +517,21 @@ "label": "음성 합성 활성화", "description": "활성화되면 Roo는 음성 합성을 사용하여 응답을 소리내어 읽습니다.", "speedLabel": "속도" + }, + "system": { + "label": "시스템 알림 활성화", + "description": "활성화되면 Roo는 중요한 이벤트와 메시지에 대한 시스템 알림을 표시합니다.", + "testButton": "시스템 알림 테스트", + "testTitle": "Roo Code", + "testMessage": "시스템 알림이 정상적으로 작동합니다!", + "apiReqFailed": "API 요청 오류", + "mistakeLimitReached": "오류 한도 도달", + "followup": "후속 답변을 기다리고 있습니다", + "toolRequest": "도구 요청이 승인 대기 중입니다", + "browserAction": "브라우저 작업이 승인 대기 중입니다", + "command": "명령이 승인 대기 중입니다", + "useMcpServer": "승인에 MCP 서버 사용", + "completionResult": "작업 완료" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index f77717b38e..57ff49f997 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -517,6 +517,21 @@ "label": "Tekst-naar-spraak inschakelen", "description": "Indien ingeschakeld, leest Roo zijn antwoorden hardop voor via tekst-naar-spraak.", "speedLabel": "Snelheid" + }, + "system": { + "label": "Systeemmeldingen inschakelen", + "description": "Indien ingeschakeld, toont Roo systeemmeldingen voor belangrijke gebeurtenissen en berichten.", + "testButton": "Systeemmelding testen", + "testTitle": "Roo Code", + "testMessage": "Systeemmeldingen werken correct!", + "apiReqFailed": "API-verzoekfout", + "mistakeLimitReached": "Foutlimiet bereikt", + "followup": "Wachten op je vervolgantwoord", + "toolRequest": "Toolverzoek wacht op goedkeuring", + "browserAction": "Browseractie wacht op goedkeuring", + "command": "Opdracht wacht op goedkeuring", + "useMcpServer": "MCP-server gebruiken voor goedkeuring", + "completionResult": "Taak voltooid" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index a09c276fd1..81e3308976 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -517,6 +517,21 @@ "label": "Włącz syntezę mowy", "description": "Gdy włączone, Roo będzie czytać na głos swoje odpowiedzi za pomocą syntezy mowy.", "speedLabel": "Szybkość" + }, + "system": { + "label": "Włącz powiadomienia systemowe", + "description": "Gdy włączone, Roo będzie wyświetlać powiadomienia systemowe dla ważnych zdarzeń i wiadomości.", + "testButton": "Testuj powiadomienie systemowe", + "testTitle": "Roo Code", + "testMessage": "Powiadomienia systemowe działają prawidłowo!", + "apiReqFailed": "Błąd żądania API", + "mistakeLimitReached": "Osiągnięto limit błędów", + "followup": "Oczekiwanie na twoją odpowiedź", + "toolRequest": "Żądanie narzędzia oczekuje na zatwierdzenie", + "browserAction": "Akcja przeglądarki oczekuje na zatwierdzenie", + "command": "Polecenie oczekuje na zatwierdzenie", + "useMcpServer": "Użyj serwera MCP do zatwierdzenia", + "completionResult": "Zadanie ukończone" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index e23dfe8e7b..c4b23e303e 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -517,6 +517,21 @@ "label": "Ativar texto para fala", "description": "Quando ativado, o Roo lerá em voz alta suas respostas usando texto para fala.", "speedLabel": "Velocidade" + }, + "system": { + "label": "Ativar notificações do sistema", + "description": "Quando ativado, o Roo mostrará notificações do sistema para eventos e mensagens importantes.", + "testButton": "Testar notificação do sistema", + "testTitle": "Roo Code", + "testMessage": "As notificações do sistema estão funcionando corretamente!", + "apiReqFailed": "Erro de solicitação da API", + "mistakeLimitReached": "Limite de erros alcançado", + "followup": "Aguardando sua resposta de acompanhamento", + "toolRequest": "Solicitação de ferramenta aguardando aprovação", + "browserAction": "Ação do navegador aguardando aprovação", + "command": "Comando aguardando aprovação", + "useMcpServer": "Usar servidor MCP para aprovação", + "completionResult": "Tarefa concluída" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 900996b569..30c64be5e3 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -517,6 +517,21 @@ "label": "Включить озвучивание", "description": "Если включено, Roo будет озвучивать свои ответы с помощью преобразования текста в речь.", "speedLabel": "Скорость" + }, + "system": { + "label": "Включить системные уведомления", + "description": "Если включено, Roo будет показывать системные уведомления для важных событий и сообщений.", + "testButton": "Тестировать системное уведомление", + "testTitle": "Roo Code", + "testMessage": "Системные уведомления работают правильно!", + "apiReqFailed": "Ошибка API-запроса", + "mistakeLimitReached": "Достигнут лимит ошибок", + "followup": "Ожидаем твой ответ", + "toolRequest": "Запрос инструмента ожидает одобрения", + "browserAction": "Действие браузера ожидает одобрения", + "command": "Команда ожидает одобрения", + "useMcpServer": "Использовать MCP-сервер для одобрения", + "completionResult": "Задача завершена" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index e34f6c08b9..7f0b9061fe 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -517,6 +517,21 @@ "label": "Metinden sese özelliğini etkinleştir", "description": "Etkinleştirildiğinde, Roo yanıtlarını metinden sese teknolojisi kullanarak sesli okuyacaktır.", "speedLabel": "Hız" + }, + "system": { + "label": "Sistem bildirimlerini etkinleştir", + "description": "Etkinleştirildiğinde, Roo önemli olaylar ve mesajlar için sistem bildirimleri gösterecektir.", + "testButton": "Sistem bildirimini test et", + "testTitle": "Roo Code", + "testMessage": "Sistem bildirimleri düzgün çalışıyor!", + "apiReqFailed": "API istek hatası", + "mistakeLimitReached": "Hata sınırına ulaşıldı", + "followup": "Takip yanıtını bekliyoruz", + "toolRequest": "Araç isteği onay bekliyor", + "browserAction": "Tarayıcı eylemi onay bekliyor", + "command": "Komut onay bekliyor", + "useMcpServer": "Onay için MCP sunucusu kullan", + "completionResult": "Görev tamamlandı" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 8348156569..28eb161e8f 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -517,6 +517,21 @@ "label": "Bật chuyển văn bản thành giọng nói", "description": "Khi được bật, Roo sẽ đọc to các phản hồi của nó bằng chức năng chuyển văn bản thành giọng nói.", "speedLabel": "Tốc độ" + }, + "system": { + "label": "Bật thông báo hệ thống", + "description": "Khi được bật, Roo sẽ hiển thị thông báo hệ thống cho các sự kiện và tin nhắn quan trọng.", + "testButton": "Kiểm tra thông báo hệ thống", + "testTitle": "Roo Code", + "testMessage": "Thông báo hệ thống hoạt động bình thường!", + "apiReqFailed": "Lỗi yêu cầu API", + "mistakeLimitReached": "Đã đạt giới hạn lỗi", + "followup": "Đang chờ phản hồi tiếp theo của bạn", + "toolRequest": "Yêu cầu công cụ đang chờ phê duyệt", + "browserAction": "Hành động trình duyệt đang chờ phê duyệt", + "command": "Lệnh đang chờ phê duyệt", + "useMcpServer": "Sử dụng máy chủ MCP để phê duyệt", + "completionResult": "Tác vụ hoàn thành" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 69e13dbfdf..82f18fc57a 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -517,6 +517,21 @@ "label": "启用文本转语音", "description": "启用后,Roo 将使用文本转语音功能朗读其响应。", "speedLabel": "速度" + }, + "system": { + "label": "启用系统通知", + "description": "启用后 Roo 将为重要事件和消息显示系统通知。", + "testButton": "测试系统通知", + "testTitle": "Roo Code", + "testMessage": "系统通知运行正常!", + "apiReqFailed": "API 请求错误", + "mistakeLimitReached": "错误次数达到上限", + "followup": "等待你的后续回复", + "toolRequest": "工具请求等待批准", + "browserAction": "浏览器操作等待批准", + "command": "命令等待批准", + "useMcpServer": "使用 MCP 服务进行批准", + "completionResult": "任务完成" } }, "contextManagement": { diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index aa26fea4cf..725ed8f298 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -517,6 +517,21 @@ "label": "啟用文字轉語音", "description": "啟用後,Roo 將使用文字轉語音功能朗讀其回應。", "speedLabel": "速度" + }, + "system": { + "label": "啟用系統通知", + "description": "啟用後,Roo 將為重要事件和訊息顯示系統通知。", + "testButton": "測試系統通知", + "testTitle": "Roo Code", + "testMessage": "系統通知運作正常!", + "apiReqFailed": "API 請求錯誤", + "mistakeLimitReached": "錯誤次數達到上限", + "followup": "等待你的後續回覆", + "toolRequest": "工具請求等待核准", + "browserAction": "瀏覽器動作等待核准", + "command": "指令等待核准", + "useMcpServer": "使用 MCP 伺服器進行核准", + "completionResult": "工作完成" } }, "contextManagement": { diff --git a/webview-ui/src/utils/showSystemNotification.ts b/webview-ui/src/utils/showSystemNotification.ts new file mode 100644 index 0000000000..4450553b96 --- /dev/null +++ b/webview-ui/src/utils/showSystemNotification.ts @@ -0,0 +1,11 @@ +import { vscode } from "../utils/vscode" +import debounce from "debounce" + +export const showSystemNotification = debounce((message: string) => { + vscode.postMessage({ + type: "showSystemNotification", + notificationOptions: { + message, + }, + }) +}, 100) From a3bffcd5ce617f31b019c0e3aa9ccf8568aad002 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Tue, 12 Aug 2025 15:00:47 +0800 Subject: [PATCH 2/5] feat: system notifications only trigger when vscode unfocus --- src/integrations/notifications/index.ts | 7 +++++++ src/shared/WebviewMessage.ts | 1 + .../src/components/settings/NotificationSettings.tsx | 1 + 3 files changed, 9 insertions(+) diff --git a/src/integrations/notifications/index.ts b/src/integrations/notifications/index.ts index 8a26c1bf10..8d236b9142 100644 --- a/src/integrations/notifications/index.ts +++ b/src/integrations/notifications/index.ts @@ -1,10 +1,12 @@ 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 { @@ -65,6 +67,11 @@ async function showLinuxNotification(options: NotificationOptions): Promise { try { + if (vscode.window.state.focused && !options.force) { + // If the window is focused, do not show a notification + return + } + const { title = "Roo Code", message } = options if (!message) { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index a29621f614..576246655d 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -297,6 +297,7 @@ export interface WebviewMessage { title?: string subtitle?: string message: string + force?: boolean // Force notification even if the window is focused } } diff --git a/webview-ui/src/components/settings/NotificationSettings.tsx b/webview-ui/src/components/settings/NotificationSettings.tsx index 3fe1bad616..668775a788 100644 --- a/webview-ui/src/components/settings/NotificationSettings.tsx +++ b/webview-ui/src/components/settings/NotificationSettings.tsx @@ -39,6 +39,7 @@ export const NotificationSettings = ({ notificationOptions: { title: t("settings.notifications.system.testTitle"), message: t("settings.notifications.system.testMessage"), + force: true, }, }) } From 26c6ffb27c32a3327656887345898facca7c98f0 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Tue, 12 Aug 2025 15:26:43 +0800 Subject: [PATCH 3/5] fix: escape special characters in notification options --- src/integrations/notifications/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/integrations/notifications/index.ts b/src/integrations/notifications/index.ts index 8d236b9142..ef59d1e4a2 100644 --- a/src/integrations/notifications/index.ts +++ b/src/integrations/notifications/index.ts @@ -78,11 +78,12 @@ export async function showSystemNotification(options: NotificationOptions): Prom throw new Error("Message is required") } + const escape = (str: string = "") => str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') const escapedOptions = { ...options, - title: title.replace(/"/g, '\\"'), - message: message.replace(/"/g, '\\"'), - subtitle: options.subtitle?.replace(/"/g, '\\"') || "", + title: escape(title), + message: escape(message), + subtitle: escape(options.subtitle), } switch (platform()) { From 5926f209cdde8e7421857f70bd611ba6966c48f1 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Tue, 12 Aug 2025 23:19:17 +0800 Subject: [PATCH 4/5] fix: correct notification translation keys format in NotificationSettings --- webview-ui/src/components/settings/NotificationSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/settings/NotificationSettings.tsx b/webview-ui/src/components/settings/NotificationSettings.tsx index 668775a788..3cb1ea5512 100644 --- a/webview-ui/src/components/settings/NotificationSettings.tsx +++ b/webview-ui/src/components/settings/NotificationSettings.tsx @@ -37,8 +37,8 @@ export const NotificationSettings = ({ vscode.postMessage({ type: "showSystemNotification", notificationOptions: { - title: t("settings.notifications.system.testTitle"), - message: t("settings.notifications.system.testMessage"), + title: t("settings:notifications.system.testTitle"), + message: t("settings:notifications.system.testMessage"), force: true, }, }) From faf1d1ec2e305d4bc65ba422720ce7a61e8a3378 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Mon, 18 Aug 2025 22:42:16 +0800 Subject: [PATCH 5/5] fix: provide specific error message when notify-send is not installed --- src/integrations/notifications/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/integrations/notifications/index.ts b/src/integrations/notifications/index.ts index ef59d1e4a2..6962637c9e 100644 --- a/src/integrations/notifications/index.ts +++ b/src/integrations/notifications/index.ts @@ -60,7 +60,12 @@ async function showLinuxNotification(options: NotificationOptions): Promise