fix(web): keep the Workspace rail clear of the OS status and nav bars - #3587
fix(web): keep the Workspace rail clear of the OS status and nav bars#3587btli wants to merge 23 commits into
Conversation
2033967 to
57c8698
Compare
|
Withdrawing — this isn't the right fix for the rail inset issue; a different approach is coming. |
|
Reopening after discussion — this approach is correct after all. |
|
Pushed 2 commits:
Verification: web suite green on this branch (4,715); Android green except one pre-existing failure ( Recommended merge order — this PR is one of seven fixed and verified together; the fully integrated reference (all cross-PR conflicts resolved, 5,003 web tests + 273/273 Android tests green) is Android track (suggest landing first — security fixes, and the later two resolve against it):
Web track (file-disjoint from the Android track; order within it matters): |
752db09 to
906a6f1
Compare
On a tablet or unfolded foldable the right Workspace rail rendered under the status bar, leaving its tab icons un-tappable, and ran under the gesture-nav bar at the bottom. The rail is `md:m-2` and only renders at md+, but every native-shell inset rule lives inside `@media (width < 48rem)` — so none of them reach it — and it is absent from the shared panel selector lists on both the CSS and Android-injected sides. Add the safe-area margins outside the width gate, and mirror the same declaration into the Android injected sheet so shells pointed at an older web build get the fix too. Uses --omnigent-safe-*, not --omnigent-inset-*: the latter folds in the native bottom-bar footprint and would double-count. Signed-off-by: Bryan Li <bryan.li@gmail.com>
The panel docks as a full-height rail at md+ but was missing from both inset selector lists, so it had no safe-area padding at any width. It is reachable only in debug mode, which is why it went unnoticed. Pre-existing gap, adjacent to the Workspace rail fix rather than part of it. Signed-off-by: Bryan Li <bryan.li@gmail.com>
Signed-off-by: Bryan Li <bryan.li@gmail.com>
A media-gated override appearing later in index.css with the same selector previously slipped past the ancestry check, which only inspected the first matching rule. Assert over every rule that sets the Workspace rail's margins so a later override at md+ fails the suite instead of silently winning the cascade. Signed-off-by: Bryan Li <bryan.li@gmail.com>
The safe-area assertion read only the first matching rule, so a second top-level rule could reintroduce the composite inset vars — double counting the native bottom bar — while the suite stayed green. Signed-off-by: Bryan Li <bryan.li@gmail.com>
Only the longhand properties were matched, so a shorthand `margin` declaration set both edges without ever being checked for an enclosing at-rule. Signed-off-by: Bryan Li <bryan.li@gmail.com>
Matching the exact selector text let an equivalently-spelled one through, and checking only the first rule let a later override zero the margins without naming a banned variable. Match on the rail's aria-label and require the margin rule to be unique. Signed-off-by: Bryan Li <bryan.li@gmail.com>
margin-block and its start/end forms set the same edges as the rule being guarded, so an override written that way was never matched. Signed-off-by: Bryan Li <bryan.li@gmail.com>
Signed-off-by: Bryan Li <bryan.li@gmail.com>
…vert rail offset - Revert --workspace-panel-offset to inlinePanelWidth + 16 - Hoist the shared native-panel selector list out of the phone-width media gate into one ungated rule in index.css, mirrored in the Android injected sheet for pre-shell servers - Add :not([data-collapsed]) so zero-width collapsed push panels stay zero-width, and cover files/shells drawers at md+ - Derive injected-sheet coverage from the index.css rule in tests instead of hand-maintained lists; add execution-logs e2e geometry Signed-off-by: Bryan Li <bryan.li@gmail.com>
Ungating the unified rule from phone-width made the peeking sidebar reachable: it is a floating card inset 8px off every edge (md:absolute md:inset-2 p-0) and touches neither system bar, so the fold just dented the card and overrode its p-0. Add :not(.is-peek) to the index.css rule; the Kotlin injected sheet and the rule's comment block already carry it. Round-2 test-vacuity fixes: - Anchor the rule to a checked-in REQUIRED_PANEL_TEST_IDS list: deleting a testid from both stylesheets shrank the derived lists together and stayed green. - Assert brace depth 0 at each matching block so the unified rule stays at stylesheet top level — cssBlocks matches innermost blocks, so re-wrapping it in an at-rule left every other assertion green while silently dropping the md+ coverage. - Correct the dead-space comment: a real cutout is ~30-48px and the padding lands on content-facing edges, so that is visible dead space on cutout devices. Signed-off-by: Bryan Li <bryan.li@gmail.com>
Related issue
Closes #3585
Summary
On a tablet or unfolded foldable (Pixel 10 Pro Fold), the right Workspace rail rendered underneath the OS status bar — its Files / Agents / Shells / Tasks tab icons sat in the occluded band and could not be tapped — and its bottom edge ran under the gesture-nav bar.
The rail is
md:m-2(8px on every edge) and, beinghidden md:flex, only exists at ≥768px. Every native-shell inset rule lives inside@media (width < 48rem)(index.css:522→731), so none of them can reach it; it is also absent from the shared panel selector lists on both the CSS side and the Android-injected side. Its top edge therefore landed at y = 16px, inside a 24–48dp status bar.:is([data-ios-native], [data-android-native]), so both native shells get it and browser/Electron are untouched.ensureInsetStyles) so shells pointed at a server running an older web build get the fix from an APK update. Same property, same value, so the two stylesheets are idempotent and cannot double-count.Uses
--omnigent-safe-*, not--omnigent-inset-*— the latter folds in the native bottom-bar footprint and would double-count against the bottom edge.ELI5: the rail is a floating card pinned 8px from the screen edges. On phones a separate set of rules pushes UI clear of the status bar, but those rules switch off at tablet width — exactly where this card starts existing. So it kept its 8px and slid under the clock and battery icons.
Test Plan
npx vitest run src/index.css.test.ts— 16 passed.--omnigent-safe-*rather than the inset vars, and that exactly one such rule exists.@media (width < 48rem),@media (max-width: 767px)or@supports; appending a later@media (min-width: 48rem)block that zeroes the margins; a second top-level rule reintroducing the inset vars; shorthandmarginand logicalmargin-block*overrides; and the same selector written single-quoted, unquoted or with extra spacing.pre-commit run --files <changed files>— passed.npm run lint— no new findings.Note the uniqueness assertion will deliberately fail if someone later adds a second margin rule for the rail. That is intended: this rule was silently inert on every tablet until now, so a future change to it should require a human to look rather than pass quietly.
Demo
Recorded on an Android emulator at 800×1280 @ 160dpi (an 800dp-wide viewport, i.e. the md+ tablet/unfolded-foldable layout) with the tall display-cutout overlay enabled, connected to a local server.
mainAPK)Before: the Workspace rail's Files / Agents / Shells tab icons render inside the status bar, sharing the row with the clock and battery; tapping them does nothing (the taps land on the system bar), while content lower in the rail still responds. After: the rail's card starts below the status bar and clear of the gesture-nav area, and the same taps switch tabs normally. Full-resolution MP4s alongside the GIFs on the asset branch.
The floating server pill visible near the top of the "after" capture still overlaps web chrome — that is the separate defect fixed by #3589 and is untouched by this PR.
Type of change
Test coverage
Coverage notes
The CSS contract is covered by the mutation-checked regression test above, and the rendered result is now verified on an emulator at md+ width (see Demo), including the tap behaviour before and after. Not yet verified on physical foldable hardware. The inset values themselves are unchanged and already exercised on phones — this change only adds a new consumer of them.
Deliberately out of scope, and worth a follow-up: the same
@media (width < 48rem)gate means that at iPad widths the chat header, conversation content, terminal view and composer are also uncovered, because iOS has no injected-sheet backstop the way Android does. This PR fixes the rail on both shells but does not make iPad correct in general.Changelog
The workspace rail on Android tablets and foldables no longer slides under the status bar, so its tab icons stay tappable