Lock screen first pass: click-swallow fix and locked-state skeleton
First pass on lock-screen support (VISION item 3): investigation, the post-unlock click-swallow fix, and a locked-state skeleton. No settings UI yet (deferred until the Raycast integration exists).
Changes
docs/lock-screen.md: how lock/unlock is detected, how the SkyLight and CGS notch spaces interact, what the notch exposed while locked, the click-swallow bug, what we take from upstream #1346, hook points, and a manual test checklist.- Click-swallow fix (
NotchWindowManager,BoringNotchSkyLightWindow,CGSSpace). SkyLight delegation (flag 7) evicts the window from our notch space, and nothing re-added it. After unlock we now disable SkyLight, re-add the window to the notch space, then swap every window that was ever delegated for a fresh one. The liveNSHostingViewmoves across with a snapshot underneath. Adapted from upstream PR #1346 by @TemujinCalidius. - Locked-state skeleton:
BoringViewCoordinator.isScreenLockedis the single source of lock state. It's re-checked at launch, on wake and when the user session becomes active again (private/SessionLockState.swift, undocumented key).LockScreenFeature/LockScreenVisibilitypolicy with one Defaults key per feature: music and battery visible; notifications, calendar, shelf and mirror hidden.- While locked the notch can't open, windows ignore the mouse, drag detectors stop, and hidden alerts are filtered.
Private API (SkyLight, CGS, undocumented session key) is marked in the code.
⚠️ Needs rebasing onto compact-only (#1)
PR #1 rewrites ContentView into a panel layout. Once it merges, this branch needs main merged in again and its two ContentView hooks re-applied to the panel layout:
liveActivities:coordinator.isVisible(.notifications)/coordinator.isVisible(.music).- Open content:
vm.notchState == .open && !coordinator.isScreenLocked.
Verification
scripts/dev.sh build: BUILD SUCCEEDED (after mergingorigin/mainat785298c).- Not verified at runtime. The app wasn't launched and the screen wasn't locked. Whether the click-swallow bug actually reproduces, and whether the window swap fixes it, needs a manual test on a real lock screen. See the checklist in
docs/lock-screen.md.
🤖 Generated with Claude Code
Summary by cubic
First pass on lock-screen support: fixes a post-unlock click-swallow bug and adds a locked-state skeleton with a per-feature visibility policy. No settings UI yet. The fix is adapted from upstream PR #1346, isn't runtime-verified yet (manual test checklist in docs/lock-screen.md), and needs a rebase onto the compact-only ContentView rewrite to re-apply two hook points.
Click-swallow fix
- SkyLight delegation evicts the notch window from our CGS notch space and leaves server-side state that makes its transparent margins swallow desktop clicks after unlock.
- After unlock the window is re-added to the notch space, then swapped for a fresh one; the live
NSHostingViewmoves across behind a snapshot so the notch never goes blank.
Locked-state skeleton
BoringViewCoordinator.isScreenLockedis the single lock-state source, seeded at launch and re-checked on wake and session-active via an undocumented session key.- Per-feature policy (
LockScreenFeature/LockScreenVisibility): music and battery visible; notifications, calendar, shelf, and mirror hidden, oneDefaultskey each. - While locked the notch can't open, open notches close, windows ignore the mouse, drag detectors stop, and hidden alerts are filtered.