git.iko.soy/opencode-android
on master help sign in
Fix chat crash by skipping animateItem on the actively-streaming message row
commit2987128bcc83794ecbb03c108e3d4c044b4b858d authoriko <mail@iko.soy> dateTue 7 Jul 2026, 18:14 (3 months ago) parent121b3a3
1 file changed+19−1
app/src/main/java/soy/iko/opencode/ui/chat/ChatScreen.kt+19−1
app/src/main/java/soy/iko/opencode/ui/chat/ChatScreen.kt+19−1
@@ -1486,9 +1486,27 @@ fun ChatScreen(
14861486 // Default placement animation so inserted/moved rows glide in. Skipped
14871487 // entirely under reduced motion so a streaming message growing in place
14881488 // doesn't animate on every token (and respects the a11y preference).
1489+ //
1490+ // Also skipped for the actively-streaming message even at full motion.
1491+ // animateItem() is backed by SizeAnimationModifierNode, which forces a
1492+ // draw-phase remeasure (the display-list rebuild triggered by
1493+ // AndroidComposeView.dispatchDraw) when its target's size changes. While
1494+ // the streaming bubble grows token-by-token, that remeasure runs inside
1495+ // the draw pass and reads Placeable.getWidth()/getHeight() of a child
1496+ // whose measured size hasn't been committed yet — an NPE in
1497+ // PaddingValuesModifier.measure / FillNode.measure (the R8 map marks both
1498+ // sites with a throws(NullPointerException) rewriteFrame). This is the
1499+ // same class of draw-phase-remeasure crash previously fixed for the
1500+ // markdown library's animateContentSize and MessageBubble's send-status
1501+ // AnimatedContent; animateItem() on a continuously-resizing row is the
1502+ // remaining instance. Non-streaming rows (including the one that just
1503+ // finished streaming) keep the glide animation since their size is stable.
1504+ val isStreamingMessage = item is MessageListItem.Message &&
1505+ running &&
1506+ item.message.info.id == lastMessageId
14891507 Box(
14901508 Modifier
1491− .then(reducedMotionAnimateItem())
1509+ .then(if (isStreamingMessage) Modifier else reducedMotionAnimateItem())
14921510 .then(
14931511 if (isFocusedMatch) {
14941512 Modifier.background(