Add urgent-view feature (Win+U)#10
Conversation
Mirrors AwesomeWM's urgent-window behavior: when a managed window flashes its taskbar entry (HSHELL_FLASH), its non-active views light up red on the bar; Win+U cycles through urgent views, clearing each on activation. Two test surfaces: - tests/test_Manager_urgentView.ahk: Yunit unit + dispatch integration against synthesized state, including regression guards for the HSHELL_FLASH-above-early-return ordering invariant. - src/Bench_urgent.ahk: bugn-bench --scenario urgent runs the round-trip end-to-end against real bug.n state — spawns a cmd, lets the real shell hook manage it, calls real FlashWindowEx, asserts urgency flags flip and Win+U jumps to the urgent view. The bench-style test caught two production-only bugs the Yunit harness masked: Manager_managedWndIds is hex-formatted in production (decimal in synthesized tests), and Window_#%wndId%_isUrgent was never initialized as a global so dynamic writes from Manager_markUrgent created locals that vanished on return. Manager_isManaged now does numeric comparison and returns the stored-key form so dynamic-var lookups in Manager_markUrgent match Manager__setWinProperties' format. Side fixes pulled in along the way: - Window_isHung guards against wndId=0 (SendMessage ahk_id 0 throws). - Monitor_activateView guards Window_set(0, "AlwaysOnTop", "Off"). - Bar_updateView / Bar_updateTitle no-op when Bar_initialized is unset so the Yunit harness can run them headless. - Window_getHidden extracted to its own file so the test runner can swap in a controllable stub (matches the View_arrange pattern). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an “urgent view” concept to bug.n: views containing a flashing managed window are highlighted (red), and Win+U cycles to the next urgent view while clearing urgency upon activation. This is integrated at the shell-hook layer (HSHELL_FLASH) and supported by both Yunit tests and a new real-OS bench scenario.
Changes:
- Handle
HSHELL_FLASHby marking non-active views urgent and addWin+U(#u) to jump/cycle through urgent views. - Clear urgency on view activation and render urgent views with a distinct bar color theme.
- Add coverage: new Yunit suite + a new
bugn-bench --scenario urgentend-to-end scenario; extractWindow_getHiddento enable test stubbing.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/flash-window.ps1 | Helper script to trigger HSHELL_FLASH via FlashWindowEx for manual smoke testing. |
| tests/test_Manager_urgentView.ahk | New Yunit suite covering urgent marking, clearing on activation, dispatch ordering, and Win+U cycling. |
| tests/stubs_io.ahk | Adds a controllable stub for Window_getHidden to validate dispatch ordering. |
| tests/run.ahk | Registers the new urgent-view test suite in the runner. |
| src/Window_getHidden.ahk | Extracts Window_getHidden into its own file to support stubbing in tests. |
| src/Window.ahk | Removes inlined Window_getHidden impl; adds Window_isHung(0) guard. |
| src/View.ahk | Initializes per-view ..._isUrgent state. |
| src/Monitor.ahk | Clears urgency when activating a destination view; guards Window_set(0, ...). |
| src/Manager.ahk | Adds Manager_isManaged, urgent dispatch for HSHELL_FLASH, and Win+U cycling logic. |
| src/Main.ahk | Includes the new Window_getHidden.ahk implementation. |
| src/Config.ahk | Adds urgent-view hotkey and updates default palette for urgent highlighting. |
| src/Bench_urgent.ahk | New real-OS end-to-end urgent scenario exercising shell hook + FlashWindowEx. |
| src/Bench_main.ahk | Adds --scenario urgent plumbing and includes Bench_urgent.ahk. |
| src/Bar.ahk | Adds headless guards for update functions and renders urgent views using palette #3. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Closes the urgency loop introduced by the urgent-view feature: when Claude Code is waiting on user input (permission prompt, idle await), its Notification hook flashes the alacritty terminal hosting that session, which bug.n picks up via HSHELL_FLASH and lights the holding view red on the bar. tools/flash-window.ps1 gains an -AncestorProcess mode that walks up the parent process chain from the hook's PowerShell process (hook shell → claude.exe → ... → alacritty.exe) and flashes a top-level window owned by that ancestor's PID. The walk is needed because the Notification hook payload doesn't include a terminal PID — only session_id, cwd, etc. The existing -Process / -TitlePattern modes are unchanged. tools/claude-attention-hook.md documents the integration and the ~/.claude/settings.json snippet for setting it up on a new machine, including how to override -AncestorProcess for non-alacritty terminals. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bar_updateStatus and Bar_updateView read indexed globals like Config_backColor_#3_#1 (view slot) and Config_backColor_#3_#8 (batteryStatus slot), produced by Config_initColors's StringSplit on ';'. If a future edit to the palette strings drops or adds a ';', slot 8 silently shifts and the wrong color reaches Bar_updateStatus. tests/test_Config_urgentPalette.ahk locks the slot mapping for all three palette colors (back/fore/font, palette #3) — slot count = 9, slot 1 = urgent fill, slot 8 = batteryStatus, slots 2-7 empty. Negative-tested by dropping one ';' from the production string: the suite fails with slot count = 8 and 'ff8040' at slot 7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Monitor_activateView cleared urgency on the destination view only for aMonitor, not for the other monitors that Config_syncMonitorViews=1 pulls onto the same view. The result: those synced monitors land on view i as their *active* view but with View_#m_#i_isUrgent still True, so the bar renders the active view with the urgent palette. Move the urgency-clear into the Loop, % n body keyed by m so every synced monitor is cleared. Single-monitor and syncMonitorViews=0 behavior is unchanged (n = 1, m = aMonitor — same effect as before). Add a regression test (ActivateView_SyncMonitorViews_ClearsUrgencyOn AllSyncedMonitors) that fails against master and passes against the fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Manager_onShellMessage called Manager_isManaged for every shell message, but the result was only consumed by the HSHELL_FLASH dispatch on the next line. Manager_isManaged scans Manager_managedWndIds with StringTrimRight + Loop, PARSE — O(n) over managed windows. Shell messages fire on every activate/create/destroy/redraw, so the scan ran dozens of times per minute only to be discarded. Move the call inside the (wParam = HSHELL_FLASH) And lParam branch. Semantics unchanged: the existing ShellHook_HSHELL_FLASH_* tests cover all three flash branches (managed dispatch, hidden window dispatch, unmanaged ignore) and pass against the refactor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
HSHELL_FLASH), its non-active views light up red on the bar;Win+Ucycles through urgent views, clearing each on activation.bugn-bench --scenario urgentreal-OS round-trip that spawns a cmd, lets the real shell hook manage it, calls realFlashWindowEx, and asserts the urgency state andWin+Ujump.Window_isHung(0)guard,Window_set(0, …)guard inMonitor_activateView, headless guards inBar_updateView/Bar_updateTitle, andWindow_getHiddenextracted into its own file (matchesView_arrangestub pattern).Bugs the bench-style test caught that Yunit didn't
Manager_managedWndIdsis hex-formatted in production (0x...) but Yunit synthesized decimal — so the originallParam-to-decimal "fix" passed Yunit and broke production. Replaced withManager_isManageddoing numeric comparison and returning the stored-key form.Window_#%wndId%_isUrgentwas never initialized as a global byManager__setWinProperties, so the dynamic write inManager_markUrgentcreated a function-local that vanished on return. Yunit masked it becauseBegin()pre-initialized that global. Fix: initialize it at manage time, clear at unmanage time.Test plan
.\test.bat→ 49/49 green (Yunit + dispatch integration)..\dist\bugn-bench.exe --scenario urgentexits 0 and the log shows "PASS — real-OS HSHELL_FLASH → Manager_markUrgent → Manager_activateUrgentView round-trip verified"..\tools\flash-window.ps1 -TitlePattern "Notepad"; verify the view's bar button turns red andWin+Ujumps to it.Win+Ucycles through.🤖 Generated with Claude Code