Skip to content

Pulse active provider icons in the menu bar - #313

Open
ozgurasan-agent wants to merge 1 commit into
vinzdg:mainfrom
ozgurasan-agent:codex/provider-activity-pulse
Open

ozgurasan-agent wants to merge 1 commit into
vinzdg:mainfrom
ozgurasan-agent:codex/provider-activity-pulse

Conversation

@ozgurasan-agent

Copy link
Copy Markdown

Summary

Fix the provider activity indicator in the top macOS menu bar. Existing provider monitors already normalized real work as busy, but the closed NSStatusItem never received that state and its artwork had no activity presentation.

This change forwards the existing provider-specific AgentSession state to the status item and pulses only the matching visible provider glyph while actual work is in progress.

Changes

  • Feed ActivityCoordinator session updates to the existing StatusItemController instance.
  • Treat only .busy sessions as active; idle, waiting, success, and an open process without work remain static.
  • Keep activity provider-specific and intersect it with providers already visible in the menu bar.
  • Render active glyphs as pass-through AppKit overlays with a Core Animation opacity pulse.
  • Preserve animation phase across usage/countdown artwork rebuilds.
  • Respect macOS Reduce Motion and the existing menu-bar visibility/master settings.
  • Add tests for independent provider state and the pulse animation contract.

Animation

  • Provider glyph only; percentage, countdown, weekly ring, separators, and other providers stay static.
  • Smooth 1.0 -> 0.62 -> 1.0 opacity cycle over 1.2 seconds.
  • Runs in Core Animation rather than an application-side redraw timer.

Verification

  • make test on current origin/main: 1,737 tests, 3 skipped, 0 failures.
  • git diff --check.
  • Real Codex task: normalized busy state reached the status item and only the Codex glyph pulsed.
  • Real interactive Claude task: Claude and Codex pulsed concurrently and independently.
  • Claude completion: Claude pulse stopped while the idle CLI process remained open; Codex continued pulsing.
  • Usage/countdown changes did not stop the active pulse.
  • Menu open/dismiss continued to work while pulsing.
  • Master menu-bar limit setting retained the default static icon when disabled.

Notes

No second activity-monitoring system is introduced. Providers without a reliable repository activity monitor remain static rather than inferring activity from process existence or usage refreshes.

@vinzdg

vinzdg commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Thanks @ozgurasan-agent — this is the right answer to the objection I raised on #297. It adds no poller and no timer at all: it hangs off the ActivityCoordinator callback that already feeds the notch (AppDelegate.swift:771), and it's gated on the status item existing rather than on a provider being connected, so a notch-only user pays nothing. That's exactly what I asked for.

One change before I merge it:

Use #297's StatusItemPulse mask instead of the overlay. #313 lays a separate NSImageView over the button (StatusItemController.swift:223) and hand-tints it with button.contentTintColor ?? .labelColor (:231). AppKit inverts the status button's template image while the menu is open and tints it for light/dark/wallpaper-tinted bars — a hand-tinted subview won't follow any of that, so the pulsing glyph will be the wrong colour exactly when someone is looking at it. #297 already solves this properly: StatusItemPulse puts a CALayer mask on the button's own layer, so AppKit keeps tinting the real template and only the mask's alpha moves.

Everything else here is good — rebuilding the image only when the active-provider set changes, keeping pulseBeganAt across countdown rebuilds so the cycle doesn't restart every minute, and observing reduce-motion live.

I'm keeping #297 open, and I've said so there. The pollers were about twelve lines of it; the Copilot, OpenCode, Ollama and LM Studio activity sources and the ProviderActivityBoard abstraction are the valuable part and I do want them — rebased onto this, with those sources gated the way this PR gates.

Note #299 and #298 are merged, so AppDelegate.swift and StatusItemController.swift have both moved under you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants