Skip to content

Add Leave space for Dock and stop following the auto-hidden Dock - #168

Merged
LMC-Karma merged 2 commits into
mainfrom
feat/leave-space-for-dock
Oct 15, 2026
Merged

LMC-Karma merged 2 commits into
mainfrom
feat/leave-space-for-dock

Conversation

@LMC-Karma

@LMC-Karma LMC-Karma commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Problem

With Dock auto-hide on, BetterTile tried to follow the Dock: it shrank the work
area while the Dock was shown and grew it back after the Dock hid. Both steps
were unreliable. It noticed the Dock only through a 10-second backup check, so
a layout tiled while the Dock was shown kept an 82-point gap for 15 to 20
seconds after the Dock hid. Repair Bento in that time rebuilt the gap from the
stale work area.

Change

Settings → Window Layout → Window Mode gets Leave space for Dock, off by
default.

  • Off: BetterTile uses the work area macOS reports. With auto-hide on, that is
    the full screen, and the Dock draws over windows when it slides in.
  • On: the Dock's strip stays free on its display, whether the Dock is shown or
    hidden and whether auto-hide is on or off. Layouts do not move when the Dock
    slides in or out.

A Dock that never hides gets the strip with the setting on or off. macOS
reserves the same strip, but AppKit inside a running BetterTile can report a
secondary display's work area late after the Dock moves there or changes edge.

The strip is the thickness of the Dock's Accessibility list plus 6 points. The
list keeps its size while the Dock is hidden. A Dock not shown since it
started, as after login, reports no size, so the thickness last measured on
each edge is remembered across launches. The setting applies to Bento, Tabbed,
snaps, and Maximize. Changing it lays out Bento and Tabbed again at once.

The code that followed the Dock is removed: a global mouse-move monitor, burst
and backup timers, WindowServer Dock window scans, and repeated Dock
Accessibility scans (about 400 lines). docs/ARCHITECTURE.md and SECURITY.md
describe the new reads.

Evidence

  • Before (main, live, 2560×1440, auto-hide on, Bento): with the Dock shown
    for 13 seconds the layout shrank to 1359, leaving an 82-point gap. After the
    Dock hid, the gap stayed for 15 to 20 seconds. Repair Bento with the Dock
    hidden rebuilt the gap, which cleared about 25 seconds later.

  • After, live, setting off: the layout stayed at full height through Dock
    show, hide, and Repair.

  • After, live, setting on (2560×1440 and 1210×812):

    • Bento and Tabbed stayed at the Dock strip through Dock show, hide, and
      Repair. The switch in Settings laid them out again both ways at once.
    • Maximize, with the Dock hidden or shown: ends at the Dock strip.
    • Auto-hide on and off give the same area.
    • Dock on the left and on the right: same area as macOS reserves.
    • Dock icon size changed while running: the old strip stayed until the Dock
      showed, then the new strip matched macOS exactly.
    • Relaunch after a Dock restart: strip in place before the Dock was shown.
  • After, live, three displays side by side (2560×1440 main plus two
    BetterDisplay screens), Native, Bento, and Tabbed:

    • Auto-hide on, setting on: the strip follows the Dock to each display it
      moves to and leaves the others at full height. Existing Bento and Tabbed
      layouts follow about 3 seconds after the Dock moves.
    • Dock shown and hidden, and Repair, on the Dock's display: no change.
    • Settings switch: Tabbed on the Dock's display goes from 1362 to full height
      and back. The other displays do not change.
    • Auto-hide off: each display matches what macOS reserves, with the setting
      on or off.
    • Left Dock on the main display and right Dock on the rightmost display:
      strip on that display only.
    • Relaunch with the Dock hidden on a secondary display: strip on that display
      before the Dock is shown.
    • Secondary display disconnected while the Dock is on it: the Dock and the
      strip move to the main display.
  • Regression found and fixed (ee3026e): with the setting off, a right Dock that
    never hides on the rightmost secondary display, Maximize there gave 2560
    wide, under the Dock, while macOS reported 2482. main gave 2478 through the
    old Dock following. Now it gives 2481.

  • The Dock's list thickness against what macOS reserves for a Dock that never
    hides, measured on macOS 26:

    Icon size List macOS reserves
    36 50 56
    52 72 78
    80 100 106
    52 (shrunk to fit 1210 pt) 66 72
    30, side Dock 44 50
    36, side Dock 44 51
  • Checks run and results:

    • swift test passes (all three test targets, run three times).
    • Both Debug xcodebuild builds succeed, signed and with
      CODE_SIGNING_ALLOWED=NO.
    • New tests cover the measured strips, choosing the Dock's display, the
      remembered thickness, the setting's default and saving, and Bento laying
      out again when the setting changes.

