fix(chat): 附件芯片改换行排布 —— 多附件不再被裁到屏幕外
主人 2026-10-04 在主会话里发现「用户消息显示有问题」:带 2~3 个附件的用户消息里, 第一枚芯片就吃掉了整排——服务端存储名是 32 位十六进制 + 扩展名(如 `36a4aba0…5524808.txt`), 一枚约 267dp,而芯片排是「横向可滚的单行 + `widthIn(max = 320.dp)`」,于是第二枚只剩一条边 露在屏幕外。看的人不知道还有别的附件,也看不出那一行可以滚。 改为 `FlowRow` 换行排布(用户消息整排贴右 `alignEnd`),N 枚芯片全部可见;代价是 5 枚会占 2~3 行——比「藏起来」值。去掉随之失效的 `CHIP_ROW_MAX_WIDTH` 与两个 import, 并同步 `ChatScreen` 里待发芯片排的注释(同一处行为)。 实机复核:主会话那条三附件消息现在渲染为三行,三枚芯片与各自的 「TXT · 4 B · 4 字 · 已读入」全部可见(截图 `app/build/e2e-shots/chips-after.png`)。
This commit is contained in:
1 parent
2369dd2115
commit
ca5596a732
2 files changed
+19
-15
No files matched your search
@@ -363,7 +363,7 @@ private fun InputBar(
|
||||
}
|
||||
}
|
||||
|
||||
/** 待发附件的芯片排:横向可滚(5 枚芯片不挤成两行,也不会把输入框顶上去)。 */
|
||||
/** 待发附件的芯片排:换行排布(5 枚最多占 3 行;宁可占点高度,也不把附件藏进横向滚动里)。 */
|
||||
@Composable
|
||||
private fun DraftDocumentChips(documents: List<DraftDocument>, onRemove: (Int) -> Unit) {
|
||||
DocumentChipRow(
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package top.yeij.cyrene.ui.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -41,9 +40,6 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
/** 流式进行中的光标。 */
|
||||
private const val CURSOR = "▍"
|
||||
|
||||
/** 附件芯片排的最大宽度:与气泡同级,避免 5 枚芯片把整行铺满屏幕。 */
|
||||
private val CHIP_ROW_MAX_WIDTH = 320.dp
|
||||
|
||||
/**
|
||||
* 一条落库消息渲染成一个或多个气泡:
|
||||
* - `mode` 为 `chat`(或缺失——后端当前不落库 mode,一律回落 chat)时按 `<p>` 切分,
|
||||
@@ -70,7 +66,7 @@ fun MessageBubble(message: HistoryMessageDto) {
|
||||
|
||||
MessageGroup(isUser = isUser, footer = messageSourceLabel(message, LocalEnumCatalog.current)) {
|
||||
message.thinking?.takeIf { it.isNotBlank() }?.let { ThinkingCard(it) }
|
||||
DocumentChipRow(chips = message.documents.map(DocumentChips::of))
|
||||
DocumentChipRow(chips = message.documents.map(DocumentChips::of), alignEnd = isUser)
|
||||
message.image?.takeIf { it.isNotBlank() }?.let { ChatImage(it) }
|
||||
bubbles.forEachIndexed { index, bubble ->
|
||||
Bubble(bubble, isUser, isLast = index == bubbles.lastIndex)
|
||||
@@ -88,16 +84,21 @@ fun MessageBubble(message: HistoryMessageDto) {
|
||||
@Composable
|
||||
fun PendingBubble(item: PendingMessage) {
|
||||
MessageGroup(isUser = true, footer = "发送中…") {
|
||||
DocumentChipRow(chips = item.documents.map(DocumentChips::of))
|
||||
DocumentChipRow(chips = item.documents.map(DocumentChips::of), alignEnd = true)
|
||||
item.imageUri?.takeIf { it.isNotBlank() }?.let { ChatImage(it) }
|
||||
if (item.text.isNotEmpty()) Bubble(plainBubble(item.text), isUser = true, isLast = true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件芯片排:横向可滚(最多 5 枚也不挤成两行),一枚一枚展示
|
||||
* 「名字(中略,保住扩展名)/ 类型 · 体积 · 字数 · 状态」。
|
||||
* 附件芯片排:**换行排布**——一枚放不下就落到下一行,N 枚都看得见。
|
||||
*
|
||||
* 它曾经是「横向可滚的单行 + `widthIn(max = 320.dp)`」,**实机一看就是坏的**(2026-10-04
|
||||
* 主人在主会话里发现):服务端存储名是 **32 位十六进制 + 扩展名**(`36a4aba0…5524808.txt`),
|
||||
* 一枚芯片就能吃掉 ~267dp,于是 320dp 的视口里第二枚只剩一条边露在屏幕外——看的人根本
|
||||
* 不知道还有第二个附件,也没人看得出那一行可以滚。换行之后 5 枚会占 2~3 行,比「藏起来」值。
|
||||
*
|
||||
* [alignEnd] = 用户消息(气泡在右侧):整排贴右;否则一枚短芯片会贴在该排左端、看着像错位。
|
||||
* [onRemove] 非空即带移除按钮——待发位(输入栏上方)要能反悔,历史气泡里则不给。
|
||||
*
|
||||
* 附件可能是空列表(绝大多数消息都是),此时**不占位、不留空白**。
|
||||
@@ -106,14 +107,17 @@ fun PendingBubble(item: PendingMessage) {
|
||||
fun DocumentChipRow(
|
||||
chips: List<DocumentChipModel>,
|
||||
modifier: Modifier = Modifier,
|
||||
alignEnd: Boolean = false,
|
||||
onRemove: ((Int) -> Unit)? = null,
|
||||
) {
|
||||
if (chips.isEmpty()) return
|
||||
Row(
|
||||
modifier = modifier
|
||||
.widthIn(max = CHIP_ROW_MAX_WIDTH)
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 6.dp,
|
||||
alignment = if (alignEnd) Alignment.End else Alignment.Start,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
chips.forEachIndexed { index, chip ->
|
||||
DocumentChip(model = chip, onRemove = onRemove?.let { remove -> { remove(index) } })
|
||||
|
||||
Reference in new issue
Block a user