iPhone Duo support: fold-aware dialogs, and a fold is no longer mistaken for the keyboard - #407
Open
Ark0N wants to merge 1 commit into
Open
iPhone Duo support: fold-aware dialogs, and a fold is no longer mistaken for the keyboard#407Ark0N wants to merge 1 commit into
Ark0N wants to merge 1 commit into
Conversation
…oard) Apple's "Designing for iPhone Duo" asks an app to adapt to both displays, to stay continuous as the device opens and closes, and to treat the band a partly-open display folds through as a reserved region. Three things here. 1. A visual-viewport resize that changes the WIDTH is the device changing shape (a rotation, or a foldable opening or closing) and is never the virtual keyboard, which only ever takes height. handleViewportResize() read any height drop over 150px as the keyboard appearing, so closing a Duo (890 to 678pt tall) latched keyboardVisible with no keyboard on screen: the accessory bar appeared, main grew 84px of dead padding, and updateAppHeight() stopped refreshing --app-height. The latch was sticky, because clearing it needs the height back within 100px of a baseline belonging to a display the user is no longer looking at. Rotating any phone hit the same latch. The shape branch re-baselines instead, which is also what lets a keyboard opened after the fold be detected. 2. The hinge is now a reserved region in CSS. --fold-inline-end and --fold-block-end measure the strip to keep clear from the Viewport Segments env() variables, and are 0px everywhere else, so the seven centred overlays are inert by construction off a foldable. Each shrinks its content box with padding rather than the box itself, so the backdrop still covers the far side of the fold and still swallows taps there. 3. iPhone Duo (outer) and iPhone Duo (inner) join the mobile device registry, derived from Apple's published pixel specs at 3x. Verified in Chromium: flat, a dialog stays centred at 313 of a 626pt viewport; in book pose it centres at 153 inside the 0-305 leading segment with its right edge at 293, while the backdrop still spans all 626. The 3-term calc on the offline overlay resolves to 367px in tabletop pose and 20px flat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Apple published Designing for iPhone Duo. It asks an app to do three things: adapt to both displays, stay continuous as the device opens and closes, and treat the band a partly-open display folds through as a reserved region that content avoids covering. This PR does those three things for Codeman's web UI, and fixes a bug the first one exposed that also affects hardware shipping today.
1. A fold is not the virtual keyboard (bug fix)
KeyboardHandler.handleViewportResize()(mobile-handlers.js) read any visual-viewport height drop over 150px as the keyboard appearing.A virtual keyboard only ever takes height. Anything that changes the viewport WIDTH is the device changing shape. Closing an iPhone Duo moves the browser from the 626x890pt inner display to the 466x678pt outer one, a 212px height drop, so the app latched
keyboardVisiblewith no keyboard on screen:maingrew 84px of padding reserving room for a keyboard that was not there,MobileDetection.updateAppHeight(), which bails out while the keyboard is up, stopped refreshing--app-height.The latch was sticky. Clearing it needs the height back within 100px of a baseline that now belongs to a display the user is no longer looking at, so the state survived until the device was opened again.
This is not Duo-specific. Rotating any phone hits the same latch: 393x659 to 852x330 is a 329px drop with no keyboard anywhere. That has presumably been true for a long time and is fixed here too.
The fix is one distinction: a resize that changes the width re-baselines and is never read as a keyboard transition. The re-baseline (rather than just suppressing the branch) is what lets a keyboard opened after the fold still be detected, measured against the display it is actually on.
2. The hinge is a reserved region (new)
On the web, a fold is described by the CSS Viewport Segments media features and
env()variables, which report two segments only while a foldable is actually bent. Codeman's centred overlays are allposition: fixed; inset: 0flex-centring boxes, so in book pose their dialog lands on the hinge, and in tabletop pose it lands on a horizontal one.--fold-inline-end/--fold-block-end(styles.css) measure the strip to keep clear and are0pxon everything else, so the rules are inert by construction rather than behind a branch. Each overlay shrinks its CONTENT box with padding rather than the box itself, so the backdrop still covers the far side of the fold and still swallows taps there. The response viewer, a bottom sheet, is capped to the bottom segment in tabletop pose only, since a vertical hinge through a full-width sheet is fine.Two details worth a reviewer's eye:
padding-rightlonghand beats the earlierpaddingshorthand it composes with and would otherwise erase it.test/foldable-layout.test.tsreads both numbers out of the stylesheet and compares them, so changing one alone fails there..modal.command-palette-modal, because mobile.css loads after styles.css and gives it apaddingshorthand under 768px, exactly the width a folding phone lives at.Physical sides, not logical ones: dialogs sit in the left segment (the top one in tabletop pose) in every language, because the HIG keeps Duo's side controls on the same physical edge in RTL, aligned with the hardware.
3. Device profiles
iPhone Duo (outer)466x678 andiPhone Duo (inner)626x890, both at DPR 3, derived from Apple's published pixel specs (1398x2034 and 1878x2670, at 460 and 430 ppi). No browser-chrome allowance is subtracted, unlike the other iOS entries, because the HIG says the system moves toolbars and tab bars to the side on this device, so the vertical allowance copied from other iPhones would be wrong in both axes. The registry's other foldable uses the full viewport for the same reason.Both postures land in the tablet band (466 and 626 are each above the 430px phone cut and below 768). That is deliberate and matches the existing note on
shouldUseMobileOverview(): layout follows width, while per-device settings identity follows form factor and must not move with the fold.Testing
npm test: 6831 passed, 357 files. Two new files in the gate, 28 tests:test/viewport-shape-change.test.ts(the keyboard/shape logic, in a vm against the realKeyboardHandler) andtest/foldable-layout.test.ts(postcss guard plus the handheld-identity check).test/foldable-layout.test.tsDERIVES the overlay list from the stylesheet, so a newposition: fixed; inset: 0centred overlay added without a fold rule fails there rather than on hardware nobody has.npm run test:mobile -- test/mobile/settings.test.ts(excluded from CI, run by hand): 19 passed, 1 failed. Master is 18 passed, 1 failed, the same pre-existing failure (subagentTrackingEnableddefault), so the FAIL list is unchanged and the new Duo close test passes.Notes