feat: redesign ReasoningBlock + SourceCards to match design system

ReasoningBlock: accent-tinted background + gradient border (matches pill
language), KaizenShapes.md (16dp radius), larger padding, better line
height (20sp), softer icon tint. Replaces flat gray box with solid border.

SourceCards: gradient border instead of solid, KaizenShapes.md, shows
domain instead of full URL in the link line, slightly larger padding.

Both blocks now use the same visual language as the rest of the app —
gradient borders, accent tinting, squircle shapes.
This commit is contained in:
Bruno Deanoz 2026-06-23 01:35:45 +02:00
parent b2df7e9330
commit 421b29ef5a

View file

@ -28,6 +28,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@ -49,44 +50,51 @@ fun ReasoningBlock(reasoning: String, streaming: Boolean = false) {
val accent = LocalKaizenAccent.current
var expanded by remember { mutableStateOf(streaming) }
val bg = if (isDark) Color.White.copy(alpha = 0.05f) else Color.Black.copy(alpha = 0.03f)
val borderColor = if (isDark) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.06f)
val bg = accent.primary.copy(alpha = if (isDark) 0.06f else 0.04f)
val borderBrush = remember(isDark, accent) {
Brush.verticalGradient(
listOf(
accent.primary.copy(alpha = if (isDark) 0.20f else 0.15f),
accent.primary.copy(alpha = if (isDark) 0.05f else 0.03f),
)
)
}
Column(
Modifier
.fillMaxWidth()
.clip(KaizenShapes.sm)
.clip(KaizenShapes.md)
.background(bg)
.border(1.dp, borderColor, KaizenShapes.sm)
.border(0.8.dp, borderBrush, KaizenShapes.md)
) {
Row(
Modifier
.fillMaxWidth()
.clickable { expanded = !expanded }
.padding(horizontal = 12.dp, vertical = 8.dp),
.padding(horizontal = 14.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(KaizenIcons.Brain, null, tint = accent.primary, modifier = Modifier.size(16.dp))
Spacer(Modifier.width(8.dp))
Icon(KaizenIcons.Brain, null, tint = accent.primary.copy(alpha = 0.75f), modifier = Modifier.size(17.dp))
Spacer(Modifier.width(9.dp))
Text(
stringResource(R.string.stream_reasoning),
color = cs.onSurfaceVariant,
color = cs.onSurfaceVariant.copy(alpha = 0.75f),
fontSize = 13.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f),
)
Icon(
if (expanded) KaizenIcons.ChevronUp else KaizenIcons.ChevronDown,
null, tint = cs.onSurfaceVariant, modifier = Modifier.size(16.dp),
null, tint = cs.onSurfaceVariant.copy(alpha = 0.40f), modifier = Modifier.size(15.dp),
)
}
AnimatedVisibility(expanded, enter = expandVertically(), exit = shrinkVertically()) {
Text(
reasoning,
color = cs.onSurfaceVariant.copy(alpha = 0.8f),
color = cs.onSurfaceVariant.copy(alpha = 0.70f),
fontSize = 13.sp,
lineHeight = 18.sp,
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 10.dp),
lineHeight = 20.sp,
modifier = Modifier.padding(start = 14.dp, end = 14.dp, bottom = 12.dp),
)
}
}
@ -175,18 +183,18 @@ fun SourcesBlock(sources: List<SearchSource>, query: String = "") {
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(KaizenIcons.Globe, null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(13.dp))
Icon(KaizenIcons.Globe, null, tint = cs.onSurfaceVariant.copy(alpha = 0.55f), modifier = Modifier.size(14.dp))
Spacer(Modifier.width(6.dp))
Text(
stringResource(R.string.stream_sources) + " (${sources.size})",
color = cs.onSurfaceVariant.copy(alpha = 0.6f),
color = cs.onSurfaceVariant.copy(alpha = 0.55f),
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
)
Spacer(Modifier.width(4.dp))
Icon(
if (expanded) KaizenIcons.ChevronUp else KaizenIcons.ChevronDown,
null, tint = cs.onSurfaceVariant.copy(alpha = 0.4f), modifier = Modifier.size(12.dp),
null, tint = cs.onSurfaceVariant.copy(alpha = 0.35f), modifier = Modifier.size(13.dp),
)
}
AnimatedVisibility(expanded, enter = expandVertically(), exit = shrinkVertically()) {
@ -208,7 +216,14 @@ fun SourcesBlock(sources: List<SearchSource>, query: String = "") {
private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen.app.ui.theme.themes.KaizenAccentScheme, onClick: () -> Unit) {
val cs = MaterialTheme.colorScheme
val bg = if (isDark) Color.White.copy(alpha = 0.05f) else Color.Black.copy(alpha = 0.03f)
val borderColor = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.05f)
val borderBrush = remember(isDark) {
Brush.verticalGradient(
listOf(
if (isDark) Color.White.copy(alpha = 0.10f) else Color.Black.copy(alpha = 0.06f),
if (isDark) Color.White.copy(alpha = 0.03f) else Color.Black.copy(alpha = 0.02f),
)
)
}
val domain = remember(source.url) {
try {
@ -219,11 +234,11 @@ private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen
Column(
Modifier
.fillMaxWidth()
.clip(KaizenShapes.sm)
.clip(KaizenShapes.md)
.background(bg)
.border(1.dp, borderColor, KaizenShapes.sm)
.border(0.8.dp, borderBrush, KaizenShapes.md)
.clickable(onClick = onClick)
.padding(horizontal = 12.dp, vertical = 10.dp),
.padding(horizontal = 14.dp, vertical = 10.dp),
) {
Text(
source.title.ifEmpty { domain },
@ -235,8 +250,8 @@ private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen
)
Spacer(Modifier.height(2.dp))
Text(
source.url,
color = accent.primary.copy(alpha = 0.7f),
domain,
color = accent.primary.copy(alpha = 0.65f),
fontSize = 11.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@ -245,7 +260,7 @@ private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen
Spacer(Modifier.height(3.dp))
Text(
source.snippet,
color = cs.onSurfaceVariant.copy(alpha = 0.5f),
color = cs.onSurfaceVariant.copy(alpha = 0.50f),
fontSize = 11.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis,