fix(web-ui): keep the composer card synced when the input wraps - #1802
Merged
bobleer merged 1 commit intoJul 27, 2026
Merged
Conversation
At the exact point where the session input wraps to a second line, the capsule -> multi-line flip left the card and its controls out of sync. - The box transitioned `max-height` 44px -> 500px while its `height` stays `auto`, so mid-transition the painted border box was clamped below its own content. `overflow` must stay visible for the popovers, so the text and the whole model/mic/send row painted outside the rounded border. Measured on the same rule set: the action row sat 55px below the painted box bottom at the start of the transition. Only `min-height` is animated now - it can never fall below the content, so the collapse still animates and the card never clips. - `__actions-left` / `__actions-right` re-ran `bitfun-stacked-reveal` on every flip (capsule mode nulled it out, so it never played on mount). With `both` fill the groups were pinned at `opacity: 0` through the 0.17s / 0.22s delay and finished sliding ~0.45s after the box had already resized. - The ResizeObserver reacted to the animating box height, and each callback cloned the whole composer into the document to re-measure the capsule width (two forced layouts per frame of the transition). It now ignores entries whose width is unchanged. Trade-off: growth is no longer animated. A CSS height animation from small to large always clamps the box below its content; smooth growth would need `overflow: hidden` during the transition, which would clip the mention and model popovers. The radius, padding, shadow and border-color transitions still carry the shape morph.
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.
Summary
At the exact point where the session composer wraps to a second line, the capsule → multi-line flip left the card and its controls out of sync: the text and the
glm-5.2 / mic / sendrow rendered outside the rounded border, and the controls only caught up ~0.45s later.Three causes, all on that one transition:
.bitfun-chat-input__boxtransitionsmax-heightfrom44px(capsule) to500px(multi-line) while itsheightstaysauto. Mid-transition the painted border/background is stuck at the interpolatedmax-heightwhile the children already occupy the full multi-line height, andoverflowmust stay visible for the mention/model popovers — so the content paints outside the card. Measured on the same rule set: the action row sat 55px below the painted box bottom at the start of the transition (23px at ~30% progress), versus 1px inside once unclamped.__actions-left/__actions-rightcarriedanimation: bitfun-stacked-reveal … 0.17s both/… 0.22s both, and capsule mode nulled it out — so it never played on mount, it only restarted on each flip.bothfill pins thefromkeyframe (opacity: 0), leaving the plus button and the model/mic/send cluster blank for ~0.2s and still sliding ~0.45s after the box had already resized. At the wrap boundary a single keystroke replays all of it.ResizeObserverwatches the box, whose height animated every frame; each callback ranmeasureCapsuleInputWidth(), which clones the entire composer into the document and forces two layouts — roughly 20 clone+layout cycles per transition, exactly while the user is typing.Fixes:
max-heightfrom the box transition and keptmin-height.min-heightcan never fall below the content, so expansion is instantly correct and collapse still animates.bitfun-stacked-revealanimation from both action groups (plus the now-deadanimation: noneoverrides and the unused keyframes) so the controls resize with the card.ResizeObserverentries whose width is unchanged, so the height animation no longer triggers the clone-based re-measure.Type and Areas
Type: bug fix (UI/UX)
Areas: web UI (
src/web-ui/src/flow_chat/components/ChatInput.{scss,tsx})Motivation / Impact
Typing at the wrap boundary is a very common state, and every keystroke that crosses it replayed a visibly broken transition — the composer's own text and controls escaping the card. After the change the card and its controls always move together, and the transition stops forcing ~20 full-composer clone measurements while the user types.
Verification
Behavioral check: reproduced the box's rule set in a browser and sampled
getBoundingClientRect()for the card and the action row at intermediate clamp values.max-heightanimated)min-heightonly)No focused unit test was added: the defect lives entirely in CSS transition behavior, which the jsdom-based suite cannot observe (no layout).
Reviewer Notes
Trade-off worth a look: growth is no longer animated. A CSS height animation from small to large always clamps the box below its content, so smooth growth would require
overflow: hiddenduring the transition — which would clip the mention and model-selector popovers. Theborder-radius,padding,box-shadowandborder-colortransitions still carry the shape morph, and collapse still animates viamin-height.Collapse timing is unchanged (
min-heightstill 0.32s), so the collapse-protection windows inVirtualMessageList(1000ms / 1500ms intents) are unaffected. Expansion now grows the Virtuoso footer in one step instead of ~20 — growth does not trigger thescrollTopclamp that the shrink path guards against.AI-assisted change. Testing level: lightly tested — checks above pass and the geometry was measured in a browser, but I have not run the full desktop app to capture before/after screenshots of the real composer. The reported behavior matches the measurements exactly.
Checklist