Limits

  • Checks not run and why:
    • Moving a Dock that never hides between displays by pointer: synthetic
      pointer events cannot push past a screen edge. The Dock was moved with
      auto-hide on, then auto-hide was turned off.
    • The remembered thickness after login could not be staged live, because
      any app launch, including BetterTile's own, makes the Dock report its size.
      A unit test covers it.
  • Remaining uncertainty:
    • The list size is rounded to whole points, so a side Dock can differ from
      macOS by one point.
    • The very first use on a Mac, before the Dock has ever been shown, adjusts
      the layout once when the Dock first appears.
    • The app-model test waits for the layout to settle before turning the
      setting off. A quick on-and-off is fixed separately in Lay out Bento again when the work area changes back before settling #167.
    • Found, not changed here, also on main: an existing Tabbed layout does not
      refit when a side Dock narrows its display. It logs "placement not
      started". Entering Tabbed again lays it out correctly.

Safety

  • Accessibility: reads the position and size of the Dock's list, at most once
    a second, while the setting is on or the Dock never hides. Reads no item
    names. Uses the existing Accessibility permission. Also reads the Dock's
    orientation and autohide preferences.
  • Stored data: the last Dock thickness for each edge, in BetterTile's own user
    defaults. Disclosed in SECURITY.md.
  • Removed: a global mouse-move monitor and the WindowServer Dock window scans.
  • No new permissions, private APIs, networking, or dependencies.

🤖 Generated with Claude Code

BetterTile followed an auto-hidden Dock as it slid in and out. It noticed
the Dock only through a 10-second backup check, so a layout tiled while
the Dock showed kept an 82-point gap for 15-20 seconds after the Dock hid,
and Repair Bento rebuilt the gap from the stale work area.

Replace the following with one setting, off by default:
- Off: use the work area macOS reports. A hidden Dock draws over windows.
- On: keep the Dock's strip free on its display whether the Dock is shown
  or hidden, and whether auto-hide is on or off.

The strip is the Dock's Accessibility list thickness plus 6 points, which
matches what macOS reserves for a Dock that never hides. The list keeps its
size while hidden. A Dock not shown since it started reports no size, so
the thickness last measured on each edge is remembered across launches.

This removes the global mouse-move monitor, burst and backup timers, and
repeated Dock scans that the following used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 15, 2026

Copy link
Copy Markdown

Review summary

Checked the diff against the PR body (problem/change/evidence/limits/safety) and against AGENTS.md, SECURITY.md, and docs/ARCHITECTURE.md. No confirmed problems — the change matches its stated goal and the repository's rules.

