fix(chat): serialize scroll follow and timeline jumps (#1322, #1323) (#1359)

This commit is contained in:
m57 authored and GitHub committed 2026-09-28 15:59:29 +03:00
1 parent 58794bee00
commit cc98f3ba89
5 files changed
+142 -58

No files matched your search

@@ -3,6 +3,8 @@ package com.m57.hermescontrol.ui.chat.fullbleed
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertCountEquals
@@ -12,6 +14,9 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -25,6 +30,7 @@ import com.m57.hermescontrol.ui.chat.ToolStatus
import com.m57.hermescontrol.ui.chat.components.ChatScrollController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.junit.Assert.assertFalse
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,9 +78,18 @@ class FullBleedChatListTest {
showUserMessageTokens: Boolean = true,
showAssistantMessageTokens: Boolean = true,
showTokensPerSecond: Boolean = true,
onController: (ChatScrollController) -> Unit = {},
) {
composeTestRule.setContent {
val listState = LazyListState()
val listState = remember { LazyListState() }
val scrollController =
remember(listState) {
ChatScrollController(
listState = listState,
scope = CoroutineScope(Dispatchers.Main.immediate),
)
}
SideEffect { onController(scrollController) }
FullBleedChatList(
transcript =
TranscriptUiState.resolve(
@@ -106,15 +121,24 @@ class FullBleedChatListTest {
actions = testTranscriptActions(),
searchState = ChatSearchState(),
listState = listState,
scrollController =
ChatScrollController(
listState = listState,
scope = CoroutineScope(Dispatchers.Main.immediate),
),
scrollController = scrollController,
)
}
}
@Test
fun upwardUserGesture_pausesBottomFollow() {
lateinit var scrollController: ChatScrollController
render(
messages = List(40) { index -> msg("u$index", MessageRole.USER) },
onController = { scrollController = it },
)
composeTestRule.runOnIdle { scrollController.jumpToBottom() }
composeTestRule.waitForIdle()
composeTestRule.onRoot().performTouchInput { swipeDown() }
composeTestRule.runOnIdle { assertFalse(scrollController.isFollowingBottom) }
}
@Test
fun userMessages_keepBubble_agentProse_isFullBleed() {
render(