Collect other apps' menu bar items into a hamburger panel
The menu bar icon is now a hamburger whose panel gathers every other
app's status item via the Accessibility API, so tray icons no longer
need to be hunted down across the menu bar. Items can be opened from
the panel, and a best-effort toggle pushes them off the real menu bar
so they only live there.
💘 Generated with Crush
Assisted-by: Crush:deepseek-v4.1-flash
This commit is contained in:
@@ -14,12 +14,31 @@ executable SwiftPM package, no external dependencies (all Apple frameworks).
|
||||
splits them by `activationPolicy` into `regularApps` and `backgroundApps`
|
||||
(both sorted by name). Subscribes to the `NSWorkspace` launch/terminate/
|
||||
activate/hide/unhide notifications to refresh automatically.
|
||||
- `Sources/SimpleTopBar/TopBarView.swift` — the panel. `section(...)` renders
|
||||
the regular apps; `backgroundSection` is the collapsible dropdown for
|
||||
- `Sources/SimpleTopBar/TopBarView.swift` — the panel. `menuBarSection` is the
|
||||
hamburger tray of other apps' menu bar items; `section(...)` renders the
|
||||
regular apps; `backgroundSection` is the collapsible dropdown for
|
||||
background apps. `AppRow` shows icon + name + active dot with a right-click
|
||||
Activate/Hide/Quit menu.
|
||||
- `Sources/SimpleTopBar/MenuBarItems.swift` — `MenuBarItemManager` enumerates
|
||||
other apps' status items via the Accessibility API (`kAXExtrasMenuBarAttribute`
|
||||
→ `kAXChildrenAttribute`) and presses them (`kAXPressAction`, falling back to
|
||||
`kAXShowMenuAction`). `MenuBarHider` owns the zero-width spacer `NSStatusItem`
|
||||
used to push items off the menu bar. `AccessibilityPermission` wraps
|
||||
`AXIsProcessTrusted` / `AXIsProcessTrustedWithOptions`.
|
||||
- `Sources/SimpleTopBar/SelfTest.swift` — `runSelfTest()` powers `--self-test`.
|
||||
|
||||
## The menu bar tray (Accessibility)
|
||||
|
||||
- Requires Accessibility permission. `MenuBarItemManager.refresh()` short-circuits
|
||||
to an empty list when `AXIsProcessTrusted()` is false, and `TopBarView` shows a
|
||||
"Grant Accessibility…" button in that state. Never assume permission.
|
||||
- Enumeration must be resilient: `AXUIElementCopyAttributeValue` can fail per-app
|
||||
or per-element, so every helper returns `nil` instead of force-unwrapping. The
|
||||
`enumerate()` routine is `nonisolated static` so `--self-test` can run it.
|
||||
- Items are keyed by `pid-index`; `MenuBarItem` equality ignores the AX element.
|
||||
- Reading another app's UI is the only TCC permission needed; there is no Screen
|
||||
Recording or Apple Events usage.
|
||||
|
||||
## The background dropdown
|
||||
|
||||
This is the core feature. Requirements:
|
||||
@@ -68,7 +87,8 @@ version argument and pass the same version to `release.sh`.
|
||||
the single-instance guard terminates stale copies with the same bundle id.
|
||||
- Reading `activationPolicy` is reliable but must happen on the main thread;
|
||||
keep all `NSWorkspace` access inside the model.
|
||||
- Don't add Accessibility/Screen Recording permission code: this app only reads
|
||||
the running-app list, which needs no TCC grants.
|
||||
- The menu bar tray needs Accessibility permission; do not add Screen Recording
|
||||
or Apple Events usage. `MenuBarHider` must always be restored on quit, but
|
||||
because a status item dies with its process, a crash cleans up on its own.
|
||||
- `.gitignore` covers `.build/`, `SimpleTopBar.app/`, `dist/` — build artifacts
|
||||
should never be committed.
|
||||
|
||||
Reference in New Issue
Block a user