Skip to content

📱 feat: Give the Mobile Drawer One Place for Each Control - #16248

Merged
berry-13 merged 4 commits into
canaryfrom
berry-13/mobile-drawer-controls
Oct 1, 2026
Merged

berry-13 merged 4 commits into
canaryfrom
berry-13/mobile-drawer-controls

Conversation

@berry-13

@berry-13 berry-13 commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Pull Request

Summary

On a phone, the sidebar drawer split its two global destinations across opposite corners and repeated one of them under every panel. The Agent Marketplace was a bare icon in the header strip, the easiest thing there to miss. New chat was a large footer button sitting under prompts, memories and MCP settings, none of which it relates to. Text in the header strip and footer also inherited black from the document, so it was near-invisible in dark mode.

This PR gives each control one place. New chat becomes an icon beside the panel switcher, where the drawer keeps what means the same thing on every panel. It starts a chat and closes the drawer. The marketplace becomes a labelled full-width row above the panel content, outside the scroller, so it stays put while panels change. The footer holds search alone and shrinks to the bottom safe-area spacer on a panel with nothing to search. The drawer sets text-text-primary.

It also folds in the drawer's scrim and boundary follow-ups (berry-13#105, berry-13#145, berry-13#199, berry-13#65). The scrim now paints the theme's bg-scrim role (80% by default, Click UI's 75% in ClickHouse) instead of a fixed 50%, which separates the drawer by 3:1 in both light palettes. In dark mode no scrim opacity separates two near-black surfaces, so the drawer draws a border-xheavy edge (6.1:1 default, 3.7:1 ClickHouse). The scrim's keyboard focus indicator paints above the fill in two tones, a surface band and the focus ring, because a single dark ring falls to 2.3:1 on the 80% light scrim. Audit criteria: themable C9 (five no-restyle suppressions removed, none added), themable C4 (the scrim role gains a consumer beyond the dialogs), ClickHouse C11 (the drawer defect left over from #105).

Type of change

  • Feature
  • Bug fix

Testing

Tested environments/configuration:

  • Chromium at 390x844, loaded at phone width (not resized into it), default and ClickHouse themes in light and dark
  • Mock harness (reviewctl verify): desktop light, desktop dark and mobile emulation

Automated tests:

  • e2e/specs/mock/scenarios/mobile-drawer-controls.spec.ts (14 scenarios, desktop light, desktop dark and mobile, 42 passing): New chat placement and behavior, the marketplace row's position across panel changes and its navigation, no gap when the marketplace is off, the footer standing down, dark-mode text contrast, the drawer boundary in four palettes, and the scrim role plus a focus indicator that paints above the fill at 3:1 in four palettes
  • reviewctl precheck against origin/canary (ESLint, Prettier, import order, design-rule suppressions, typecheck, related jest): pass
  • npx jest src/components/UnifiedSidebar src/components/Nav/__tests__/AgentMarketplaceButton.spec.tsx (client): 11 suites, 57 tests pass
  • npx tsc --noEmit -p client/tsconfig.json: clean

Screenshots / recordings

Phone width (390px), drawer open, loaded at that width. Each image is before (canary) then after, light then dark.

Default theme:

Default theme, before and after, light and dark

ClickHouse theme:

ClickHouse theme, before and after, light and dark

With the chat strip setting on, which shows the scrim (dark mode also shows the drawer's new edge):

Chat strip on, before and after, light and dark

Risk / compatibility

The scrim is darker than before (80% instead of 50% by default), deliberately, to meet the 3:1 boundary. Chromium cannot emulate a nonzero env(safe-area-inset-bottom), so the home-indicator spacer is covered by the unit test and by inspection rather than in the browser.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors

@berry-13
berry-13 added this pull request to stack #16249 September 23, 2026 15:15
@berry-13
berry-13 marked this pull request as ready for review September 23, 2026 16:42
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T16:51:13.126798Z b171124 Draft marked ready
🔒 Security Review ✅ Completed 2026-09-23T16:46:39.189358Z b171124 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b17112431d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +23 to +24
if (!showSearch) {
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep safe-area padding when search is absent

On devices with a nonzero bottom safe-area, this return removes the drawer's only env(safe-area-inset-bottom) padding whenever search is disabled or the active panel is prompts, memories, MCP, and so on. The footer previously remained mounted because it also contained New Chat, so the panel stayed above the home indicator; now bottom rows and actions can render beneath the system gesture area. Keep a safe-area spacer or move the padding to the drawer independently of search visibility.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0f9be8d. BottomBar now always renders: without search it is an empty spacer carrying env(safe-area-inset-bottom), so panels with nothing to search still stop above the home indicator. Covered by BottomBar.spec (both stand-down cases) and @Scenario:mobile-drawer-footer-search-only.

Comment thread client/src/components/UnifiedSidebar/mobile/NewChat.tsx
Comment thread eslint-suppressions.json Outdated
Comment on lines +1261 to +1264
"client/src/components/Nav/AgentMarketplaceButton.tsx": {
"shadcn/no-restyle": {
"count": 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the newly added design-rule suppression

This adds a fresh shadcn/no-restyle allowance for AgentMarketplaceButton, masking the new Button override instead of using or extending an appropriate shared primitive variant. The repository explicitly prohibits widening eslint-suppressions.json to land a restyle, so the component should be corrected and this entry removed.

AGENTS.md reference: AGENTS.md:L88-L94

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0f9be8d. The row uses the Button's shared row size with layout-only classes, and the AgentMarketplaceButton suppression entry is gone. The design-rule suppressions gate passes, and the BottomBar no-restyle entry drops by 4.

@danny-avila danny-avila added the 🗺️ Chat UI Shell codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) label Sep 24, 2026
Base automatically changed from berry-13/grouped-model-params to canary September 30, 2026 07:28
@berry-13
berry-13 force-pushed the berry-13/mobile-drawer-controls branch from b171124 to 0f9be8d Compare October 1, 2026 08:09
@berry-13

berry-13 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the current PR head 0f9be8d. State the exact reviewed commit and ignore findings that apply only to earlier heads.

Purpose supplied by the requester: rebuilt on canary; 3 fixes since reviewed head b171124: safe-area spacer when search is absent, marketplace row on the shared row size (suppression removed), empty marketplace wrapper hidden; plus scrim role and dark drawer edge (berry-13#199)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f9be8dda2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

>
{/* The dialogs' theme-owned scrim role, painted on its own layer so the
button itself carries no color of its own. */}
<span aria-hidden="true" className="bg-scrim absolute inset-0" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the scrim fill below its focus ring

At reviewed head 0f9be8dda29644c2524342e90ce7284e9032b7bb, when the drawer-strip setting is enabled and a keyboard user focuses the dismiss scrim, this absolutely positioned bg-scrim child is painted over the Button's inset box-shadow, substantially obscuring the only visible focus indicator under the default 75–80% scrim opacity. The new browser test only checks that boxShadow is computed, which remains true while the shadow is hidden beneath this child; paint the fill on the button or place the focus indicator above the fill.

AGENTS.md reference: AGENTS.md:L49-L52

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dd0bd19. The indicator now draws on a layer above the fill. Fixing that exposed a second problem: at 80% a single dark ring reaches only 2.3:1 on the light palettes' mid-gray, so it is two-tone (a surface band plus the focus ring). The scenarios mobile-drawer-scrim-focus-{default,clickhouse}-{light,dark} hit-test the topmost layer at the scrim's edge and require a tone at 3:1 against the fill, rather than reading the computed box-shadow.

@berry-13
berry-13 force-pushed the berry-13/mobile-drawer-controls branch from dd0bd19 to 875516f Compare October 1, 2026 08:29
@berry-13

berry-13 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the current PR head 875516f. State the exact reviewed commit and ignore findings that apply only to earlier heads.

Purpose supplied by the requester: 1 fix since reviewed head 0f9be8d: the scrim focus indicator paints above the fill, two-tone so one tone reaches 3:1 in all four palettes (finding 4153260127); rebased on canary with no content change

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 875516f02c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@berry-13

berry-13 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the current PR head cab7354. State the exact reviewed commit and ignore findings that apply only to earlier heads.

Purpose supplied by the requester: 1 change since clean head 875516f: the drawer close toggle keeps canary's header-action surface (CI scenario mobile-drawer-close-toggle-stays-the-same-control from #15894)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: cab73542a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The drawer put its two global destinations in opposite corners and repeated
one of them under every panel. The marketplace was a bare icon in the top
strip, the hardest thing there to notice; new chat was a large footer button
sitting under prompts, memories and MCP settings alike, none of which it has
anything to do with; and the close toggle wore the chat header's bordered
plate beside two flat icons, so the row read as one control and two
afterthoughts.

They swap. New chat takes the icon slot beside the panel switcher, which is
where the drawer keeps what means the same thing whichever panel is showing,
and its click behaviour moves with it into a component of its own rather than
staying behind in the footer. The marketplace becomes a full-width entry
above the panel content, labelled, in the list where someone already looks
for somewhere to go, and outside the scroller so it stays put while the
panels change under it. The toggle drops to `ghost` like its neighbours. The
footer is left with search alone and stands down entirely on a panel that has
nothing to search, rather than taking its padding out of the list above it.

The drawer now carries a text colour beside its background. Only the panel
nav set one, so the header strip, the new marketplace row and the footer all
inherited black from the document and rendered near-invisible in dark mode,
except where a control happened to name a token itself. That was hidden while
the toggle wore `header-action`, which brought its own.
…part from its scrim

The footer now renders a safe-area spacer when search is absent, so panel
content no longer runs under the system gesture area on panels with nothing
to search. The marketplace row uses the Button's `row` size instead of
restyling it, which drops its new lint suppression.

The scrim paints the theme's `bg-scrim` role (80% by default) on its own
layer, which separates the drawer by 3:1 in both light palettes, and in dark
mode the drawer draws a `border-xheavy` edge, since no scrim opacity
separates two near-black surfaces. The scrim's focus ring is checked in the
browser instead of by class name.
The scrim's theme fill sits on its own layer, which painted over the
button's inset focus ring, so a keyboard user focusing the scrim with the
chat strip on saw almost none of it. The indicator now draws on a layer
above the fill, in two tones: at the theme's 80% scrim a single dark ring
reaches only 2.3:1 on the light palettes' mid-gray, so a surface band
carries it in light and the focus ring in dark. The scenarios check, in
all four palettes, that the topmost layer at the scrim's edge carries an
indicator with a tone at 3:1 against the fill.
The drawer's close toggle is the chat header's open toggle flipping state,
so it keeps the header-action surface and size it shares with that control
rather than going flat like its new neighbours.
@berry-13
berry-13 force-pushed the berry-13/mobile-drawer-controls branch from cab7354 to 152bb9a Compare October 1, 2026 09:20
@berry-13
berry-13 merged commit 0da8987 into canary Oct 1, 2026
30 checks passed
@berry-13
berry-13 deleted the berry-13/mobile-drawer-controls branch October 1, 2026 10:28
lia-by-librechat Bot pushed a commit that referenced this pull request Oct 1, 2026
* feat(client): Give the mobile drawer one place for each control

The drawer put its two global destinations in opposite corners and repeated
one of them under every panel. The marketplace was a bare icon in the top
strip, the hardest thing there to notice; new chat was a large footer button
sitting under prompts, memories and MCP settings alike, none of which it has
anything to do with; and the close toggle wore the chat header's bordered
plate beside two flat icons, so the row read as one control and two
afterthoughts.

They swap. New chat takes the icon slot beside the panel switcher, which is
where the drawer keeps what means the same thing whichever panel is showing,
and its click behaviour moves with it into a component of its own rather than
staying behind in the footer. The marketplace becomes a full-width entry
above the panel content, labelled, in the list where someone already looks
for somewhere to go, and outside the scroller so it stays put while the
panels change under it. The toggle drops to `ghost` like its neighbours. The
footer is left with search alone and stands down entirely on a panel that has
nothing to search, rather than taking its padding out of the list above it.

The drawer now carries a text colour beside its background. Only the panel
nav set one, so the header strip, the new marketplace row and the footer all
inherited black from the document and rendered near-invisible in dark mode,
except where a control happened to name a token itself. That was hidden while
the toggle wore `header-action`, which brought its own.

* fix(client): Keep the mobile drawer clear of the home indicator and apart from its scrim

The footer now renders a safe-area spacer when search is absent, so panel
content no longer runs under the system gesture area on panels with nothing
to search. The marketplace row uses the Button's `row` size instead of
restyling it, which drops its new lint suppression.

The scrim paints the theme's `bg-scrim` role (80% by default) on its own
layer, which separates the drawer by 3:1 in both light palettes, and in dark
mode the drawer draws a `border-xheavy` edge, since no scrim opacity
separates two near-black surfaces. The scrim's focus ring is checked in the
browser instead of by class name.

* fix(client): Paint the mobile scrim's focus indicator above its fill

The scrim's theme fill sits on its own layer, which painted over the
button's inset focus ring, so a keyboard user focusing the scrim with the
chat strip on saw almost none of it. The indicator now draws on a layer
above the fill, in two tones: at the theme's 80% scrim a single dark ring
reaches only 2.3:1 on the light palettes' mid-gray, so a surface band
carries it in light and the focus ring in dark. The scenarios check, in
all four palettes, that the topmost layer at the scrim's edge carries an
indicator with a tone at 3:1 against the fill.

* fix(client): Keep the mobile drawer close toggle on the header's control

The drawer's close toggle is the chat header's open toggle flipping state,
so it keeps the header-action surface and size it shares with that control
rather than going flat like its new neighbours.

(cherry picked from commit 0da8987)

Original-PR: #16248
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🗺️ Chat UI Shell codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🛡️ security review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants