storytellurx/reports/text-jump-diagnosis.md

4.8 KiB

Prose start/end jump diagnosis

Historical first-pass diagnosis. A later two-turn/manual-reading replay also identified a transient native error at completion, which these initial replays missed. See completion correction.

Four offline phone replays: Nelly and Polaire, each with normal and reduced motion. The neutral recorded library text was repeated eight times and paced in synthetic 350-character deltas to expose overflowing text. A requestAnimationFrame probe measured every rendered frame. Changed-frame evidence. These measurements identify reproducible causes; chunk sizes and absolute distances can differ in a real conversation.

Moment Measured movement Owning behavior
First prose chunk Header 108 → 80px; transcript top moves up 28px in both personas The streamed short title replaces a two-line title with one line. conversation-status.tsx:98 renders it immediately; conversation-status.stylex.ts:59 gives each line 28px. Header height is natural, so the entire viewport moves. No scroll-direction toggle occurs.
Growing prose first exceeds the viewport Nelly: text height grows 115.5 → 250.25px; scroll stays 0 for a frame, then jumps to 113px about 33ms later. The same paragraph moves from y=533.5 to y=420.5. Native MessageScroller content ResizeObserver schedules a requestAnimationFrame, then performs scrollToEnd({ behavior: 'auto' }). This produces a visible layout update followed by a discrete bottom correction. Subsequent chunks repeat the pattern.
Suggestions arrive after prose Polaire composer 51 → 85px; viewport 597 → 563px; about 33ms later scrollTop 587 → 621px and unchanged prose y=-130 → -164px narrative-composer.tsx:59 mounts the suggestion rail. Polaire's composer participates in normal flow. The native viewport ResizeObserver then follows the new bottom.
Native run completes No measured geometry change in any of the four replays aria-busy changes true → false after the preceding movements. The final structured-output transition is not itself the cause in these fixtures.

Nelly's overlay composer also grows, 80 → 152px, when suggestions arrive. In this replay it does not move the prose: its existing 160px transcript padding already covers that height. Thus the end-adjacent jump depends on persona layout.

The native observer/scroll implementation is in node_modules/@shadcn/react/dist/message-scroller/index.js:2: Content's observer (pt), Viewport's observer (Rt), resize handler (Ce), and scroll call (V). Our current local patch changes user-intent marker clearing only; it does not introduce those observers, their animation-frame delay, or their automatic scroll behavior.

The earlier settled-sample checks missed the two-frame correction. Their normalized transcript coordinates intentionally subtract scrolling; a stable content coordinate does not prove a stable on-screen reading position. The compact receipt tests correctly establish that closed rationales do not grow, but do not establish that the surrounding viewport is stable.

Not reproduced here: a standalone completion jump with unchanged title, composer, content and viewport. Image insertion, persona handoff, title wrapping in other locales, and a real conversation's existing scroll position remain separate possible contributors. No new UX fix or fixed-height reservation is proposed or applied in this diagnosis.