feat(chat): 改为文本选择模式,全选仅作用于当前气泡
用 SelectionContainer 包住气泡/旁白/思维链/工具卡的正文,长按可拖选;移除长按整段复制的实现与其工具文件。
This commit is contained in:
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.padding
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -34,6 +35,9 @@ private const val CURSOR = "▍"
|
|||||||
* - 其它 mode 原样整条渲染,不解析标签;
|
* - 其它 mode 原样整条渲染,不解析标签;
|
||||||
* - `thinking` 非空时(来自 REST `/messages`,owner 可见)在气泡上方显示思维链卡片;
|
* - `thinking` 非空时(来自 REST `/messages`,owner 可见)在气泡上方显示思维链卡片;
|
||||||
* - `image` 非空时在气泡上方显示图片。
|
* - `image` 非空时在气泡上方显示图片。
|
||||||
|
*
|
||||||
|
* 文本可自由选择:每个气泡/旁白各自包一层 [SelectionContainer],
|
||||||
|
* 因此选择菜单的「全选」只作用于当前那一块,而不是整个页面。
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageBubble(message: HistoryMessageDto) {
|
fun MessageBubble(message: HistoryMessageDto) {
|
||||||
@@ -129,13 +133,14 @@ private fun Bubble(
|
|||||||
private fun AnnotationLine(parts: List<ContentPart>) {
|
private fun AnnotationLine(parts: List<ContentPart>) {
|
||||||
val text = parts.joinToString(" ") { it.text }.trim()
|
val text = parts.joinToString(" ") { it.text }.trim()
|
||||||
if (text.isEmpty()) return
|
if (text.isEmpty()) return
|
||||||
Text(
|
SelectionContainer(modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)) {
|
||||||
text = text,
|
Text(
|
||||||
style = MiuixTheme.textStyles.footnote1,
|
text = text,
|
||||||
fontStyle = FontStyle.Italic,
|
style = MiuixTheme.textStyles.footnote1,
|
||||||
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
fontStyle = FontStyle.Italic,
|
||||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp),
|
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -143,7 +148,6 @@ private fun BubbleBody(text: String, isUser: Boolean) {
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.widthIn(max = 300.dp)
|
.widthIn(max = 300.dp)
|
||||||
.copyOnLongPress(text)
|
|
||||||
.background(
|
.background(
|
||||||
color = if (isUser) {
|
color = if (isUser) {
|
||||||
MiuixTheme.colorScheme.primary
|
MiuixTheme.colorScheme.primary
|
||||||
@@ -154,11 +158,13 @@ private fun BubbleBody(text: String, isUser: Boolean) {
|
|||||||
)
|
)
|
||||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
SelectionContainer {
|
||||||
text = text,
|
Text(
|
||||||
style = MiuixTheme.textStyles.main,
|
text = text,
|
||||||
color = if (isUser) MiuixTheme.colorScheme.onPrimary else MiuixTheme.colorScheme.onSurfaceContainer,
|
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.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -24,8 +25,8 @@ import top.yukonga.miuix.kmp.basic.Text
|
|||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 思维链卡片。默认展开(方便观察她的思考过程),可折叠。
|
* 思维链卡片。流式期间展开(方便观察),流式结束自动折叠;历史思维链默认折叠。
|
||||||
* 流式期间标题显示「思考中…」。
|
* 正文包在 [SelectionContainer] 里,长按可自由选择、菜单「全选」只作用于本卡片。
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ThinkingCard(
|
fun ThinkingCard(
|
||||||
@@ -34,7 +35,6 @@ fun ThinkingCard(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (text.isBlank() && !active) return
|
if (text.isBlank() && !active) return
|
||||||
// 流式期间展开(方便观察),流式结束自动折叠;历史思维链默认折叠。
|
|
||||||
var expanded by remember { mutableStateOf(active) }
|
var expanded by remember { mutableStateOf(active) }
|
||||||
LaunchedEffect(active) {
|
LaunchedEffect(active) {
|
||||||
if (!active) expanded = false
|
if (!active) expanded = false
|
||||||
@@ -126,9 +126,11 @@ private fun FoldCardLabel(text: String) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FoldCardBody(text: String) {
|
private fun FoldCardBody(text: String) {
|
||||||
Text(
|
SelectionContainer {
|
||||||
text = text,
|
Text(
|
||||||
style = MiuixTheme.textStyles.footnote1,
|
text = text,
|
||||||
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
style = MiuixTheme.textStyles.footnote1,
|
||||||
)
|
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user