Phase 7: lock-screen expand (state L2)#7
Conversation
Clicking the widget's art/track expands to the full view (PRD §3.2 L2, reference image 2): the wallpaper dims toward black, the album art scales up to center via a matched-geometry hero morph, and a frosted control pill (title/artist, scrubber, transport) settles below it. Click-away on the dimmed backdrop or Esc collapses back to the widget; Esc at the widget level dismisses the surface. New LockScreenUIState (widget/expanded/lyrics presentation, shared spring), LockScreenExpandedView, and a shared TransportButtons component reused by both the widget and the expanded view. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces an expanded lock-screen now-playing view (LockScreenExpandedView) and manages transitions between the compact widget and expanded states using a new LockScreenUIState and matched geometry effects. It also refactors transport controls into a shared TransportButtons component. Feedback includes marking the onDismiss closure in PseudoLockScreenController as @MainActor for concurrency safety and removing a redundant default parameter in LockScreenNowPlayingWidget.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| window.isReleasedWhenClosed = false | ||
| window.onDismiss = { [weak self] in self?.hide() } | ||
| window.contentView = NSHostingView(rootView: LockScreenBackdropView(metadata: metadata)) | ||
| window.onDismiss = { [weak self] in self?.handleEscape() } |
There was a problem hiding this comment.
To ensure strict concurrency safety and compatibility with Swift 6, explicitly mark the onDismiss closure as @MainActor. Since PseudoLockScreenController is @MainActor isolated, calling handleEscape() from a non-isolated closure context can trigger compiler warnings or errors.
| window.onDismiss = { [weak self] in self?.handleEscape() } | |
| window.onDismiss = { @MainActor [weak self] in self?.handleEscape() } |
| TransportButtons( | ||
| isPlaying: nowPlaying.isPlaying, | ||
| onPrevious: onPrevious, | ||
| onPlayPause: onPlayPause, | ||
| onNext: onNext, | ||
| primarySize: 26 | ||
| ) |
There was a problem hiding this comment.
What changed
Phase 7 of the dual-surface build: click-to-expand on the lock screen (PRD §3.2 state L2, reference image 2).
LockScreenUIState(widget/expanded/lyrics presentation + shared spring, mirroring the notch'sNotchUIState),LockScreenExpandedView, and a sharedTransportButtonscomponent now reused by both the widget and the expanded view (andScrubberViewcontinues to be shared with the notch).Why
Continues Surface B toward the reference flow: L1 widget (done) → L2 expand (this) → L3 swipe-to-lyrics (Phase 8).
Base
Stacked on
phase-6-lockscreen-widget(open PR #6, verified/unmerged). Diff is Phase 7 only. Merge the stack (5 → 6 → 7) in order, or rebase ontomain.Notes for review — needs your eyes in Xcode
.smoothspring as the notch's open/close, so the two surfaces should feel identical..lyricscurrently renders the expanded view as a placeholder — the real lyrics panel lands in Phase 8.🤖 Generated with Claude Code