777genius / 777genius/agent-notifications
macOS: click-to-focus fails silently when target terminal window is minimized (AXRaise is a no-op)
- Dominant language
- Go
- Stars
- 813
- Forks
- 108
- Avg merge
- 17h 58m
- Merged PRs (30d)
- 36
Description
## Summary
On macOS, clicking a notification to focus a terminal window **silently fails** when the target window is minimized to the Dock. The focused app activates but the target window stays minimized, so nothing visible happens.
Affects both code paths in `internal/notifier/ax_focus_darwin.go`:
- `raiseWindowByAXDocument` (Ghostty via AXDocument / OSC 7 file URL)
- `raiseWindowByAXTitle` (other terminals via AXTitle folder matching)
## Environment
- macOS 14.5 (Sonoma, Darwin 23.5.0)
- Plugin v1.36.6 (installed via marketplace)
- Terminal: Ghostty (`com.mitchellh.ghostty`), but same bug applies to every terminal path that ends in `AXRaise`
- Accessibility and Screen Recording permissions granted
## Steps to reproduce
1. Open Claude Code in Ghostty at e.g. `~/work`.
2. Minimize the Ghostty window (yellow traffic light).
3. Trigger a notification (e.g., Stop event on task completion).
4. Click the notification banner.
**Expected:** Ghostty activates AND the minimized window is restored (un-minimized) and raised.
**Actual:** Ghostty activates (app icon bounces / menu bar switches) but the window stays minimized in the Dock. Notification click has no visible effect.
## Root cause
Both `raiseWindowByAXDocument` and `raiseWindowByAXTitle` iterate `AXWindows`, match the target window, then call:
\`\`\`c
AXUIElementPerformAction(w, CFSTR(\"AXRaise\"));
AXUIElementSetAttributeValue(appEl, CFSTR(\"AXFrontmost\"), kCFBooleanTrue);
\`\`\`
On macOS, **`AXRaise` is a no-op on a window whose `AXMinimized` attribute is `true`**. A minimized window must be un-minimized first via:
\`\`\`c
AXUIElementSetAttributeValue(w, CFSTR(\"AXMinimized\"), kCFBooleanFalse);
\`\`\`
`NSRunningApplication activateWithOptions:` (called in \`activateByPID\`) also does not un-minimize windows; it only brings the app's non-minimized windows forward.
## Proposed fix
Before calling \`AXRaise\`, check and clear \`AXMinimized\` in both helpers. Minimal diff:
\`\`\`diff
--- a/internal/notifier/ax_focus_darwin.go
+++ b/internal/notifier/ax_focus_darwin.go
@@ raiseWindowByAXDocument @@
if (ok && strcmp(buf, fileURL) == 0) {
+ CFTypeRef minRef = NULL;
+ if (AXUIElementCopyAttributeValue(w, CFSTR(\"AXMinimized\"), &minRef) == kAXErrorSuccess && minRef) {
+ if (CFBooleanGetValue((CFBooleanRef)minRef)) {
+ AXUIElementSetAttributeValue(w, CFSTR(\"AXMinimized\"), kCFBooleanFalse);
+ }
+ CFRelease(minRef);
+ }
AXUIElementPerformAction(w, CFSTR(\"AXRaise\"));
AXUIElementSetAttributeValue(appEl, CFSTR(\"AXFrontmost\"), kCFBooleanTrue);
found = 1;
}
@@ raiseWindowByAXTitle @@
if (matched) {
+ CFTypeRef minRef = NULL;
+ if (AXUIElementCopyAttributeValue(w, CFSTR(\"AXMinimized\"), &minRef) == kAXErrorSuccess && minRef) {
+ if (CFBooleanGetValue((CFBooleanRef)minRef)) {
+ AXUIElementSetAttributeValue(w, CFSTR(\"AXMinimized\"), kCFBooleanFalse);
+ }
+ CFRelease(minRef);
+ }
AXUIElementPerformAction(w, CFSTR(\"AXRaise\"));
AXUIElementSetAttributeValue(appEl, CFSTR(\"AXFrontmost\"), kCFBooleanTrue);
found = 1;
break;
}
\`\`\`
Setting \`AXMinimized = false\` triggers the Dock un-minimize animation identical to a manual Dock icon click. After that completes, \`AXRaise\` + \`AXFrontmost\` work as usual. No extra permissions needed (Accessibility is already a prerequisite for the existing calls).
A small delay (e.g. \`usleep(150000)\`) between the un-minimize and \`AXRaise\` may be needed on slower machines while the un-minimize animation plays; the existing \`retryWindowFocus\` backoff in Go (150/250/400 ms) should cover this.
## Note on the other fallback path
\`findSwitchAndActivate\` + \`switchToWindowSpace\` uses \`CGSCopySpacesForWindows\` / \`CGSManagedDisplaySetCurrentSpace\` to cross Spaces, but \`CGWindowListCopyWindowInfo\` by default returns the minimized window's off-screen bounds, and the same \`AXRaise\` no-op applies afterwards. The same un-minimize step would fix that path as well.
Happy to open a PR with the full patch and tests if that's welcome.
Contributor guide
Research direction
Read internal/notifier/ax_focus_darwin.go, especially raiseWindowByAXDocument and raiseWindowByAXTitle, then inspect the existing retryWindowFocus path. Verify that a minimized matching window is restored before AXRaise and AXFrontmost are used, and confirm that clicking a notification visibly restores and focuses it on macOS.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, macos
- Domain
- desktop, operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100