Skip to content

Phase 7: lock-screen expand (state L2)#7

Open
tirthfx wants to merge 1 commit into
phase-6-lockscreen-widgetfrom
phase-7-lockscreen-expand
Open

Phase 7: lock-screen expand (state L2)#7
tirthfx wants to merge 1 commit into
phase-6-lockscreen-widgetfrom
phase-7-lockscreen-expand

Conversation

@tirthfx

@tirthfx tirthfx commented Jul 10, 2026

Copy link
Copy Markdown
Owner

What changed

Phase 7 of the dual-surface build: click-to-expand on the lock screen (PRD §3.2 state L2, reference image 2).

  • Clicking the widget's art/track expands: 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 symmetrically; Esc at the widget level dismisses the whole surface.
  • New LockScreenUIState (widget/expanded/lyrics presentation + shared spring, mirroring the notch's NotchUIState), LockScreenExpandedView, and a shared TransportButtons component now reused by both the widget and the expanded view (and ScrubberView continues 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 onto main.

Notes for review — needs your eyes in Xcode

  • Visuals unverifiable headlessly (lock screen blocks synthesized input/screenshots). Confirmed: builds clean, launches without crashing, notch unaffected. Please run it, play a track, ⌃⌘L, then click the album art: expect the wallpaper to dim, art to grow to center, controls pill below; click empty space or Esc to collapse.
  • The expand uses the same .smooth spring as the notch's open/close, so the two surfaces should feel identical.
  • .lyrics currently renders the expanded view as a placeholder — the real lyrics panel lands in Phase 8.

🤖 Generated with Claude Code

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>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
window.onDismiss = { [weak self] in self?.handleEscape() }
window.onDismiss = { @MainActor [weak self] in self?.handleEscape() }

Comment on lines +43 to +49
TransportButtons(
isPlaying: nowPlaying.isPlaying,
onPrevious: onPrevious,
onPlayPause: onPlayPause,
onNext: onNext,
primarySize: 26
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The primarySize parameter is redundant here because TransportButtons already defaults primarySize to 26. Removing it simplifies the call site.

            TransportButtons(
                isPlaying: nowPlaying.isPlaying,
                onPrevious: onPrevious,
                onPlayPause: onPlayPause,
                onNext: onNext
            )

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