Zonogy

DockMenus

DockMenus adds Dock integration to Zonogy, providing an ultra-fast “peek and switch” for an app’s windows directly from the Dock.

Hover → DockMenu

Performance requirements:

DockMenu Presentation

DockMenu Contents (Mini Launcher “Drilled Down”)

Actions

Clicking the Dock App (Interception)

When the user left-clicks a Dock app without Shift:

Modifiers:

Dragging the Dock App (Interception)

When the user drags a Dock app icon without Shift/Control:

Modifiers:

Clicking Inside DockMenu

Clicking items inside DockMenu performs the same action as clicking the corresponding item in the Launcher:

Dragging Window Entries from DockMenu

Window entries in the DockMenu can be dragged directly to zones. This uses the same overlay UI and drop targets as dragging actual windows (see Dragging Windows Between Zones in SPECIFICATION.md).

Dismissal / Lifetime

DockMenu dismisses when:

Settings

Implementation Notes

Hover Detection

Debouncing

Panel Positioning

Dock Visibility Tracking

Click Interception

Dock Icon Drag Interception

Window Selection Semantics

DockMenu differs from Launcher in how window selection works:

Accessibility API Workarounds

AXSelectedChildrenChanged Does Not Signal Cursor Exit

The Dock’s AXSelectedChildrenChanged notification fires when:

However, when the cursor leaves the Dock entirely, AXSelectedChildrenChanged may fire with the same selectedChildren as before (the last hovered item), not an empty selection. Additionally, AXSelectedChildrenChanged with empty selection can fire at unpredictable times unrelated to user interaction.

Consequence: We cannot rely on AX notifications to detect when the cursor leaves the Dock. DockMenu dismissal must be driven by cursor-in-region checks (Dock ∪ panel), not by AX hover-end.

Cursor Region Polling

Because the Dock may prevent Zonogy from receiving reliable mouse enter/exit events, dismissal uses a lightweight polling timer while the panel is visible:

Re-establishing the Observer After the Dock Rebuilds or Relaunches

The Dock seems to rebuild its accessibility tree in place (same process) at times. The AXList the hover observer is attached to can then go “stale but alive”: it still answers position and size and still fires AXSelectedChildrenChanged, but reports an empty selection on hover, so hovering Dock icons silently stops producing DockMenus. The Dock process can also exit entirely and relaunch (a crash, or killall Dock); because the hover observer is bound to a specific Dock process, a relaunch leaves it bound to a dead process and hover stops working. Zonogy re-discovers and re-attaches the observer on these signals:

Each re-establish builds the replacement first and swaps only on success, so a transient failure keeps a working observer rather than leaving DockMenus with none — except when the bound Dock process exits, where the old observer is bound to a dead process and is dropped up front. Because a freshly relaunched Dock is often not observable the moment it is detected, an attempt that finds no usable AXList retries a bounded number of times before giving up.