mirror of
https://github.com/imjasonh/cnotes
synced 2026-07-18 23:01:31 +00:00
Switch to terminal-notifier for visual notifications
- Replaced go-toast with terminal-notifier for better reliability - Visual notifications now require: brew install terminal-notifier - Speech notifications continue to work with built-in say command - Cleaner implementation with no deprecation warnings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f36813078c
commit
9cfbafb584
4 changed files with 67 additions and 40 deletions
|
|
@ -21,52 +21,73 @@ func SpeakNotification(ctx context.Context, input hooks.HookInput) (hooks.HookOu
|
|||
return hooks.HookOutput{Decision: "continue"}, nil
|
||||
}
|
||||
|
||||
// Check if say command is available
|
||||
if _, err := exec.LookPath("say"); err != nil {
|
||||
slog.Debug("say command not found", "error", err)
|
||||
return hooks.HookOutput{Decision: "continue"}, nil
|
||||
}
|
||||
|
||||
// Build the message to speak
|
||||
var message string
|
||||
// Build the notification content
|
||||
var title, message, subtitle string
|
||||
if input.Notification.Permission != "" {
|
||||
// Permission request
|
||||
message = fmt.Sprintf("Claude is requesting permission to use %s. %s",
|
||||
input.Notification.Tool,
|
||||
input.Notification.Message)
|
||||
title = "Claude Permission Request"
|
||||
subtitle = fmt.Sprintf("Tool: %s", input.Notification.Tool)
|
||||
message = fmt.Sprintf("Requesting permission to %s", input.Notification.Message)
|
||||
} else {
|
||||
// Regular notification
|
||||
message = fmt.Sprintf("Notification from %s: %s",
|
||||
input.Notification.Tool,
|
||||
input.Notification.Message)
|
||||
title = "Claude Notification"
|
||||
subtitle = fmt.Sprintf("Tool: %s", input.Notification.Tool)
|
||||
message = input.Notification.Message
|
||||
}
|
||||
|
||||
// Sanitize the message for speech
|
||||
message = strings.ReplaceAll(message, "\n", " ")
|
||||
message = strings.ReplaceAll(message, "\"", "'")
|
||||
|
||||
// Run say command with a shorter message if it's too long
|
||||
if len(message) > 200 {
|
||||
message = message[:197] + "..."
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "say", "-v", "Samantha", message)
|
||||
if err := cmd.Start(); err != nil {
|
||||
slog.Error("failed to start say command", "error", err)
|
||||
return hooks.HookOutput{Decision: "continue"}, nil
|
||||
}
|
||||
|
||||
// Don't wait for completion to avoid blocking
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
slog.Debug("say command failed", "error", err)
|
||||
// Show notification using terminal-notifier if available
|
||||
if _, err := exec.LookPath("terminal-notifier"); err == nil {
|
||||
args := []string{
|
||||
"-title", title,
|
||||
"-subtitle", subtitle,
|
||||
"-message", message,
|
||||
"-sound", "default",
|
||||
"-group", "claude-hooks",
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "terminal-notifier", args...)
|
||||
if err := cmd.Start(); err != nil {
|
||||
slog.Error("failed to show notification", "error", err)
|
||||
} else {
|
||||
// Don't wait for completion
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
slog.Debug("terminal-notifier failed", "error", err)
|
||||
}
|
||||
}()
|
||||
slog.Info("showed notification",
|
||||
"tool", input.Notification.Tool,
|
||||
"permission", input.Notification.Permission != "")
|
||||
}
|
||||
} else {
|
||||
slog.Debug("terminal-notifier not found, install with: brew install terminal-notifier")
|
||||
}
|
||||
|
||||
slog.Info("speaking notification",
|
||||
"tool", input.Notification.Tool,
|
||||
"permission", input.Notification.Permission != "",
|
||||
"message_length", len(message))
|
||||
// Also speak the notification if say is available
|
||||
if _, err := exec.LookPath("say"); err == nil {
|
||||
spokenMessage := fmt.Sprintf("%s. %s", subtitle, message)
|
||||
|
||||
// Sanitize for speech
|
||||
spokenMessage = strings.ReplaceAll(spokenMessage, "\n", " ")
|
||||
spokenMessage = strings.ReplaceAll(spokenMessage, "\"", "'")
|
||||
|
||||
// Truncate if too long
|
||||
if len(spokenMessage) > 200 {
|
||||
spokenMessage = spokenMessage[:197] + "..."
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "say", "-v", "Samantha", spokenMessage)
|
||||
if err := cmd.Start(); err != nil {
|
||||
slog.Debug("failed to start say command", "error", err)
|
||||
} else {
|
||||
// Don't wait for completion to avoid blocking
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
slog.Debug("say command failed", "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
return hooks.HookOutput{Decision: "continue"}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue