Skip to content

fix(web): keep the Workspace rail clear of the OS status and nav bars - #3587

Open
btli wants to merge 23 commits into
omnigent-ai:mainfrom
btli:fix/android-right-rail
Open

fix(web): keep the Workspace rail clear of the OS status and nav bars#3587
btli wants to merge 23 commits into
omnigent-ai:mainfrom
btli:fix/android-right-rail

Conversation

@btli

@btli btli commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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, being hidden md:flex, only exists at ≥768px. Every native-shell inset rule lives inside @media (width < 48rem) (index.css:522731), 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.

  • Add the safe-area margins outside the width gate, scoped to :is([data-ios-native], [data-android-native]), so both native shells get it and browser/Electron are untouched.
  • Mirror the identical declaration into the Android injected sheet (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.
  • Separate, independently droppable commit: the execution-logs panel docks as a full-height rail at md+ but was missing from both selector lists, so it had no safe-area padding at any width. Debug-mode only, which is why it went unnoticed.

Uses --omnigent-safe-*, not --omnigent-inset-* — the latter folds in the native bottom-bar footprint and would double-count against the bottom edge.

BEFORE                          AFTER
┌──── status bar ────┐          ┌──── status bar ────┐
│ ▣ ▣ ▣ ▣  ← dead    │          ├────────────────────┤
├────────────────────┤          │ ▣ ▣ ▣ ▣  ← tappable│
│  workspace rail    │          │  workspace rail    │

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.ts16 passed.
  • The regression test pins the property that actually matters: the rule must apply at md+. It asserts the rule has no enclosing at-rule of any kind, that every rule setting the rail's margins is top-level and uses --omnigent-safe-* rather than the inset vars, and that exactly one such rule exists.
  • Mutation-checked throughout — an early version of this test passed while the fix was inert, so each hardening was accepted only after the corresponding mutation went red. The suite now fails for: wrapping the rule in @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; shorthand margin and logical margin-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.
  • Reviewed by three independent engines (gpt-5.6-sol, Grok 4.5, Gemini 3.1 Pro) over six rounds; all three report clean on the final state.

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.

Before (main APK) After (this PR's APK)
before — rail tab icons render inside the status bar; taps do nothing after — rail starts below the status bar; the same taps switch tabs

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

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

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

@github-actions github-actions Bot added the size/M Pull request size: M label Jul 30, 2026
@btli
btli force-pushed the fix/android-right-rail branch from 2033967 to 57c8698 Compare July 30, 2026 18:39
@btli
btli marked this pull request as ready for review August 4, 2026 09:27
@github-actions
github-actions Bot requested a review from serena-ruan August 4, 2026 09:28
@btli

btli commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Withdrawing — this isn't the right fix for the rail inset issue; a different approach is coming.

@btli btli closed this Aug 4, 2026
@btli

btli commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Reopening after discussion — this approach is correct after all.

@btli

btli commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 2 commits:

  • The workspace rail pads content clear of the OS bars on all four edges instead of shrinking with top/bottom margins, so its background and divider stay flush; lateral insets fold into --omnigent-safe-left/right.
  • Published insets include the display cutout, which systemBars() alone omits; the injected fallback sheet mirrors the same rules.
  • Plus a test-helper dedup in the CSS contract tests.

Verification: web suite green on this branch (4,715); Android green except one pre-existing failure (configuration change updates system bar icon polarity) — this branch's base reads resources.configuration, which lags direct onConfigurationChanged calls under Robolectric. #3800 fixes that by passing the Configuration in, hence the rebase note below.


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 btli:test-android-ui-integration @ 3fa45ff3.

Android track (suggest landing first — security fixes, and the later two resolve against it):

  1. fix(android): sign in to servers behind front-door auth proxies (Databricks Apps) #3800 — no dependencies
  2. fix(web): keep the Workspace rail clear of the OS status and nav bars #3587 — rebase after fix(android): sign in to servers behind front-door auth proxies (Databricks Apps) #3800: it picks up applySystemBarContrast(Configuration), which fixes this branch's one pre-existing Robolectric failure; and since main now has flush rails (feat(web): make the rails flush boxes and move the canvas gradient #4020), the rebase should also drop the stale +16 from --workspace-panel-offset (see integration commit 8b687d7c)
  3. fix(android): keep the server switcher centred over the chat column, clear of header controls #3589 — after fix(android): sign in to servers behind front-door auth proxies (Databricks Apps) #3800: both add constructor callbacks to OmnigentBridgeListener; union them (resolution in integration merge 3fa45ff3)

Web track (file-disjoint from the Android track; order within it matters):
4. #3985 — base swipe-actions feature
5. #4060 — folder context menu
6. #4057 — mobile ungroup drop zone
7. #4065 — hold: superseded by the unified row-gesture recognizer, which builds on #3985 and interacts with #4057/#4060; after 4–6 land, update it to the recognizer commits (69a8ada5, bf597b87, e388aacf, reachable on the integration branch)

btli added 8 commits August 6, 2026 10:44
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>
btli added a commit to btli/omnigent that referenced this pull request Aug 11, 2026
Signed-off-by: Bryan Li <bryan.li@gmail.com>
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 11, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 11, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
…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>
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
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>
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 12, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 13, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 14, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 14, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 14, 2026
github-actions Bot pushed a commit to btli/omnigent that referenced this pull request Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1-high Priority: major feature broken, no workaround size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Android/iPad: right Workspace rail renders under the status bar at tablet widths — tab icons un-tappable

2 participants