verify feature sync selectable text and localization

This commit is contained in:
Xisheng-Zhao
2026-05-17 05:27:21 +08:00
parent df25f2346e
commit 0e2035a480
@@ -44,6 +44,7 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.automirrored.outlined.Send import androidx.compose.material.icons.automirrored.outlined.Send
@@ -5068,17 +5069,19 @@ private fun IssueMaterialListRow(
modifier = Modifier.widthIn(min = 44.dp, max = 54.dp), modifier = Modifier.widthIn(min = 44.dp, max = 54.dp),
) )
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(1.dp)) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(1.dp)) {
Text( SelectionContainer {
text = issue.title, Text(
style = MaterialTheme.typography.bodyMedium.copy( text = issue.title,
fontWeight = FontWeight.Medium, style = MaterialTheme.typography.bodyMedium.copy(
fontSize = 15.sp, fontWeight = FontWeight.Medium,
lineHeight = 19.sp, fontSize = 15.sp,
), lineHeight = 19.sp,
color = MulticaColors.Text, ),
maxLines = 1, color = MulticaColors.Text,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
}
val rowMetadata = listOf( val rowMetadata = listOf(
Models.priorityLabel(issue.priority, zh), Models.priorityLabel(issue.priority, zh),
metadata, metadata,
@@ -7607,17 +7610,19 @@ private fun IssueDetailHeroCard(
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
Text( SelectionContainer {
text = issue.title, Text(
style = MaterialTheme.typography.titleLarge.copy( text = issue.title,
fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleLarge.copy(
fontSize = 22.sp, fontWeight = FontWeight.Bold,
lineHeight = 27.sp, fontSize = 22.sp,
), lineHeight = 27.sp,
color = MulticaColors.Text, ),
maxLines = 3, color = MulticaColors.Text,
overflow = TextOverflow.Ellipsis, maxLines = 3,
) overflow = TextOverflow.Ellipsis,
)
}
} }
MulticaIconPillButton( MulticaIconPillButton(
icon = Icons.Outlined.ContentCopy, icon = Icons.Outlined.ContentCopy,
@@ -8104,6 +8109,7 @@ private fun IssueTimelineWebDenseRow(
title: String, title: String,
subtitle: String, subtitle: String,
kind: String, kind: String,
zh: Boolean,
highlighted: Boolean, highlighted: Boolean,
showDivider: Boolean = false, showDivider: Boolean = false,
contentDescription: String? = null, contentDescription: String? = null,
@@ -8153,7 +8159,7 @@ private fun IssueTimelineWebDenseRow(
) )
if (highlighted) { if (highlighted) {
Text( Text(
text = "PINNED", text = if (zh) "置顶" else "Pinned",
style = MaterialTheme.typography.labelSmall.copy(fontSize = 11.sp, lineHeight = 13.sp), style = MaterialTheme.typography.labelSmall.copy(fontSize = 11.sp, lineHeight = 13.sp),
color = MulticaColors.Accent, color = MulticaColors.Accent,
maxLines = 1, maxLines = 1,
@@ -10139,7 +10145,7 @@ private fun PilotIssueDetail(
items(data.runs) { run -> items(data.runs) { run ->
IssueDetailWebActionRow( IssueDetailWebActionRow(
eyebrow = shortDate(run.startedAt), eyebrow = shortDate(run.startedAt),
title = "Agent run · ${run.status}", title = agentRunTitle(run.status, zh),
subtitle = clean(run.error).ifBlank { null }, subtitle = clean(run.error).ifBlank { null },
icon = Icons.Outlined.Bolt, icon = Icons.Outlined.Bolt,
tone = if (run.status == "failed") MulticaButtonTone.Destructive else MulticaButtonTone.Ghost, tone = if (run.status == "failed") MulticaButtonTone.Destructive else MulticaButtonTone.Ghost,
@@ -10294,6 +10300,7 @@ private fun PilotIssueDetail(
title = if (zh) "时间线暂不可用" else "Timeline unavailable", title = if (zh) "时间线暂不可用" else "Timeline unavailable",
subtitle = if (zh) "稍后重试或查看网页版" else "Try again later or check the web app", subtitle = if (zh) "稍后重试或查看网页版" else "Try again later or check the web app",
kind = if (zh) "状态" else "Status", kind = if (zh) "状态" else "Status",
zh = zh,
highlighted = false, highlighted = false,
) )
} else if (entries.isEmpty()) { } else if (entries.isEmpty()) {
@@ -10301,6 +10308,7 @@ private fun PilotIssueDetail(
title = if (zh) "暂无时间线记录" else "No timeline events", title = if (zh) "暂无时间线记录" else "No timeline events",
subtitle = if (zh) "评论和活动会显示在这里" else "Comments and activity will appear here", subtitle = if (zh) "评论和活动会显示在这里" else "Comments and activity will appear here",
kind = if (zh) "" else "Empty", kind = if (zh) "" else "Empty",
zh = zh,
highlighted = false, highlighted = false,
) )
} else { } else {
@@ -10328,6 +10336,7 @@ private fun PilotIssueDetail(
} else { } else {
if (zh) "活动" else "Activity" if (zh) "活动" else "Activity"
}, },
zh = zh,
highlighted = highlighted, highlighted = highlighted,
showDivider = index < visibleTimelineEntries.lastIndex || timelineHasMoreBefore, showDivider = index < visibleTimelineEntries.lastIndex || timelineHasMoreBefore,
) )
@@ -10341,6 +10350,7 @@ private fun PilotIssueDetail(
}, },
subtitle = if (zh) "继续读取更早的活动记录" else "Fetch older activity entries", subtitle = if (zh) "继续读取更早的活动记录" else "Fetch older activity entries",
kind = if (zh) "更多" else "More", kind = if (zh) "更多" else "More",
zh = zh,
highlighted = false, highlighted = false,
contentDescription = "Issue Timeline Load Older", contentDescription = "Issue Timeline Load Older",
onClick = { loadOlderTimeline() }, onClick = { loadOlderTimeline() },
@@ -10995,13 +11005,15 @@ private fun PilotAgentTranscript(
} }
} }
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text( SelectionContainer {
text = "Agent run · ${run.status}", Text(
style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold), text = agentRunTitle(run.status, zh),
color = MulticaColors.Text, style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold),
maxLines = 1, color = MulticaColors.Text,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
}
Text( Text(
text = listOf(shortDate(run.startedAt), clean(run.error)) text = listOf(shortDate(run.startedAt), clean(run.error))
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
@@ -11234,6 +11246,11 @@ private fun transcriptTypeIcon(type: String): ImageVector {
} }
} }
private fun agentRunTitle(status: String, zh: Boolean): String {
val value = clean(status).ifBlank { if (zh) "未知状态" else "unknown" }
return if (zh) "Agent 运行 · $value" else "Agent run · $value"
}
@Composable @Composable
private fun PilotAgentTranscriptPreview() { private fun PilotAgentTranscriptPreview() {
val WEB_SSOT_AGENT_TRANSCRIPT_DIALOG = "/Users/park0er/coding/multica-web-source/packages/views/common/task-transcript/agent-transcript-dialog.tsx" val WEB_SSOT_AGENT_TRANSCRIPT_DIALOG = "/Users/park0er/coding/multica-web-source/packages/views/common/task-transcript/agent-transcript-dialog.tsx"
@@ -11332,23 +11349,29 @@ private fun MarkdownBlock(markdown: String) {
val textColor = MulticaColors.Text.toArgb() val textColor = MulticaColors.Text.toArgb()
val mutedColor = MulticaColors.Muted.toArgb() val mutedColor = MulticaColors.Muted.toArgb()
val borderColor = MulticaColors.Border.toArgb() val borderColor = MulticaColors.Border.toArgb()
AndroidView( SelectionContainer(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
factory = { context -> .fillMaxWidth()
LinearLayout(context).apply { orientation = LinearLayout.VERTICAL } .semantics { contentDescription = "Selectable Markdown Block" },
}, ) {
update = { view -> AndroidView(
view.removeAllViews() modifier = Modifier.fillMaxWidth(),
MarkdownRenderer.render( factory = { context ->
view.context, LinearLayout(context).apply { orientation = LinearLayout.VERTICAL }
view, },
markdown, update = { view ->
textColor, view.removeAllViews()
mutedColor, MarkdownRenderer.render(
borderColor, view.context,
) view,
}, markdown,
) textColor,
mutedColor,
borderColor,
)
},
)
}
} }
@Composable @Composable
@@ -11591,12 +11614,14 @@ private fun PilotProjectDetail(
}, },
contentDescription = "Project Detail Web Description", contentDescription = "Project Detail Web Description",
) { ) {
Text( SelectionContainer {
text = preview(data.project.description) Text(
?: if (zh) "暂无项目描述" else "No project description", text = preview(data.project.description)
style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 17.sp), ?: if (zh) "暂无项目描述" else "No project description",
color = if (data.project.description.isBlank()) MulticaColors.Muted else MulticaColors.Text, style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 17.sp),
) color = if (data.project.description.isBlank()) MulticaColors.Muted else MulticaColors.Text,
)
}
} }
} }
if (!pinResult.isNullOrBlank()) { if (!pinResult.isNullOrBlank()) {
@@ -11823,18 +11848,19 @@ private fun ProjectDetailWebHero(
horizontalArrangement = Arrangement.spacedBy(7.dp), horizontalArrangement = Arrangement.spacedBy(7.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( SelectionContainer(modifier = Modifier.weight(1f)) {
project.name, Text(
modifier = Modifier.weight(1f), project.name,
style = MaterialTheme.typography.titleMedium.copy( style = MaterialTheme.typography.titleMedium.copy(
fontSize = 20.sp, fontSize = 20.sp,
lineHeight = 25.sp, lineHeight = 25.sp,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
), ),
color = MulticaColors.Text, color = MulticaColors.Text,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
}
if (isPinned) { if (isPinned) {
Text( Text(
if (zh) "已置顶" else "Pinned", if (zh) "已置顶" else "Pinned",
@@ -11844,13 +11870,15 @@ private fun ProjectDetailWebHero(
) )
} }
} }
Text( SelectionContainer {
preview(project.description) ?: if (zh) "暂无项目描述" else "No project description", Text(
style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 16.sp), preview(project.description) ?: if (zh) "暂无项目描述" else "No project description",
color = MulticaColors.TextTertiary, style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 16.sp),
maxLines = 3, color = MulticaColors.TextTertiary,
overflow = TextOverflow.Ellipsis, maxLines = 3,
) overflow = TextOverflow.Ellipsis,
)
}
} }
} }
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) {
@@ -22988,20 +23016,24 @@ private fun SkillWebDenseRow(
) { ) {
Icon(Icons.Outlined.Folder, contentDescription = null, tint = MulticaColors.Muted, modifier = Modifier.size(20.dp)) Icon(Icons.Outlined.Folder, contentDescription = null, tint = MulticaColors.Muted, modifier = Modifier.size(20.dp))
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(5.dp)) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(5.dp)) {
Text( SelectionContainer {
skill.name, Text(
style = MaterialTheme.typography.titleSmall.copy(fontSize = 15.sp, lineHeight = 19.sp, fontWeight = FontWeight.SemiBold), skill.name,
color = MulticaColors.Text, style = MaterialTheme.typography.titleSmall.copy(fontSize = 15.sp, lineHeight = 19.sp, fontWeight = FontWeight.SemiBold),
maxLines = 2, color = MulticaColors.Text,
overflow = TextOverflow.Ellipsis, maxLines = 2,
) overflow = TextOverflow.Ellipsis,
Text( )
clean(skill.description).ifBlank { if (zh) "暂无描述" else "No description" }, }
style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 15.sp), SelectionContainer {
color = MulticaColors.Muted, Text(
maxLines = 2, clean(skill.description).ifBlank { if (zh) "暂无描述" else "No description" },
overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp, lineHeight = 15.sp),
) color = MulticaColors.Muted,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
LazyRow( LazyRow(
horizontalArrangement = Arrangement.spacedBy(6.dp), horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -25982,18 +26014,19 @@ private fun AgentRosterWebDenseRow(
} }
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(5.dp)) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(5.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(7.dp)) { Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(7.dp)) {
Text( SelectionContainer(modifier = Modifier.weight(1f)) {
text = agent.name, Text(
modifier = Modifier.weight(1f), text = agent.name,
style = MaterialTheme.typography.bodyMedium.copy( style = MaterialTheme.typography.bodyMedium.copy(
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
fontSize = 15.sp, fontSize = 15.sp,
lineHeight = 19.sp, lineHeight = 19.sp,
), ),
color = MulticaColors.Text, color = MulticaColors.Text,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
}
Text( Text(
text = statusLabel, text = statusLabel,
style = MaterialTheme.typography.labelSmall.copy(fontSize = 10.sp, lineHeight = 12.sp, fontWeight = FontWeight.SemiBold), style = MaterialTheme.typography.labelSmall.copy(fontSize = 10.sp, lineHeight = 12.sp, fontWeight = FontWeight.SemiBold),
@@ -26002,13 +26035,15 @@ private fun AgentRosterWebDenseRow(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
} }
Text( SelectionContainer {
text = clean(agent.description).ifBlank { compactTaskLabel }, Text(
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, lineHeight = 15.sp), text = clean(agent.description).ifBlank { compactTaskLabel },
color = MulticaColors.Muted.copy(alpha = 0.72f), style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, lineHeight = 15.sp),
maxLines = 1, color = MulticaColors.Muted.copy(alpha = 0.72f),
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
}
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()