storytellurx/reports/text-jump-diagnosis.md

20 lines
4.8 KiB
Markdown

# 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](chat-composition.md#completion-interruption-corrected-at-the-source).
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](text-jump-diagnosis.json). 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.