Skip to content

fix(chat): keep composer visible with long queued-message lists - #1429

Open
wowayou wants to merge 2 commits into
agegr:mainfrom
wowayou:fix/chat-queued-messages-overflow
Open

wowayou wants to merge 2 commits into
agegr:mainfrom
wowayou:fix/chat-queued-messages-overflow

Conversation

@wowayou

@wowayou wowayou commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1428

Reproduction and root cause

Independently reproduced against current main (e533d9b) in a separate worktree, using the new browser fixture with no component/CSS changes. At 1280×600, 19 synthetic steering messages plus retry feedback put the textarea at y=619.75–643.75: outside the visible viewport, with its hit target inaccessible. The same assertion passes with this change.

Both queue maps were rendered directly into an unbounded panel. The fieldset and its chat wrapper could not shrink, while the transcript scrolled independently and the document had overflow: hidden. Scrolling history could therefore never recover the composer controls.

Implementation and trade-offs

  • Keep the count/header and Recall outside a separately scrollable rows region. Cap rows at min(180px, app viewport × 25%), using the existing --app-viewport-height with a 100dvh fallback. Unlike plain dvh, this respects keyboards that shrink only the visual viewport.
  • Make the rows region labeled/focusable; wheel scrolling and desktop Home/End reach both ends. Keep both maps, order, command handlers and recall/draft semantics unchanged.
  • Bound the composer wrapper to the chat height and allow its fieldset/inner column to shrink. Existing model notices, retry and compaction feedback share a bounded scrollable diagnostic shelf, retaining their existing content/styles. Empty shelves take no space.
  • A list-only cap still failed with long diagnostics and a tall draft. Let those supplemental regions absorb the shortfall, preserve a queue header/complete row, and reduce the draft's existing 200px cap proportionally in short viewports. With the phone keyboard open, the cap is 20% (14% with a queue). These same caps apply independently in narrow/coarse-pointer layouts at most 500px high: interactive-widget: resizes-content shrinks both viewports, leaving no height difference for the keyboard marker. This covers both resize modes without hiding buttons or changing keyboard detection.
  • Keep the running phone controls/Stop available through the existing More controls row while the keyboard is open. Mirror layout changes and the keyboard-open marker in the static demo.

No dependencies, settings, transport changes, queue management features, SDK changes, model requests or retries added. Uses existing theme and scrollbar styles. No build run in the development checkout.

Changed files

  • components/ChatInput.tsx, components/ChatWindow.tsx, app/globals.css: bounded queue/composer and short-viewport handling.
  • demo/components/ChatInput.tsx, demo/components/ChatWindow.tsx, demo/app/globals.css, demo/hooks/useViewportHeight.ts: corresponding demo behavior.
  • components/ChatInput.test.mjs: empty/3/19/50 queues, count/order/recall rendering, immutable queue data.
  • e2e/queued-messages.mjs, e2e/run.mjs: real rendered components with intercepted agent transport/catalog, layout/hit-testing, actual command clicks, scrolling and draft assertions.
  • e2e/README.md, docs/agents/client-platform.md: reproduction, coverage, layout constraints and remaining device-verification gaps.
  • docs/screenshots/queued-messages-{before,after}.png and queued-messages-resize-{before,after}.png: synthetic-data before/after evidence for the original queue overflow and the additional layout-resize failure.

Verification actually run

Tests ran with an isolated temporary HOME/agent directory and no real credentials. Provider/agent commands in the queue browser tests are intercepted, not executed.

Check Actual result
npm test 2870 passed, 0 failed, 1 Windows-junction test skipped on Linux (2871 total)
node_modules/.bin/tsc --noEmit Passed
npm run lint Passed
git diff --check Passed
npm run test:e2e (Turbopack dev server) Entire suite passed, including the new queue regression and existing browser regressions
cd demo && node_modules/.bin/tsc --noEmit Passed
cd demo && npm run lint Passed
One-off Playwright check against the running demo Passed at 1280×600 and touch 390×844: empty/3/19/50 mixed queues, retry/long diagnostics, draft preservation, reduced 400px window plus 450/340/280px visual-only and layout+visual viewport shrink, first/last queue access, original-height restoration, and an actual Stop click at every short height

The queue browser regression covers 1440×900, 1366×768, 1280×600, touch 390×844, plus 400px desktop heights and 450/340/280px in two independent resize modes: visual-only shrink (including a late keyboard-height change without a resize event), and layout+visual shrink using page.setViewportSize. The latter asserts that the keyboard marker/height override remain unset. It checks bounds, hit targets, actual Steer/Follow-up/Recall/Stop command payloads at each short height, internal overflow, first/last row access, queue growth/shrink/order, unchanged drafts, readable diagnostics, page position and restoration of the original viewport. Short idle layouts without a queue also preserve the textarea/Send and draft. Existing idle composer/model-picker regressions pass too.

Expected failures recorded before the fix: original main overflow; list-cap-only candidate with long diagnostics; keyboard candidates with Stop clipped. One final rerun also encountered a pre-queue fixture route 404 in the reused Turbopack cache; after preserving .next in a temporary backup and rerunning from a clean dev cache, the entire suite passed. No route or test assertion was weakened to bypass it.

origin/main was fetched again immediately before submission and remains e533d9b; no upstream changes to reconcile.

Before / after

Same 1280×600 synthetic fixture, 19 steering messages and retry feedback. No user sessions, credentials or provider details.

Before (main) After
Before: queue pushes composer below viewport After: queue scrolls and composer remains visible

Acceptance follow-up: layout viewport shrink

The first PR implementation (7eb8a97) did not fully satisfy A08/A09. With 50 mixed queued messages, retry/long model diagnostics and a tall draft, shrinking both viewports to 390×340 put Stop at y=318.5–350.5 (outside 340px); at 390×280, Steer extended to 288.5px. The keyboard-marker-only CSS was inactive because innerHeight and visualViewport.height shrank together.

The committed regression was extended before the CSS fix and reproduced the 340px failure. Adding only short-layout CSS constraints in the app and demo makes the same test pass at 450/340/280px, without forcing a keyboard marker, hiding controls, changing interactive-widget, or touching the hook/SDK/queue logic. Both resize modes exercise command clicks and preserve scrolling/drafts on restore.

Before short-layout fix (390×340) After
Before: layout resize clips Stop After: short-layout limits keep controls visible

The final one-off demo check initially failed on a README image's ERR_CONNECTION_RESET from GitHub's image host. Its test fixture then served the same checked-in docs/screenshot2.png locally and passed; no production fallback or error suppression was added.

Remaining risks / unverified scenarios

  • Browser layout verified in Linux Chromium; Windows Chrome, Safari/iOS 16.2 and physical-device software keyboards were not tested. Keyboard checks simulate visual-only or combined viewport shrink, not a native keyboard animation.
  • The production-build E2E mode was not run locally (per the development no-build rule); the existing PR CI owns that check.
  • Extremely small viewports below the tested 280px visual height, unusually large text scaling and large attachment-preview stacks are not covered. The fix deliberately does not expand into attachment/layout redesign or queue management.
  • No deployment or auto-merge requested. Existing public-repository PR workflows use standard Ubuntu runners with existing timeouts; no workflow/billing configuration changed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(chat): long queued-message list pushes composer below viewport

1 participant