mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
refactor: use terminal-notifier for macOS and node-notifier for Windows/Linux
- macOS: Uses terminal-notifier (native tool) with fallback to osascript - Windows/Linux: Uses node-notifier with fallback to native tools - Removed VSCode notification API to use only native OS notifications - No compilation issues as terminal-notifier doesn't require building
This commit is contained in:
parent
cb3941ff65
commit
14b0d66e8a
3 changed files with 125 additions and 66 deletions
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
|
|
@ -598,6 +598,9 @@ importers:
|
|||
'@types/lodash.debounce':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
'@types/node-notifier':
|
||||
specifier: ^8.0.5
|
||||
version: 8.0.5
|
||||
'@vscode/codicons':
|
||||
specifier: ^0.0.36
|
||||
version: 0.0.36
|
||||
|
|
@ -815,9 +818,6 @@ importers:
|
|||
'@types/node-ipc':
|
||||
specifier: ^9.2.3
|
||||
version: 9.2.3
|
||||
'@types/node-notifier':
|
||||
specifier: ^8.0.5
|
||||
version: 8.0.5
|
||||
'@types/proper-lockfile':
|
||||
specifier: ^4.1.4
|
||||
version: 4.1.4
|
||||
|
|
|
|||
|
|
@ -14,20 +14,118 @@ async function showMacOSNotification(options: NotificationOptions): Promise<void
|
|||
const { title = "Roo Code", subtitle = "", message } = options
|
||||
|
||||
try {
|
||||
// Try to use node-notifier first (more reliable cross-platform solution)
|
||||
const notifier = await import("node-notifier")
|
||||
// First try terminal-notifier (native macOS tool, no compilation needed)
|
||||
await execa("terminal-notifier", [
|
||||
"-title",
|
||||
title,
|
||||
"-subtitle",
|
||||
subtitle || "",
|
||||
"-message",
|
||||
message,
|
||||
"-sound",
|
||||
"default",
|
||||
"-group",
|
||||
"com.roocode.vscode",
|
||||
"-appIcon",
|
||||
path.join(__dirname, "..", "..", "assets", "icons", "icon.png"),
|
||||
])
|
||||
} catch (terminalNotifierError) {
|
||||
// If terminal-notifier is not available, fall back to osascript
|
||||
console.log("terminal-notifier not available, falling back to osascript")
|
||||
|
||||
// Get the extension's icon path
|
||||
const escape = (str: string = "") => str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'")
|
||||
const script = `display notification "${escape(message)}" with title "${escape(title)}" subtitle "${escape(subtitle)}" sound name "default"`
|
||||
|
||||
try {
|
||||
await execa("osascript", ["-e", script])
|
||||
} catch (osascriptError) {
|
||||
console.error("Failed to show macOS notification:", osascriptError)
|
||||
throw new Error(`Failed to show macOS notification: ${osascriptError}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function showWindowsNotification(options: NotificationOptions): Promise<void> {
|
||||
const { title = "Roo Code", subtitle = "", message } = options
|
||||
|
||||
try {
|
||||
// Use node-notifier for Windows
|
||||
const notifier = await import("node-notifier")
|
||||
const iconPath = path.join(__dirname, "..", "..", "assets", "icons", "icon.png")
|
||||
|
||||
// Windows notification doesn't support subtitle, so combine it with message
|
||||
const fullMessage = subtitle ? `${subtitle}\n${message}` : message
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
notifier.notify(
|
||||
{
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
message: message,
|
||||
sound: "Tink",
|
||||
message: fullMessage,
|
||||
icon: iconPath,
|
||||
sound: true,
|
||||
wait: false,
|
||||
appID: "Roo Code",
|
||||
},
|
||||
(error: Error | null) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
} catch (error) {
|
||||
// Fallback to PowerShell if node-notifier fails
|
||||
console.warn("node-notifier failed, falling back to PowerShell:", error)
|
||||
|
||||
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 (psError) {
|
||||
throw new Error(`Failed to show Windows notification: ${psError}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function showLinuxNotification(options: NotificationOptions): Promise<void> {
|
||||
const { title = "Roo Code", subtitle = "", message } = options
|
||||
|
||||
try {
|
||||
// Use node-notifier for Linux
|
||||
const notifier = await import("node-notifier")
|
||||
const iconPath = path.join(__dirname, "..", "..", "assets", "icons", "icon.png")
|
||||
|
||||
// Combine subtitle and message for node-notifier
|
||||
const fullMessage = subtitle ? `${subtitle}\n${message}` : message
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
notifier.notify(
|
||||
{
|
||||
title: title,
|
||||
message: fullMessage,
|
||||
icon: iconPath,
|
||||
sound: true,
|
||||
wait: false,
|
||||
},
|
||||
(error: Error | null) => {
|
||||
|
|
@ -40,69 +138,30 @@ async function showMacOSNotification(options: NotificationOptions): Promise<void
|
|||
)
|
||||
})
|
||||
} catch (error) {
|
||||
// Fallback to osascript if node-notifier fails
|
||||
console.warn("node-notifier failed, falling back to osascript:", error)
|
||||
// Fallback to notify-send if node-notifier fails
|
||||
console.warn("node-notifier failed, falling back to notify-send:", error)
|
||||
|
||||
const escape = (str: string = "") => str.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
||||
const script = `display notification "${escape(message)}" with title "${escape(title)}" subtitle "${escape(subtitle)}" sound name "Tink"`
|
||||
// Combine subtitle and message if subtitle exists
|
||||
const fullMessage = subtitle ? `${subtitle}\n${message}` : message
|
||||
|
||||
try {
|
||||
await execa("osascript", ["-e", script])
|
||||
} catch (osascriptError) {
|
||||
throw new Error(`Failed to show macOS notification: ${osascriptError}`)
|
||||
await execa("notify-send", [
|
||||
title,
|
||||
fullMessage,
|
||||
"-i",
|
||||
path.join(__dirname, "..", "..", "assets", "icons", "icon.png"),
|
||||
])
|
||||
} catch (notifySendError: any) {
|
||||
if (notifySendError.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: ${notifySendError}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function showSystemNotification(options: NotificationOptions): Promise<void> {
|
||||
try {
|
||||
if (vscode.window.state.focused && !options.force) {
|
||||
|
|
|
|||
|
|
@ -437,6 +437,7 @@
|
|||
"@roo-code/telemetry": "workspace:^",
|
||||
"@roo-code/types": "workspace:^",
|
||||
"@types/lodash.debounce": "^4.0.9",
|
||||
"@types/node-notifier": "^8.0.5",
|
||||
"@vscode/codicons": "^0.0.36",
|
||||
"async-mutex": "^0.5.0",
|
||||
"axios": "^1.7.4",
|
||||
|
|
@ -511,7 +512,6 @@
|
|||
"@types/node": "20.x",
|
||||
"@types/node-cache": "^4.1.3",
|
||||
"@types/node-ipc": "^9.2.3",
|
||||
"@types/node-notifier": "^8.0.5",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/ps-tree": "^1.1.6",
|
||||
"@types/stream-json": "^1.7.8",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue