This commit is contained in:
2026-07-20 17:01:19 +08:00
parent 83c031930b
commit 9e0eddaf4a
3 changed files with 21 additions and 7 deletions
+1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">
@@ -418,9 +418,16 @@ private fun MessagesArea(
}.collect { nearBottom -> autoScroll = nearBottom } }.collect { nearBottom -> autoScroll = nearBottom }
} }
// Instant scroll (not animated) so it doesn't fight with user drag gestures
LaunchedEffect(state.streamContent, state.streamReasoning, messages.size, state.isStreaming) { LaunchedEffect(state.streamContent, state.streamReasoning, messages.size, state.isStreaming) {
if (autoScroll && totalItems > 0) { if (autoScroll && totalItems > 0) {
listState.animateScrollToItem(totalItems - 1) if (state.streamContent.isNotBlank()) {
// Content phase: scroll to bottom of the streaming message
listState.scrollToItem(totalItems - 1, 100000)
} else {
// Reasoning phase: keep streaming message header visible
listState.scrollToItem(totalItems - 1)
}
} }
} }
@@ -484,7 +491,7 @@ private fun MessagesArea(
.border(1.dp, c.border, CircleShape) .border(1.dp, c.border, CircleShape)
.clickable { .clickable {
autoScroll = true autoScroll = true
scope.launch { listState.animateScrollToItem(totalItems - 1) } scope.launch { listState.animateScrollToItem(totalItems - 1, 100000) }
}, },
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
@@ -633,6 +640,13 @@ private fun StreamingMessage(content: String, reasoning: String, onCopy: () -> U
private fun ReasoningCard(reasoning: String, isStreaming: Boolean) { private fun ReasoningCard(reasoning: String, isStreaming: Boolean) {
val c = LocalAppColors.current val c = LocalAppColors.current
var collapsed by remember { mutableStateOf(false) } var collapsed by remember { mutableStateOf(false) }
val scrollState = rememberScrollState()
// Auto-scroll reasoning text to bottom as new content arrives
LaunchedEffect(reasoning) {
if (isStreaming && !collapsed) {
scrollState.animateScrollTo(scrollState.maxValue)
}
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -663,7 +677,7 @@ private fun ReasoningCard(reasoning: String, isStreaming: Boolean) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(max = 320.dp) .heightIn(max = 320.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(scrollState)
.padding(horizontal = 12.dp, vertical = 8.dp) .padding(horizontal = 12.dp, vertical = 8.dp)
) { ) {
SelectionContainer { SelectionContainer {
@@ -763,15 +777,14 @@ private fun ComposerBar(state: ChatUiState, onSend: (String) -> Unit, onStop: ()
Box( Box(
modifier = Modifier modifier = Modifier
.size(44.dp) .size(44.dp)
.clip(RoundedCornerShape(12.dp)) .shadow(6.dp, RoundedCornerShape(12.dp), clip = true)
.background(c.accentBrush) .background(c.accentBrush)
.shadow(6.dp, RoundedCornerShape(12.dp), clip = false)
.clickable(enabled = input.isNotBlank()) { .clickable(enabled = input.isNotBlank()) {
onSend(input); input = "" onSend(input); input = ""
}, },
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "发送", tint = Color.White) Icon(Icons.AutoMirrored.Filled.Send, contentDescription = "发送", tint = Color.White, modifier = Modifier.size(22.dp))
} }
} }
} }