verify feature sync selectable text and localization
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user