Two related additions for using omarchy-send beyond the focused TUI: Headless send (no TUI / no TTY) — for scripts, cron, SSH sessions: omarchy-send --to "<alias>" --message "<text>" [--send-pin N] [--wait 15s] Resolves the peer by alias over multicast (discovery only, not the HTTP receiver, so it co-exists with a running instance), sends, prints a one-line result, and exits non-zero on not-found / failure. New discovery.FindPeer/Snapshot and client.SendMessageSync (returns the error directly, incl. ErrPinRequired). Bugfix surfaced by the above: the official LocalSend client answers a message prepare-upload with HTTP 204 No Content (the text rides in the preview field, nothing to upload). prepareUpload only accepted 200, so message sends to official peers failed with "prepare-upload status 204" in BOTH the new headless path and the existing TUI. Now treats 204 as success (empty response). Desktop notifications: a running receiver raises a notify-send notification on an incoming message or file offer, so mako shows it on Omarchy/Hyprland even when the TUI isn't focused. New internal/notify (best-effort; self-disabling on headless boxes with no notify-send / session bus). Off-switch: --no-notify flag, cfg.NoNotify, and a Settings 'n' toggle wired to a live atomic gate via Controller.SetNotify so it takes effect without a restart. Tests: discovery/find_test, client/message_sync_test, client/prepare_204_test, app/events_test. Race-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
Go
56 lines
1.8 KiB
Go
// Package notify raises desktop notifications via libnotify's notify-send,
|
|
// which on Omarchy/Hyprland is displayed by the mako daemon. It is best-effort:
|
|
// when notify-send is absent or there is no graphical session bus (a genuinely
|
|
// headless box), it silently does nothing, so the receiver never blocks or
|
|
// errors on a machine with no desktop.
|
|
package notify
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
"omarchy-send/internal/dbg"
|
|
)
|
|
|
|
// appName is shown as the originating application in the notification daemon,
|
|
// and iconName is the bundled hicolor icon the installer drops in (falls back to
|
|
// no icon if the theme lacks it — harmless).
|
|
const (
|
|
appName = "Omarchy-Send"
|
|
iconName = "omarchy-send"
|
|
)
|
|
|
|
// Available reports whether a desktop notification can plausibly be shown:
|
|
// notify-send is on PATH and we appear to be inside a graphical session. The
|
|
// TUI calls this once at startup to decide whether to enable notifications.
|
|
func Available() bool {
|
|
if _, err := exec.LookPath("notify-send"); err != nil {
|
|
return false
|
|
}
|
|
return os.Getenv("WAYLAND_DISPLAY") != "" ||
|
|
os.Getenv("DISPLAY") != "" ||
|
|
os.Getenv("DBUS_SESSION_BUS_ADDRESS") != ""
|
|
}
|
|
|
|
// Send shows a desktop notification with the given summary and body. It returns
|
|
// immediately; the notify-send invocation runs in the background (bounded by a
|
|
// short timeout) and any failure is recorded only in the debug log. It is a
|
|
// no-op when Available reports false, so it is safe to call unconditionally.
|
|
func Send(summary, body string) {
|
|
if !Available() {
|
|
return
|
|
}
|
|
go func() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(ctx, "notify-send",
|
|
"-a", appName,
|
|
"-i", iconName,
|
|
summary, body)
|
|
if err := cmd.Run(); err != nil {
|
|
dbg.Logf("notify-send failed: %v", err)
|
|
}
|
|
}()
|
|
}
|