feat(web): mobile shell — route depth model, stacked settings, touch affordances - #958
Merged
Merged
Conversation
…bile stacks Phase 1 (chat): useIsMobile single JS breakpoint (<768px), mobile top bar (≡/←/title/+), nav Sheet reusing the rail panels, and dockview runtime constraints keyed on it — split veto, programmatic splits collapsed into the single group, group merge on entry, hidden tab strip, layout persistence sealed so the desktop snapshot is never read or written while mobile. Phase 2 (settings): mobile list/content stack for the settings section (full-width SettingsSidebar as the nav homepage) and the same stack for master-detail, deleting the hamburger+Sheet mobile mode outright. docs: PLAN.md — architecture ruling and phase plan. Checkpoint before the depth-model rework: the list states are currently shell state rather than routes, which the review showed cannot be patched into consistency — the route-based depth tree lands next.
…t composer Depth model: every mobile navigation level is now a real addressable route, so the system back button and the shell's ← walk the same history. - /settings is the settings LIST (redirect removed; a desktop guard re-applies the old /settings/bots default). The list renders as an overlay above a persistent, keep-alive content section, so the breakpoint never unmounts the page tree. - Bot detail's tab list is driven by the ?tab= query: tab content is a push from the list (system back returns), close falls back to replace. - master-detail-sidebar-layout drops the hamburger + left Sheet entirely; below the breakpoint it is a two-pane stack keyed on the detailOpen prop. - Back affordances stay history-following (useBackAffordance), so labels always name where back actually leads. Page-level adaptation: PageShell gains a <md gutter step (px-4/pt-6/pb-8) matching SettingsShell/DetailPane; PageHeader's framed row wraps instead of crushing the title into truncation; the settings list's first row and the bot-detail container pick up the same 16px mobile margin. (ui submodule: 069430e, 9178910.) Composer: useVirtualKeyboard tracks the software keyboard via the Visual Viewport API and lifts the whole dock (plus the message list's bottom pad) above it on iOS; Android lifts natively via interactive-widget= resizes-content. The lift is suppressed while the model picker is open so it can't yank the trigger from under the popover. Composer controls grow to the 44px touch floor below md, the model picker clamps to the viewport, and menu search inputs render at 16px on phones to stop iOS focus zoom.
…rows - Top-bar "+" menu was dead: the rework had wrapped the reka as-child trigger in MobileBarIconButton, whose declared click emit swallows the trigger's injected onClick and strands the anchor ref (menu never opened). The trigger is a plain ui Button again; the shared bar chrome class moved to icon-button-class.ts, with a warning on the component never to use it as an as-child child. - Session rows give no feedback on touch: the hover fill never fires and the long-press context menu felt dead until it appeared. The row now paints the same fill on :active. - Bot settings General tab rows no longer force the desktop side-by-side layout onto a phone: label + wide-control rows (URL name, language, timezone, default agent, chat model, search/fetch/memory, TTS, transcription, image, video) use SettingsRow's stack="sm" owner axis and their fixed w-52/w-56 wrappers go full width when stacked. Switch rows stay side-by-side; >=sm is pixel-identical.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bb43c71de
ℹ️ 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 single-row pill is a desktop affordance: it seats the inline textarea, the model capsule, and send on one line and only reflows when text wraps or the pane is too narrow. On a phone that reflow is width luck — a slightly wider screen or a shorter model name lands the composer back on the cramped pill, which is a poor touch target. Wire the shell's mobile breakpoint (useIsMobile) into the layout engine as a third expansion trigger so the composer is ALWAYS multiline below md (textarea row, controls row below). Desktop >= md is untouched: the pill remains the compact default.
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.
Turns the desktop-only web app into a usable phone experience, modeled as a route-based depth tree (Chat=0 →
/settings=1 →/settings/x=2 →/settings/bots/:name=3 →?tab=x=4). The system back button and the shell's ← walk the same history, so navigation is consistent and every level is addressable by URL.What's in here
Mobile shell chrome — chat stack chrome (mobile top bar with ≡/← left action and a
+panel menu), settings & bot-detail mobile stacks, and a nav sheet. The+menu mirrors the desktop tab-strip add actions with the same permission gates.Keyboard-lift composer —
useVirtualKeyboard()measures the iOS/Android on-screen keyboard via the VisualViewport API and lifts the composer above it. Robust against URL-bar collapse, guards against pinch-zoom (scale !== 1), and dispatches a syntheticresizeso floating-ui re-anchors popovers. Android is handled natively viainteractive-widget=resizes-content. The lift is suppressed while the model picker is open to avoid a double-shift.Touch affordances — 44px touch targets on composer controls (
max-md:size-11),:activepress feedback on session rows (touch has no hover), and stacked settings rows belowmdso phone-width rows don't crush label + control onto one line.Page gutters — settings/bot-detail pages step down to 16px gutters below
md(desktop ≥mdpixel-identical).Submodule dependency
This PR bumps
packages/uito commits that live in felinics/ui#14 (mobile gutter, wrapping page header, 16px menu search input, hover-chip gating behind@media (hover: hover)). ui#14 must land first, or the gitlink here dangles. If ui#14 is squash-merged, the gitlink needs a one-line bump to the squashed SHA afterward.Test plan
+opens the panel menu; items respect permission gatesmd; ≥mddesktop unchangedmdNote: onboarding/real-device keyboard behavior is exercised in Playwright (fake VisualViewport) but the true iOS keyboard animation/caret-scroll needs a human on hardware.