fix(#207): dynamic contrast adjustments for chat bubbles legibility

This commit is contained in:
Hy4ri committed 2026-06-21 12:43:15 +03:00
1 parent 7ce19cd52c
commit 315e1097b2
1 file changed
+46 -13
@@ -50,6 +50,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
@@ -167,6 +168,23 @@ private fun UserBubble(
MaterialTheme.colorScheme.secondary, MaterialTheme.colorScheme.secondary,
), ),
) )
val primary = MaterialTheme.colorScheme.primary
val secondary = MaterialTheme.colorScheme.secondary
val avgLuminance = (primary.luminance() + secondary.luminance()) / 2f
val userBubbleTextColor =
if (avgLuminance > 0.5f) {
if (MaterialTheme.colorScheme.onPrimary.luminance() < 0.5f) {
MaterialTheme.colorScheme.onPrimary
} else {
Color(0xFF1A1A24)
}
} else {
if (MaterialTheme.colorScheme.onPrimary.luminance() > 0.5f) {
MaterialTheme.colorScheme.onPrimary
} else {
Color.White
}
}
Box { Box {
Surface( Surface(
modifier = modifier =
@@ -194,19 +212,21 @@ private fun UserBubble(
SelectionContainer { SelectionContainer {
Text( Text(
text = highlightedText, text = highlightedText,
color = Color.White, color = userBubbleTextColor,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
} }
Text( if (!message.isStreaming) {
text = formatTimestamp(message.timestamp), Text(
color = Color.White.copy(alpha = 0.7f), text = formatTimestamp(message.timestamp),
style = MaterialTheme.typography.labelSmall, color = userBubbleTextColor.copy(alpha = 0.6f),
modifier = style = MaterialTheme.typography.labelSmall,
Modifier modifier =
.align(Alignment.End) Modifier
.padding(top = 4.dp), .align(Alignment.End)
) .padding(top = 4.dp),
)
}
} }
} }
@@ -255,7 +275,20 @@ private fun AssistantBubble(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val bubbleColor = if (isDarkTheme) AssistantBubble else AssistantBubbleLight val bubbleColor = if (isDarkTheme) AssistantBubble else AssistantBubbleLight
val textColor = MaterialTheme.colorScheme.onSurface val textColor =
if (bubbleColor.luminance() > 0.5f) {
if (MaterialTheme.colorScheme.onSurface.luminance() < 0.5f) {
MaterialTheme.colorScheme.onSurface
} else {
Color(0xFF1A1A24)
}
} else {
if (MaterialTheme.colorScheme.onSurface.luminance() > 0.5f) {
MaterialTheme.colorScheme.onSurface
} else {
Color(0xFFE8E6EE)
}
}
val clipboardManager = LocalClipboardManager.current val clipboardManager = LocalClipboardManager.current
var showCopyButton by remember { mutableStateOf(false) } var showCopyButton by remember { mutableStateOf(false) }
@@ -786,7 +819,7 @@ private fun RichText(
// Search match highlight // Search match highlight
searchQuery.isNotEmpty() && searchQuery.isNotEmpty() &&
src.regionMatches(i, searchQuery, 0, searchQuery.length, ignoreCase = true) -> { src.regionMatches(i, searchQuery, 0, searchQuery.length, ignoreCase = true) -> {
withStyle(SpanStyle(background = searchHighlightColor)) { withStyle(SpanStyle(background = searchHighlightColor, color = Color(0xFF1A1A24))) {
append(src.substring(i, i + searchQuery.length)) append(src.substring(i, i + searchQuery.length))
} }
i += searchQuery.length i += searchQuery.length
@@ -843,7 +876,7 @@ private fun buildHighlightedString(
append(text.substring(i, matchEnd)) append(text.substring(i, matchEnd))
} }
// Append the match highlighted // Append the match highlighted
withStyle(SpanStyle(background = highlightColor)) { withStyle(SpanStyle(background = highlightColor, color = Color(0xFF1A1A24))) {
append(text.substring(matchEnd, matchEnd + query.length)) append(text.substring(matchEnd, matchEnd + query.length))
} }
i = matchEnd + query.length i = matchEnd + query.length