Skip to content

feat(dock): BarShelf Dock — widgets, launchers, profiles, Apple Dock layouts (R15) - #94

Open
jiunbae wants to merge 9 commits into
mainfrom
feat/r15-dock
Open

jiunbae wants to merge 9 commits into
mainfrom
feat/r15-dock

Conversation

@jiunbae

@jiunbae jiunbae commented Oct 7, 2026

Copy link
Copy Markdown
Member

R15 (plan: #93). All options, off by default.

What

  • BarShelf Dock: a non-activating panel on the bottom/left/right edge. Apps (running dots, drop files to open with), folders (colour + letters tile, contents menu with subfolders), files, links, Shortcuts, spaces, dividers, Trash, open apps not in the profile, and live BarShelf widgets. Classic (glass / Liquid Glass, hover labels, magnification) or Shelf style. Auto-hide. Drag in to add, drag to reorder, right-click to remove.
  • Modes: Off / Alongside the Apple Dock / Instead of the Apple Dock. Hiding backs up autohide/autohide-delay into dock.json and restores them on mode change, quit, next launch after a crash, or barshelf dock restore-apple-dock.
  • Profiles: items + optional saved Apple Dock layout (verbatim persistent-apps/persistent-others; prior layout backed up to dock-backups/, last 10; Dock restarted only when the layout differs) + optional popup page.
  • Switching: ⌃⌥1–9 (Carbon, no permission), swipe / ⌘-scroll on the dock, BarShelf menu ▸ Dock (status item and popup ⋯), barshelf://dock?profile=|next|previous, barshelf dock list|use|next|previous. Focus → Shortcuts automation (documented; no public API for the Focus name).
  • Dock widgets are "on screen" for the scheduler like menu bar items; an auto-hidden dock reports none.
  • Hub ▸ Dock page, docs/DOCK.md, README, CLI docs, Korean strings.

Verified

  • swift test: all green, including new Core (DockConfigurationTests, AppleDockTests), app (DockStoreTests: hide/restore/crash/quit transitions against a fake com.apple.dock, profile switching, tiles, deep link) and CLI (DockCommandTests) suites.
  • Ran a dev build in an isolated home (CFFIXED_USER_HOME) in Alongside mode: bottom/Classic and left/Shelf both lay out correctly.
  • Not exercised live: “Instead of the Apple Dock” and Apple Dock layout switching on the real Dock (they rewrite the user's Dock prefs); covered by unit tests with fakes only. Hover magnification, swipe and drag-and-drop not driven by hand yet.

🤖 Generated with Claude Code

jiunbae and others added 2 commits October 7, 2026 15:07
…rofiles, Apple Dock layouts (R15)

An optional dock (off by default) on the bottom, left, or right edge:
apps, folders (colour tiles, contents menu), files, links, Shortcuts,
spaces, dividers, Trash, open apps, and live BarShelf widgets. Classic
(Apple Dock look, glass, magnification) or Shelf style; auto-hide.

Modes: off, alongside the Apple Dock, or instead of it. Replacing hides
the Apple Dock with autohide + a long delay after backing up the user's
own settings; they come back on mode change, at quit, after a crash at
next launch, or with `barshelf dock restore-apple-dock`.

Profiles carry dock items, an optional saved Apple Dock layout (written
back verbatim, previous layout backed up, Dock restarted only when it
differs) and a popup page. Switch with ⌃⌥1–9, swipe or ⌘-scroll on the
dock, the app menus, barshelf://dock?profile=…, or `barshelf dock use`;
Focus follows through a Shortcuts automation.

Dock widgets count as on screen for the scheduler, like menu bar items.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Testing the dock through the accessibility API (nlc) showed the tiles'
right-click menus could not be opened by AXShowMenu: SwiftUI's
context menu does not answer it in the non-activating panel, so
VoiceOver's VO-Shift-M and any automation had no way to Remove from
Dock, Quit, or switch profiles from the dock.

Each tile's menu is now one list of entries drawn twice: as the
context menu and as an NSMenu popped for AXShowMenu (after the action
returns, so the call is not held open by menu tracking).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
jiunbae and others added 7 commits October 7, 2026 16:15
Measured against the Apple Dock on macOS 27, the Classic dock was a
grey slab: SwiftUI's glass follows the window's active state and this
panel is never active, so it drew flat. It now hosts AppKit's clear
NSGlassEffectView, with the Apple Dock's proportions (padding ~0.19
icon, corners ~0.2 of the bar, 3 pt running dots in the padding).

Widgets no longer stretch the bar: in Classic they keep to icon height
and show their name and main reading or two bars (the R14-B summary).
Full cards stay in the Shelf style. A new dock starts at the Apple
Dock's icon size, and settings offer to match it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Driving the dock through accessibility showed AXShowMenu never opened
a tile's own menu: SwiftUI's context menu claims the action and does
nothing in this never-active panel, and an accessibilityAction(.showMenu)
of our own was never called. Menus no longer go through SwiftUI:

- Right-click and control-click are handled by the dock's hosting view,
  which opens the menu of the tile under the pointer, else the bar's.
- Each tile offers the same commands as named accessibility actions
  ("Remove from Dock", "Profile: Work"), how VoiceOver presents a
  SwiftUI view's commands.

Tiles stop advertising a ShowMenu that did nothing. Keeps the nlc flows
used to test this under .context/nlc.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…, indicators, opening bounce, auto-hide delay

- Size and magnification sliders read Small–Large, without 32 ticks.
- Magnification amount (how big a hovered icon grows).
- Show on: the main display, or the display whose dock edge the pointer
  rests at, as the Apple Dock moves between displays.
- Auto-hide waits for the pointer to rest at the edge (Show after), so
  crossing onto a display below does not pull the dock out.
- Animate opening apps: an icon bounces until its app has launched.
- Show indicators for open apps can be turned off.
- The 'same edge as the Apple Dock' warning only with one display; with
  several the Apple Dock moves and the warning was wrong.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Apple Dock follows the pointer between displays, which changes a
display's visible frame with no notification; the BarShelf Dock kept
its old place and ended up under it. The frame is re-checked as the
pointer moves, the only thing that moves the Apple Dock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…key conflicts, menus, cost

From a review of #94:
- Never write an empty Apple Dock layout (it would unpin every app);
  refuse to save one when the Dock's prefs can't be read.
- `barshelf dock restore-apple-dock` asks a running BarShelf instead of
  editing dock.json under it (the app saved its stale copy back).
- Deleting the active profile switches to the next one properly: its
  Apple Dock layout and popup page, not only its items.
- The copyable Focus link names the profile by id, surviving renames.
- Profile names save on Return / leaving the field, not per keystroke
  (trimming ate spaces while typing).
- ⌃⌥ numbers another shortcut owns are marked taken, and retried.
- A right-click after a tile vanished no longer shows its menu.
- ⌘-scroll switches one profile per gesture, not a run of them.
- Icons and names are cached per path; the global mouse monitor runs
  only for auto-hide, following the pointer, or multi-display alongside.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Folders open as a grid above the dock, like the Apple Dock's stacks:
  newest first by date added, subfolders open in place with a way back,
  files drag out. 'Open folders as ▸ List' keeps the menu.
- Show recent apps: up to three apps used lately that are neither in
  the profile nor open. What was frontmost when BarShelf started does
  not count as used.
- BarShelf window sidebar rows answer a press as well as a selection;
  accessibility clients that press (automation, Switch Control) could
  not change pages.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- In a full-screen app the dock waits at the edge and slides out when
  the pointer rests there, like the Apple Dock; before, it never showed
  in a full-screen Space. A full-screen Space is told apart by an
  ordinary window covering the whole screen, menu bar area included
  (window bounds only, no permission). 'Show in full-screen apps'.
- A small registry of the global keys BarShelf's own features claim.
  Automation claims its keys before registering, so a dock ⌃⌥ profile
  key no longer makes the whole Automation script fail to start; the
  dock leaves those keys to Automation (shown in settings) and takes
  them back when Automation stops. Two lines in AutomationEngine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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