777genius/claude-notifications-go

Loading cursor persists after clicking notification on GNOME/Wayland

Open

#61 opened on Mar 31, 2026

View on GitHub
 (12 comments) (1 reaction) (0 assignees)Go (100 forks)auto 404
bughelp wantedlinux

Repository metrics

Stars
 (759 stars)
PR merge metrics
 (PR metrics pending)

Description

Problem

On Ubuntu 24.04 (GNOME/Wayland), clicking a notification from claude-notifications causes the mouse cursor to change to a loading spinner for ~15 seconds, even though click-to-focus works correctly and the terminal window is focused immediately.

Root Cause

D-Bus monitoring (dbus-monitor --session "interface='org.freedesktop.Notifications'") reveals the issue:

signal member=ActivationToken
   uint32 162
   string "gnome-shell/Visual Studio Code/4019-22-gram_TIME139555122"

signal member=ActionInvoked
   uint32 162
   string "default"

signal member=NotificationClosed
   uint32 162
   uint32 2

When the user clicks a notification, GNOME Shell emits three signals in order:

  1. ActivationToken — GNOME Shell creates a Wayland xdg-activation-v1 token for the app specified in the desktop-entry hint (e.g., "Visual Studio Code"). This is where the loading cursor starts.
  2. ActionInvoked — Our daemon receives this signal and successfully focuses the correct window.
  3. NotificationClosed — The notification is dismissed.

The problem: GNOME Shell creates an activation token expecting the target app (e.g., VS Code) to consume it via the xdg-activation-v1 Wayland protocol. However, the target app never receives or consumes this token — our daemon handles the focus instead. The unconsumed token causes GNOME Shell to show the loading cursor until its startup notification timeout (~15 seconds).

The desktop-entry hint was added in PR #42 to fix a bug where clicking a notification opened Nautilus instead of the terminal. The hint correctly tells GNOME Shell which app the notification belongs to, but it also triggers the activation token behavior.

Proposed Solution

Create a claude-notifications.desktop file with StartupNotify=false and use it as the desktop-entry hint instead of the terminal's desktop entry.

Desktop file (~/.local/share/applications/claude-notifications.desktop):

[Desktop Entry]
Name=Claude Notifications
Type=Application
Icon=utilities-terminal
Exec=true
NoDisplay=true
StartupNotify=false
Terminal=false

Code change in internal/daemon/server.go:

Hints: map[string]dbus.Variant{
    "desktop-entry":  dbus.MakeVariant("claude-notifications"),
    "suppress-sound": dbus.MakeVariant(true),
},

Why this works:

  • desktop-entry is still present → GNOME Shell knows which app the notification belongs to → no Nautilus fallback (PR #42 fix preserved)
  • StartupNotify=false → GNOME Shell does NOT create an activation token → no loading cursor
  • Our daemon still receives ActionInvoked signal and handles window focus correctly
  • NoDisplay=true → the desktop entry doesn't appear in app launchers

Trade-off: Notifications will show as "Claude Notifications" in GNOME's notification panel instead of being grouped under the terminal app. This is acceptable since the daemon handles focus correctly via ActionInvoked.

Environment

  • Ubuntu 24.04
  • GNOME Shell on Wayland
  • claude-notifications-go with click-to-focus enabled

Contributor guide