Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions claude-notify.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ the terminal is in the background.
2. The hook runs `~/bin/claude-notify`, a bash script that calls Windows PowerShell
directly from WSL via `/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`.
3. PowerShell creates a `NotifyIcon` (system tray icon) and shows a balloon tip for 5 s.
4. The script sleeps 6 s to keep the PowerShell process alive long enough for the
balloon to display, then disposes the icon and exits.
4. A WinForms message loop keeps the process alive until the balloon is dismissed or clicked.
5. **Clicking the balloon** restores the Windows Terminal window (if minimised) and brings
it to the foreground via `ShowWindow` + `SetForegroundWindow`.
6. On dismiss or click, the message loop exits, the icon is disposed, and the process ends.

---

Expand All @@ -45,12 +47,17 @@ public class Win32 {
public static extern IntPtr GetForegroundWindow();
[DllImport(\"user32.dll\")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport(\"user32.dll\")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport(\"user32.dll\")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
'@
\$hwnd = [Win32]::GetForegroundWindow()
\$pid = 0
[Win32]::GetWindowThreadProcessId(\$hwnd, [ref]\$pid) | Out-Null
\$proc = Get-Process -Id \$pid -ErrorAction SilentlyContinue
\$winPid = 0
[Win32]::GetWindowThreadProcessId(\$hwnd, [ref]\$winPid) | Out-Null
\$proc = Get-Process -Id \$winPid -ErrorAction SilentlyContinue
if (\$proc -and \$proc.Name -eq 'WindowsTerminal') { exit 0 }

Add-Type -AssemblyName System.Windows.Forms
Expand All @@ -60,8 +67,21 @@ Add-Type -AssemblyName System.Drawing
\$notification.BalloonTipTitle = '$title'
\$notification.BalloonTipText = '$message'
\$notification.Visible = \$true

\$notification.add_BalloonTipClicked({
\$wt = Get-Process -Name 'WindowsTerminal' -ErrorAction SilentlyContinue | Select-Object -First 1
if (\$wt -and \$wt.MainWindowHandle -ne [IntPtr]::Zero) {
[Win32]::ShowWindow(\$wt.MainWindowHandle, 9) | Out-Null
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic number 9 should be documented or replaced with a named constant for clarity. In Win32 API, 9 corresponds to SW_RESTORE, which restores a minimized window. Consider adding a comment explaining this value, such as: [Win32]::ShowWindow(\$wt.MainWindowHandle, 9) | Out-Null # SW_RESTORE

Suggested change
[Win32]::ShowWindow(\$wt.MainWindowHandle, 9) | Out-Null
[Win32]::ShowWindow(\$wt.MainWindowHandle, 9) | Out-Null # SW_RESTORE

Copilot uses AI. Check for mistakes.
[Win32]::SetForegroundWindow(\$wt.MainWindowHandle) | Out-Null
}
[System.Windows.Forms.Application]::Exit()
})
\$notification.add_BalloonTipClosed({
[System.Windows.Forms.Application]::Exit()
})

\$notification.ShowBalloonTip(5000)
Start-Sleep -Seconds 6
[System.Windows.Forms.Application]::Run()
\$notification.Dispose()
"
```
Expand Down Expand Up @@ -109,7 +129,8 @@ Restart Claude Code for the hook to take effect.

When Claude Code hits a permission prompt and is waiting for your approval, a Windows
balloon tip appears in the system tray with the title **Claude Code** and the message
**Needs your input!**
**Needs your input!** Clicking the balloon restores and focuses Windows Terminal so you
can approve or deny immediately without manually switching windows.

---

Expand All @@ -126,5 +147,12 @@ balloon tip appears in the system tray with the title **Claude Code** and the me
- Ensure the command in `settings.json` is wrapped as `bash -c '... &'`.

**Balloon tip flashes and disappears instantly**
- The `Start-Sleep -Seconds 6` in the script keeps the PowerShell process alive so the
notification stays visible. If you removed or reduced it, restore it to at least 6 s.
- The WinForms message loop (`Application.Run()`) keeps the PowerShell process alive until
the balloon is clicked or times out. If the process exits immediately, check that both
`add_BalloonTipClicked` and `add_BalloonTipClosed` handlers call `Application.Exit()`.

**Click does not focus Windows Terminal**
- Windows restricts `SetForegroundWindow` to prevent background processes from stealing focus.
The balloon-click event fires in the context of the notification click, which satisfies the
restriction in most cases. If it still doesn't work, try clicking the taskbar button instead.
- Make sure Windows Terminal is running (not just WSL in another host).