Skip to content

FlowChat: bottom whitespace grows after subagent card collapses and only drains on manual scrolling #2154

Description

@YodonTan

Problem

After a subagent (Task) card collapses, the chat viewport keeps a large block of blank space below the last message. The blank area grows with each subagent card event and only drains after the user manually scrubs the scroll wheel up and down repeatedly (each round trip consumes only the upward-scroll distance).

Screenshot: the failed "Explore" card is fully collapsed (compact header), the scrollbar thumb sits at the physical bottom, yet ~750-800px of empty background separates the message from the input bar.

Root cause

Collapse-intent pre-compensation adds provisional footer space (collapse.px) before the card shrinks. When the user is at the physical bottom, every settlement path dead-locks:

  • settleCollapseReservationForViewport / retained quiet settlement compute the geometric minimum for the current scrollTop, but the synthetic footer itself holds scrollTop at the physical bottom, so the minimum recomputes exactly the current reservation (e.g. 786px stays 786px).
  • The scroll handler's per-scroll consumption never fires at the physical bottom because scrollDelta is 0 (scrollTop is capped by the footer).
  • The idle reservation audit settles to the same value, so it cannot help either.

Multiple collapse intents (e.g. several parallel subagent failures) stack provisional estimates on the unsettled reservation (base = current total compensation), which is why the whitespace grows monotonically during the session.

Fix

convergeBottomReservationsToContentBottom: when the scroller sits at the physical bottom (within 2px) with no preserving-element / pinned-item viewport owner and no active collapse intent, clear collapse + pin reservations in one synchronous footer update and fall scrollTop to the new content bottom (the delayed browser clamp compensation was hiding). Wired into:

  • retained quiet settlement
  • collapse-intent finalization (non-pinned strategies)
  • idle reservation audit (bottom users only; mid-list readers keep their reading anchor)
  • scroll handler: any scroll event at the physical bottom is bottom intent - one pass replaces per-scrub consumption
  • collapse-intent accumulation guard: a new provisional estimate no longer stacks on a settled bottom reservation
  • input-stack-shrink drain

Also: bound collapse-intent finalize retries (20) so a virtualized sticky-pin target cannot keep the intent active (and auto-follow suspended) forever; intent-window growth consumption is capped at the measured protection line so subagent growth no longer sediments into footer whitespace.

Verification

Component tests cover bottom convergence (finalize / scroll / audit), negative cases (mid-list, pinned-item, preserving-element must not clear reservations), non-stacking consecutive intents, growth protection during an active intent, and bounded force-settle at mid-list. Requires manual verification with a real streaming session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions