1 Commits
Author SHA1 Message Date
842b2ac471 release: composer footer-fit jitter fix (#7275) (#7483)
* fix(composer): stop footer fit from jittering the transcript during SSE

_fitComposerFooter() measured overflow by REMOVING the .cf-icons/.cf-burger
stage classes, reading scrollWidth, then adding them back. Between those two
steps the footer is laid out at full width: the composer grows a few px and
#messages loses the same amount of clientHeight, then gets it back on the
next frame.

For a pinned reader that is a visible up/down jitter of the whole transcript,
because the fit pass runs on every context-indicator update — which fires
continuously while an SSE turn is streaming.

Measured with a real Chromium probe instrumenting the scrollTop setter,
scroll events, ResizeObserver and MutationObserver:

  before: 4 non-JS backward jumps of exactly -8px per streamed turn,
          each one ~30ms after a `ctx-indicator-wrap` -> `composer-footer`
          class mutation pair
  after:  0 backward jumps over 5 streamed turns on 2 different sessions

Fix: freeze the footer's layout box (explicit height + visibility:hidden)
for the duration of the measurement, so the intermediate expanded geometry
is never committed to the screen, and restore the resolved stage in the same
task. The adaptive behaviour is unchanged — verified across 8 viewport
widths (1600 -> 420px), with the correct full/icons/burger stage at each and
no residual inline style left behind.

Note: the guard added in the tail-jitter work is what keeps the reader pinned
here; before it, the same footer jitter unpinned the reader instead, which
masked the oscillation behind a worse bug.

* test(composer): cover footer fit freeze across full/icons/burger stages

Add a behavioural node driver test for _fitComposerFooter() that runs the
actual function from static/ui.js against a small layout model where the
stage classes dictate the footer's natural height and the left cluster's
content width. Every class mutation, inline-style write and overflow
measurement commits a layout sample, so the recorded footer heights and
messages clientHeights are what a browser would have painted.

The matrix starts from each stage (full, icons, burger), forces each
overflow outcome, and runs with empty and caller-owned prior inline
styles. It asserts the resolved stage classes, that the footer border-box
height and messages clientHeight stay pinned while the ladder is probed
(and do not move at all in the steady state), that no intermediate stage
is ever painted, and that prior inline height/visibility are restored
verbatim. It also covers the zero-height skip and an exception during
measurement, and includes a control run with the freeze made ineffective
to prove the harness reports the original jitter. Against the pre-fix
ui.js the height, jitter, painted-stage and style-restoration assertions
fail.

* docs(changelog): stamp #7275 composer footer-fit jitter fix

* ci: re-trigger (playwright browser install hung on GH runners)

* ci: re-trigger (google chrome apt mirror hash-sum mismatch, external infra)

---------

Co-authored-by: Anthony Ruiz <ruizanthony@users.noreply.github.com>
Co-authored-by: n <a@n>
2026-09-09 18:11:21 +00:00