Skip to content

fix(site/src/pages/AgentsPage): use h-dvh on root to fix mobile keyboard layout#24848

Closed
bpmct wants to merge 1 commit into
mainfrom
bpmct/fix-mobile-keyboard-agents-chat
Closed

fix(site/src/pages/AgentsPage): use h-dvh on root to fix mobile keyboard layout#24848
bpmct wants to merge 1 commit into
mainfrom
bpmct/fix-mobile-keyboard-agents-chat

Conversation

@bpmct
Copy link
Copy Markdown
Member

@bpmct bpmct commented Apr 30, 2026

Summary

On iOS, the virtual keyboard does not shrink the layout viewport. h-full (= 100% of the layout viewport) therefore keeps the root div at the full device height even after the keyboard appears. The browser then auto-scrolls the page to bring the focused chat input into view, which pushes the oldest/first messages above the visible fold.

Replacing h-full with h-dvh (100% dynamic viewport height) fixes this. dvh tracks the visual viewport, which shrinks when the keyboard opens on both iOS and Android. The container fits naturally in the remaining visible area, the input sits at the bottom above the keyboard, and no browser scroll adjustment is needed.

On desktop and Android Chrome the change is effectively transparent: dvh equals svh/lvh when no keyboard is present, and Android already resizes the layout viewport on keyboard open.

Change

- <div className="flex h-full min-h-0 flex-col overflow-hidden bg-surface-primary md:flex-row">
+ <div className="flex h-dvh min-h-0 flex-col overflow-hidden bg-surface-primary md:flex-row">

h-dvh is a Tailwind v3.3+ built-in (height: 100dvh). The project uses Tailwind v3.4.18, so no extra configuration is needed.


Generated by Coder Agents.

…ard layout

On iOS, the layout viewport does not shrink when the virtual keyboard
opens. This means h-full (= 100% of layout viewport) keeps the root at
the full device height (e.g. 844px) even after the keyboard appears.
The browser then auto-scrolls to bring the focused input into view,
which pushes the first/oldest messages off the top of the screen.

Replacing h-full with h-dvh (100% dynamic viewport height) fixes this.
dvh tracks the visual viewport on iOS, so the root shrinks when the
keyboard opens and the layout naturally fits in the visible area. The
input sits at the bottom above the keyboard with no browser scroll
intervention needed. On Android and desktop, dvh behaves identically
to svh/lvh and there is no visible change.
bpmct added a commit that referenced this pull request May 5, 2026
…at scroll keeps working (#24950)

Linear:
[CODAGT-313](https://linear.app/codercom/issue/CODAGT-313/unable-to-scroll-long-queued-messages-in-coder-agents)

## Summary

When many messages are queued in the agent chat, the chat history
becomes unscrollable: mouse wheel and scrollbar drag both stop
responding.

The input wrapper in `AgentChatPageView.tsx:496` is `shrink-0
overflow-y-auto` with **no `max-height`**, so `overflow-y-auto` is a
no-op and the section grows unbounded as `QueuedMessagesList` adds rows.
Its sibling `ChatScrollContainer` is `flex-1 min-h-0`, so it absorbs the
shrinkage and `clientHeight` collapses to 0. The chat list is then a
zero-height viewport with nothing to scroll.

Measured against the actual `AgentChatPageView` rendered in Storybook
with 20 queued messages (1280x800):

| | scroll-container `clientHeight` | input wrapper height | scrollable?
|
|---|---:|---:|---|
| 0 queued | 502 px | 270 px | yes |
| 20 queued, `main` | **0 px** | 1182 px | **no** |
| 20 queued, this PR | 258 px | 502 px | yes |

## Demo

![scroll fix
side-by-side](https://raw.githubusercontent.com/coder/coder/bpmct/codagt-313-assets/scroll-fix-side-by-side.gif)

Left (`main`): wheel-up does nothing because the chat scroll container
has been crushed to zero height.
Right (this PR): the queued list scrolls inside its own pane and the
chat history scrolls normally.

Recording is `AgentChatPageView` rendered through Storybook with the
production component source. The same gesture (wheel-up over the chat
history, then wheel-down over the queued list) is applied to both sides.
Source for the recording is in `bpmct/codagt-313-assets`.

## Change

```diff
-		<div className={cn("flex w-full flex-col", className)}>
+		// Cap the queue at ~40% of the small viewport so a long queue
+		// does not push the chat history's scroll container down to
+		// zero height (CODAGT-313). The list scrolls inside its own pane.
+		<div
+			className={cn(
+				"flex w-full flex-col max-h-[40svh] overflow-y-auto [scrollbar-gutter:stable] [scrollbar-width:thin] [scrollbar-color:hsl(var(--surface-quaternary))_transparent]",
+				className,
+			)}
+		>
```

## Why this spot, not the outer wrapper

The composer textarea already self-caps at `max-h-[50vh]` in
`ChatMessageInput.tsx:688`, so the only unbounded growth source in the
input section is the queued list. Capping the list keeps the constraint
colocated with the component that owns it, and any future consumer of
`QueuedMessagesList` is automatically safe.

`40svh` (small viewport height) so the queue doesn't fight with the iOS
keyboard once it appears, matching the `h-dvh` decision in #24848.

---

*Generated by Coder Agents.*
@github-actions github-actions Bot added the stale This issue is like stale bread. label May 14, 2026
@github-actions github-actions Bot closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale This issue is like stale bread.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant