From 486bcc65879f63c3baaa49e15076887861c43a06 Mon Sep 17 00:00:00 2001 From: marviny <369795172@qq.com> Date: Thu, 11 Jun 2026 10:50:37 +0800 Subject: [PATCH] feat(chat): add copy message to clipboard for assistant messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tapping ⋮ on any assistant message now shows a "Copy message" option that copies all text parts of the message to the system clipboard and shows a brief "Copied" toast confirmation. The menu item appears before "Fork from here" so the most commonly needed action is at the top of the context menu. Co-authored-by: Cursor --- .../ui/chat/ChatMessageContent.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/src/main/java/com/yage/opencode_client/ui/chat/ChatMessageContent.kt b/app/src/main/java/com/yage/opencode_client/ui/chat/ChatMessageContent.kt index 1a0cb8f..be3f224 100644 --- a/app/src/main/java/com/yage/opencode_client/ui/chat/ChatMessageContent.kt +++ b/app/src/main/java/com/yage/opencode_client/ui/chat/ChatMessageContent.kt @@ -31,6 +31,7 @@ import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material.icons.filled.Psychology import androidx.compose.material.icons.filled.RadioButtonUnchecked import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator @@ -71,6 +72,11 @@ import com.yage.opencode_client.ui.theme.markdownTypographyCompact import com.yage.opencode_client.ui.util.DataUriImageTransformer import com.yage.opencode_client.ui.util.HttpImageHolder import com.yage.opencode_client.ui.util.MarkdownImageResolver +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.widget.Toast +import androidx.compose.ui.platform.LocalContext import kotlinx.coroutines.flow.collect @Composable @@ -194,6 +200,7 @@ private fun MessageRow( onForkFromMessage: (String) -> Unit ) { val isUser = message.info.isUser + val context = LocalContext.current Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 4.dp)) { // No "OpenCode" speaker title — the user's blue left bar vs the @@ -294,6 +301,26 @@ private fun MessageRow( expanded = showMenu, onDismissRequest = { showMenu = false } ) { + DropdownMenuItem( + text = { Text("Copy message") }, + leadingIcon = { + Icon( + imageVector = Icons.Default.ContentCopy, + contentDescription = null + ) + }, + onClick = { + showMenu = false + val textToCopy = message.parts + .filter { it.isText } + .mapNotNull { it.text } + .joinToString("\n") + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("message", textToCopy) + clipboard.setPrimaryClip(clip) + Toast.makeText(context, "Copied", Toast.LENGTH_SHORT).show() + } + ) DropdownMenuItem( text = { Text("Fork from here") }, leadingIcon = {