fix(app): paint a long user message collapsed from its first frame

MessagePreview learned it overflowed from onLayout, which arrives after
the mount has painted on web and Fabric. Every mount (opening the chat,
a prompt arriving, a row the list mounts again) painted the text hard-cut
with no fade or Show more, then grew the row by the control a frame later.
The expanded choice also lived in the row and reset with each remount.

Measure the body in a layout effect so the first commit carries the
control, and keep the expanded choice in a per-agent store the timeline
provides, as tool calls already do.
This commit is contained in:
Mohamed Boudra
2026-11-10 12:52:04 +01:00
parent c91948048b
commit ebdf2f72cb
10 changed files with 398 additions and 49 deletions
+13
View File
@@ -39,6 +39,19 @@ The trigger can only stay put when the list has scroll room for the change. When
content fits the viewport, or a collapse at the end would need an offset past the end, the list
ends at its end and the trigger moves by the difference.
## State that survives a remount
The list mounts a row again whenever it opens the chat, brings a web row back into the history
window, or reassigns a native row's LegendList container. State held in the row resets each time.
Keep a disclosure's open state in the per-agent store the timeline provides, keyed by item id:
`ToolCallExpansionContext` for tool calls, `UserMessageExpansionContext` for long user messages.
Decide a row's first painted shape in the commit that mounts it. `MessagePreview` measures its body
in a layout effect, which commits before paint on web and Fabric. `onLayout` lands after the first
frame: a long message measured there paints capped with no fade or show more, then grows by the
control's height a frame later. `onLayout` still tracks later size changes. A zero height comes from
a hidden panel and is ignored.
## Web
`holdRow` pins the row's trigger edge in the reading anchor and runs `apply`; the next reading