iPhone bar leaves the tree while hidden; docs for the full-window call
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 files changed,
+74
−7
native/DESIGN.md+55 −0native/README.md+15 −3native/ios/Sources/PhoneFullScreenCall.swift+4 −4
diff --git a/native/DESIGN.md b/native/DESIGN.md| index d6085b1..bcf520e 100644 |
| --- a/native/DESIGN.md |
| +++ b/native/DESIGN.md |
| @@ -81,3 +81,58 @@ Safari member/guest sign-in and the real iPhone simulator native-media tests |
| passed after this change. The signed development app was built and installed on |
| Paul's paired iPhone. That installation does not substitute for a physical |
| camera/microphone and interruption acceptance call. |
| + |
| +## Full-window call · 2026-09-25 (0.3.4) |
| + |
| +The call fills the window (Mac) or the screen (iPhone), with the controls over |
| +it on demand. **Call layout: Full window (new) / Classic** — Calls → Settings… |
| +on Mac, the gear's sheet on iPhone — keeps the previous layout one choice away; |
| +Full window is the default. The mini window and tab are unchanged. |
| + |
| +- **When the controls show** is `ControlsVisibility` (CallChrome.swift), one |
| + state machine for both apps with the time passed in, so tests drive it. Mac: |
| + pointer movement shows them, 1.5 s still hides them, leaving the window hides |
| + them at once, and the pointer hides with them over the picture |
| + (`NSCursor.setHiddenUntilMouseMoves`). iPhone: a tap toggles, 2.5 s after the |
| + last touch hides — no hover says the person is still there, and a thumb needs |
| + the extra second to reach the bar. They stay up while the pointer is on the |
| + bar, a menu, popover, sheet or panel is open (AppKit's menu-tracking and |
| + window-sheet notifications cover the ones SwiftUI does not report), a bar |
| + control has keyboard focus, VoiceOver is running, or an alert shows. Tab |
| + reveals them. Space and ⌘⇧M work with them hidden. Fades are opacity only; |
| + Reduce Motion shortens them to 0.1 s and panels fade instead of sliding. |
| +- **The stage** is edge to edge: square tiles for Focus and a screen, a grid |
| + that fits every tile without scrolling until they would be under 200 pt |
| + (`GridFit`). On Mac the title bar is transparent with the traffic lights kept. |
| + Names are small capsules with the muted mark (shape and word) inside, lifted |
| + clear of the bar while it shows, visible while it does not. |
| +- **The self view** floats in Focus, draggable, settling in the corner it was |
| + flicked towards (`PiPLayout`), kept clear of the bar, an open chat and the |
| + camera strip beside a shared screen, and remembered. It always says Muted |
| + when you are. VoiceOver has "Move self view to …" actions. In Grid you are a |
| + tile, as before. |
| +- **Title and notices.** Title and status go with the controls. Reconnecting, |
| + waiting to be let in, full, ended (with Join again), problems (dismissible), |
| + "You are sharing your screen" and each person waiting with **Let in** stay |
| + until resolved (`CallNotices`); the relay-floor line fades. |
| +- **Mac bar**: a material bar along the bottom — microphone, camera, share, |
| + devices, layout (Focus/Grid and who is on the stage, in a popover), share link |
| + — with chat and Leave on the right. Devices is a floating panel over the |
| + stage above the bar, not a column that shrinks it. |
| +- **Mac chat** is an overlay sidebar sliding over the stage's trailing edge, |
| + with the existing pop-out to a separate window kept. Chosen over the window |
| + alone because the window is window management mid-call on a laptop screen, |
| + while the sidebar is one click and leaves the picture its full size underneath; |
| + anyone who wants it beside the call still has the window. |
| +- **iPhone bar**: a kebab (More controls) on the left and End call on the right. |
| + The kebab opens a sheet: mute, camera, flip camera, share screen (the system |
| + broadcast picker, which is why it is a sheet and not a menu), devices, Mic |
| + Mode, layout, share link, chat and Reduce background noise. The status bar and |
| + home indicator hide with the controls. `-CallsMicBesideMenu YES` puts a |
| + microphone button beside the kebab, off by default. |
| + |
| +Snapshots: `CallsInterop --call-snapshots <dir>` (also part of |
| +`--lobby-snapshots`) renders the Mac call, both layouts, light and dark. The |
| +iPhone's come from `CallsPhoneUITests.testFullScreenCallSnapshots` against the |
| +debug build's `-CallsCallFixture` calls (`CallFixture`), written to |
| +`CALLS_SNAPSHOT_DIR` or `/tmp/immersive-call`. |
diff --git a/native/README.md b/native/README.md| index 4ced9a7..000b175 100644 |
| --- a/native/README.md |
| +++ b/native/README.md |
| @@ -101,8 +101,11 @@ every rebuild, macOS may ask once per build whether Calls may read it. |
| Scheduling a meeting for later, ending one, and replacing invitations stay on |
| the web for this milestone. |
| -**Devices** in the call opens the camera, microphone and speaker panel, and |
| -the audio readout beneath them. |
| +**Devices** in the call opens the camera, microphone and speaker panel. The |
| +audio readout is behind its ⓘ (**Audio details**), with the screen-share line |
| +while sharing; the panel itself shows one warning line only when something is |
| +wrong (a stretched or broken-up signal, echo cancellation off, noise reduction |
| +bypassed, processing refused — `AudioWarning`). |
| The camera is a picker on both platforms. Switching rests on the capturer |
| feeding the same `RTCVideoSource`, so the `RTCVideoTrack` every peer |
| @@ -420,7 +423,16 @@ Microphone** (⌘⇧M) is the same toggle. The browser room does the same (never |
| an input, textarea, select, contenteditable, button, link or other control, and |
| announced through the polite live region), and on iPhone and iPad a hardware |
| keyboard's Space does it through a key command that yields to text editing and |
| -focus. Push-to-talk (holding Space while muted) is not built. It shows what the call window's stage would show by |
| +focus. Push-to-talk (holding Space while muted) is not built. |
| + |
| +**Call → Copy Invite Link** (⌘⇧I), beside Mute, puts the meeting's invite link |
| +on the clipboard and says "Invite link copied" briefly over the stage (and to |
| +VoiceOver), from the call window or the mini window, with the controls shown or |
| +not. It asks the server the way the Share button does, which returns the |
| +meeting's current sealed invitation and never replaces or revokes one. It is |
| +off outside a call; a guest pressing it is told only members can share. An |
| +iPad's hardware keyboard has the same shortcut. The browser room does not: |
| +⌘⇧I is Chrome's developer tools. It shows what the call window's stage would show by |
| default: the newest shared screen, otherwise the first other participant with |
| their muted and camera-off state (there is no active-speaker tracking yet), or |
| your own camera while you are alone. It is not |
diff --git a/native/ios/Sources/PhoneFullScreenCall.swift b/native/ios/Sources/PhoneFullScreenCall.swift| index 8f0f41c..f215e40 100644 |
| --- a/native/ios/Sources/PhoneFullScreenCall.swift |
| +++ b/native/ios/Sources/PhoneFullScreenCall.swift |
| @@ -30,7 +30,9 @@ extension PhoneCall { |
| .accessibilityIdentifier("callStage") |
| if !grid { selfView(in: size, safe: safe) } |
| topOverlay(safe: safe) |
| - bar(safe: safe) |
| + // Gone rather than transparent while hidden, so nothing invisible |
| + // can be tapped or found; the fade is the transition. |
| + if chrome.visible { bar(safe: safe).transition(.opacity) } |
| // Space on a hardware keyboard, as the classic bar's Mute has it; |
| // the full-screen Mute is in the menu, so this stands in for it. |
| Button("Mute or unmute microphone") { call.mute() } |
| @@ -242,6 +244,7 @@ extension PhoneCall { |
| .opacity(chrome.visible ? 1 : 0) |
| .allowsHitTesting(chrome.visible) |
| .accessibilityHidden(!chrome.visible) |
| + .accessibilityIdentifier("callTitle") |
| ForEach(notices.filter { $0.important && !dismissedNotices.contains($0.id) }) { notice in |
| noticeView(notice) |
| @@ -326,9 +329,6 @@ extension PhoneCall { |
| .frame(height: Self.barHeight + safe.bottom + 40) |
| .allowsHitTesting(false) |
| } |
| - .opacity(chrome.visible ? 1 : 0) |
| - .allowsHitTesting(chrome.visible) |
| - .accessibilityHidden(!chrome.visible) |
| } |
| } |