cmux-next: Picture in Picture for browser tabs, and a media card like macOS's
Merged
TE
16d ago+1720+1720+0123456789012345678901234567890123456789-172-172-012345678901234567890123456789
Leo asked for Picture in Picture for browser tabs, with the media card brought up to the macOS reference.
What it does
- One action, every entrypoint.
tab.pictureInPicture("Picture in Picture") backs the media card's new button, the tab menu (Options), the card's right-click menu, the palette,cmux media picture-in-pictureand the MCP toolapp_media_picture_in_picture. With a named tab it acts on that tab. With no tab named, it picks the focused tab if it shows a video, then the tab whose video plays (the one the card shows, in any workspace), then the focused tab. The card's right-click menu passesnowPlayingCard, so its row always acts on the tab the card shows and reads Exit Picture in Picture while that video floats. A CLI or MCP run gets "No video" or "can't play in Picture in Picture" back as the run's answer; a palette or menu run shows it as a notice. - Both engines. A page-world script runs as a user gesture in both engines (CEF
Runtime.evaluatewithuserGesture, WebKitevaluateJavaScript). It callsrequestPictureInPicture()on the playing video of the page and its same-origin frames, and falls back to WebKit'swebkitSetPresentationMode. If a video already floats, it brings it back. The page'sdisablePictureInPictureis respected. Chromium (Chrome runtime style) and WebKit each draw their own always-on-top PiP window, and closing it returns the video to the tab. A site's own PiP button already worked in both engines (scripts/cmux-next/now-playing-live.py, step 3); this PR leaves that path as it is. - Keeps playing. The media report gains
pip. Hibernation's probe counts a floating page as playing (the page script andtab.state.media.isPictureInPicture), so a paused or muted floating video is never frozen or discarded after its tab hides or another workspace shows. - Media card like macOS's. The site sits over the title and artist. The artwork fills the top right and fades into the card's glass (
CardSurfaceView, unchanged and never opaque). Previous, Play or Pause and Next start at the leading edge, then a divider and Picture in Picture for a video. The play disc and the scrubber use the theme accent (Palette.accentFill/onAccent; with no accent chosen that is the text color, as before). The level bars move to the end of the controls row. - The toolbar's media hub rows get the same PiP button for a video.
- Strings in all 21 locales. A What's New highlight is included.
- No default key yet. The defaults guard needs a call owner other than the author to approve a new default key, so ⌥⇧⌘P comes in its own small PR. Until then the action can be bound by id in Settings › Keyboard Shortcuts or
cmux.json.
Changelog
- Added: Picture in Picture for browser tab videos from the media card, the tab menu and the command palette.
- Changed: The media card shows the site, larger artwork and a Picture in Picture button, in the theme's accent.
Known limits
- A video inside a cross-origin frame (an embedded YouTube player) shows the row, but the toggle cannot reach it and answers "No video". The page script only sees the page and its same-origin frames.
- The toggle answers "entered" once it asked for PiP; a page that then rejects the request (no video track yet) fails silently.
Observed / Not run
- Observed:
prepush-gate.pyreports no new violations. - Observed: the fleet export (
scripts/measure/export-action-surfaces.sh) at ec2f307 (the latest feat-cmux-next merged in) passed everyActionSurfaceParityTestscheck; its exports are committed in 4679944 (the shortcuts change is base's ⌘W alias for a multi-row sidebar selection, which base's own exports don't have yet; this PR adds no default key). - Observed (fleet,
package-test-lane.shat 038941b, the head):PictureInPictureTests8/8 at cd91441 (incl. the tab menu's and the card menu's hide and Exit title hooks),NowPlayingCardDismissTests7/7,NowPlayingLevelsTests4/4,NewTabNowPlayingTests7/7,ActionSurfaceParityTests11/11 pass;NowPlayingCardTests8/9. - Not mine:
NowPlayingCardTests.aTabsArtworkThatDidNotLoadComesInLaterfails the same way at the base, 511423d1 (fleet run,NowPlayingCardTests.swift:199there,:240here after this PR's added tests, "the card never shows the tab's artwork after its first fetch failed"). This PR does not touch the artwork fetch or its retry. - Review: an independent subagent review found no blockers; its should-fix items (CLI/MCP refusals, the card menu's tab, narrow-card overlap, rank tie, docs, a menu-hook test) are in 93b9b07. A second review of 93b9b07 passed; the card-menu test it asked for (
theCardsMenuFloatsTheTabTheCardShows) is in cd91441. - Not run: the live PiP window in a running app, which needs a GUI Mac.
🤖 Generated with Claude Code
Agent session: cosmic-gray