Notes (not faults, just what I verified):

  • DockSpaceReader throttles its Accessibility/UserDefaults reads to once a second as claimed, and the remembered-per-edge-thickness fallback correctly distinguishes "Dock restarting on the same edge" (keep last reading) from "edge changed with nothing remembered" (drop to no reservation) — both covered by DockSpaceTests.swift.
  • leavesSpaceForDock is wired through ConfigurationChangeSet.workArea into the same scheduleDisplayRefresh() path used for display reconfiguration, so toggling it retiles Bento/Tabbed immediately in both directions, matching the "lays out again at once" claim and DockSpaceModelTests.swift.
  • The new code stays inside BetterTileMacOS, converts through BTRect at the macOS boundary, and uses only public AX/UserDefaults APIs — no leakage into BetterTileCore, no private-API disclosure needed.
  • The "falls back to the main display when the Dock hasn't been shown since boot" behavior (relevant on multi-display setups) is intentional and disclosed in both docs/ARCHITECTURE.md and the PR's Limits section, and is covered by a unit test — not a hidden defect.
  • The ~400-line removal of the old mouse-monitor/timer/WindowServer-scan dock-following code checks out against the deleted files.

I did not execute swift test / xcodebuild myself (no macOS toolchain available in this review environment) — relying on the PR body's reported three-times-green swift test run and static review of the diff.

Multi-display live testing found a regression. With a Dock that never hides
on the right edge of a secondary display, AppKit inside BetterTile kept
reporting that display's full width after the Dock moved there. The old Dock
following hid this by reading the Dock itself. Without it, windows went
under the Dock.

Apply the Dock's strip when Leave space for Dock is on, or when the Dock
never hides. macOS reserves the same strip for a Dock that never hides, so
this only corrects a late AppKit value. With the setting off and the Dock
hiding automatically, the Dock list is still not read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 15, 2026

Copy link
Copy Markdown

Review summary

Compared the diff against the PR body (problem, change, evidence, limits, safety) and against AGENTS.md/docs/ARCHITECTURE.md/SECURITY.md/CONTEXT.md. No confirmed problems found — the change matches its stated goal and the repository rules.

Specifics checked:

  • Architecture: DockSpace.swift lives in BetterTileMacOS (not Core) and imports only AppKit/ApplicationServices; BetterTileCore gains no AppKit/Accessibility dependency. The new leavesSpaceForDock state is applied through BetterTileWindowSystem and only read in AccessibilityWindowSystem.displays(), consistent with "convert AppKit's bottom-left coordinates at the BetterTileMacOS boundary." docs/ARCHITECTURE.md's coordinate-model paragraph is updated to match.
  • Window mutation path: this change affects reported visibleFrame, not direct window writes; it does not bypass WindowCoordinator.
  • Security: only public APIs are used (UserDefaults(suiteName:), public Accessibility calls) — no private symbols, so the private-API design-review requirement in SECURITY.md does not apply. SECURITY.md is updated to disclose the new Dock preference/list reads, the once-a-second cadence, the absence of item-name reads, and the new UserDefaults storage of remembered thickness, matching the code in DockSpaceReader.
  • Tests: traced DockSpaceReader.footprint/read logic by hand against DockSpaceTests.swift (geometry, display assignment at seams, empty-list handling, remembered-thickness fallback, setting on/off transitions) and DockSpaceModelTests.swift/ConfigurationTests.swift (retiling both ways, migration default, change-set detection) — all traces matched the asserted expectations. Both the on→off and off→on transitions force an immediate re-read via the whileHidden != lastWhileHidden check, so the switch takes effect without waiting on the 1-second cadence, matching the "laid them out again both ways at once" claim.
  • Removed code: the deleted DockFootprintMonitor/stabilizer/lease machinery (mouse monitor, burst/backup timers, WindowServer scans) has no remaining call sites; BetterTileWindowSystem protocol, AccessibilityWindowSystem, and the fake test system were all updated together, and the deleted unit tests correspond to removed behavior (several have direct successors in DockSpaceTests.swift).

The limits already disclosed in the PR body (no live second-display check, first-ever-login case, the quick on/off issue tracked separately in #167) are tooling/environment constraints, not hidden gaps — no further action needed here.

@LMC-Karma
LMC-Karma merged commit 14d523c into main Oct 15, 2026
4 checks passed
@LMC-Karma
LMC-Karma deleted the feat/leave-space-for-dock branch October 15, 2026 23:21
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.

1 participant