feat(chat): 改为文本选择模式,全选仅作用于当前气泡

用 SelectionContainer 包住气泡/旁白/思维链/工具卡的正文,长按可拖选;移除长按整段复制的实现与其工具文件。
This commit is contained in:
AskaEth committed 2026-09-08 22:38:28 +08:00
1 parent 33770d87a6
commit 91117986e4
3 files changed
+29 -53

No files matched your search

@@ -1,32 +0,0 @@
package top.yeij.cyrene.ui.chat
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
/**
* 长按复制文本到系统剪贴板,并给一条 Toast 反馈。
* 用框架的 ClipboardManager 而不是 Compose 的 LocalClipboardManager(后者在新版本已弃用)。
*/
@Composable
fun Modifier.copyOnLongPress(text: String): Modifier {
if (text.isBlank()) return this
val context = LocalContext.current
return this.pointerInput(text) {
detectTapGestures(
onLongPress = { copyToClipboard(context, text) },
)
}
}
private fun copyToClipboard(context: Context, text: String) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("Cyrene", text))
Toast.makeText(context, "已复制", Toast.LENGTH_SHORT).show()
}
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -34,6 +35,9 @@ private const val CURSOR = "▍"
* - 其它 mode 原样整条渲染,不解析标签;
* - `thinking` 非空时(来自 REST `/messages`,owner 可见)在气泡上方显示思维链卡片;
* - `image` 非空时在气泡上方显示图片。
*
* 文本可自由选择:每个气泡/旁白各自包一层 [SelectionContainer],
* 因此选择菜单的「全选」只作用于当前那一块,而不是整个页面。
*/
@Composable
fun MessageBubble(message: HistoryMessageDto) {
@@ -129,13 +133,14 @@ private fun Bubble(
private fun AnnotationLine(parts: List<ContentPart>) {
val text = parts.joinToString(" ") { it.text }.trim()
if (text.isEmpty()) return
Text(
text = text,
style = MiuixTheme.textStyles.footnote1,
fontStyle = FontStyle.Italic,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp),
)
SelectionContainer(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) {
Text(
text = text,
style = MiuixTheme.textStyles.footnote1,
fontStyle = FontStyle.Italic,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
}
}
@Composable
@@ -143,7 +148,6 @@ private fun BubbleBody(text: String, isUser: Boolean) {
Box(
modifier = Modifier
.widthIn(max = 300.dp)
.copyOnLongPress(text)
.background(
color = if (isUser) {
MiuixTheme.colorScheme.primary
@@ -154,11 +158,13 @@ private fun BubbleBody(text: String, isUser: Boolean) {
)
.padding(horizontal = 14.dp, vertical = 10.dp),
) {
Text(
text = text,
style = MiuixTheme.textStyles.main,
color = if (isUser) MiuixTheme.colorScheme.onPrimary else MiuixTheme.colorScheme.onSurfaceContainer,
)
SelectionContainer {
Text(
text = text,
style = MiuixTheme.textStyles.main,
color = if (isUser) MiuixTheme.colorScheme.onPrimary else MiuixTheme.colorScheme.onSurfaceContainer,
)
}
}
}
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -24,8 +25,8 @@ import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.theme.MiuixTheme
/**
* 思维链卡片。默认展开(方便观察她的思考过程),可折叠。
* 流式期间标题显示「思考中…」。
* 思维链卡片。流式期间展开(方便观察),流式结束自动折叠;历史思维链默认折叠。
* 正文包在 [SelectionContainer] 里,长按可自由选择、菜单「全选」只作用于本卡片。
*/
@Composable
fun ThinkingCard(
@@ -34,7 +35,6 @@ fun ThinkingCard(
modifier: Modifier = Modifier,
) {
if (text.isBlank() && !active) return
// 流式期间展开(方便观察),流式结束自动折叠;历史思维链默认折叠。
var expanded by remember { mutableStateOf(active) }
LaunchedEffect(active) {
if (!active) expanded = false
@@ -126,9 +126,11 @@ private fun FoldCardLabel(text: String) {
@Composable
private fun FoldCardBody(text: String) {
Text(
text = text,
style = MiuixTheme.textStyles.footnote1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
SelectionContainer {
Text(
text = text,
style = MiuixTheme.textStyles.footnote1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
)
}
}