Compare commits

..

3 commits

Author SHA1 Message Date
Bruno Deanoz
75ca8c0e7b feat: visual refresh — lighter typography, message animations, richer themes
Typography: body text W400→W300 for airier feel, headings scaled up
(H1 26sp, H2 22sp, H3 18sp), W600/W500 weight hierarchy for contrast.

Animations: animateItem() on LazyColumn messages — 350ms fade-in,
spring placement, 200ms fade-out.

Themes: blobs 20% larger + better full-screen distribution, higher
alpha on OLED (0.48 peak), light mode factor 0.65→0.78, reduced
vignette, distinct primaryDeep per theme for richer gradients.
2026-06-23 12:22:38 +02:00
Bruno Deanoz
fbead82ada fix: increase chat content padding to prevent overlap with top/bottom bars 2026-06-23 07:17:12 +02:00
Bruno Deanoz
10154d98bb fix: set gradlew execute permission 2026-06-23 07:15:17 +02:00
10 changed files with 35 additions and 25 deletions

View file

@ -45,7 +45,7 @@ fun MeshBackground(animateBlobs: Boolean = true, content: @Composable BoxScope.(
val dark = isSystemInDarkTheme()
val accent = LocalKaizenAccent.current
val base = MaterialTheme.colorScheme.background
val factor = if (dark) 1f else 0.65f
val factor = if (dark) 1f else 0.78f
val drift = if (animateBlobs) {
val transition = rememberInfiniteTransition(label = "blobs")
@ -70,14 +70,14 @@ fun MeshBackground(animateBlobs: Boolean = true, content: @Composable BoxScope.(
drawRect(brush = ditherBrush, alpha = DITHER_ALPHA)
},
) {
Blob(accent.blob1, 360.dp, (-60f + drift * 30f).dp, (-40f + drift * 24f).dp, 0.42f * factor)
Blob(accent.blob2, 380.dp, (170f - drift * 44f).dp, (150f + drift * 40f).dp, 0.36f * factor)
Blob(accent.blob3, 320.dp, (220f + drift * 36f).dp, (80f - drift * 28f).dp, 0.30f * factor)
Blob(accent.blob4, 300.dp, (30f + drift * 50f).dp, (560f - drift * 36f).dp, 0.34f * factor)
Blob(accent.blob1, 440.dp, (-80f + drift * 40f).dp, (-60f + drift * 30f).dp, 0.48f * factor)
Blob(accent.blob2, 460.dp, (140f - drift * 50f).dp, (200f + drift * 50f).dp, 0.40f * factor)
Blob(accent.blob3, 400.dp, (-30f + drift * 60f).dp, (450f - drift * 40f).dp, 0.36f * factor)
Blob(accent.blob4, 420.dp, (180f + drift * 40f).dp, (620f - drift * 44f).dp, 0.38f * factor)
if (dark) {
Box(
Modifier.fillMaxSize().background(
Brush.radialGradient(listOf(Color.Transparent, base.copy(alpha = 0.40f))),
Brush.radialGradient(listOf(Color.Transparent, base.copy(alpha = 0.35f))),
),
)
}

View file

@ -135,11 +135,11 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M
) {
KaizenOrb(104.dp)
Spacer(Modifier.height(24.dp))
Text("${greeting(now.hour)}, $userName", color = cs.onSurfaceVariant, fontSize = 17.sp)
Text("${greeting(now.hour)}, $userName", color = cs.onSurfaceVariant, fontSize = 17.sp, fontWeight = FontWeight.W300)
Spacer(Modifier.height(6.dp))
Text(stringResource(R.string.hero_headline), color = cs.onBackground, fontSize = 32.sp, fontWeight = FontWeight.Bold)
Text(stringResource(R.string.hero_headline), color = cs.onBackground, fontSize = 32.sp, fontWeight = FontWeight.W600)
Spacer(Modifier.height(8.dp))
Text(dateLine, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 13.sp)
Text(dateLine, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 13.sp, fontWeight = FontWeight.W300)
}
}
@ -520,7 +520,7 @@ fun MessageRow(
if (hasAttachments) AttachmentGrid(message.attachments, Modifier.padding(6.dp))
if (message.content.isNotBlank()) {
Text(
message.content, color = cs.onBackground, fontSize = 16.sp, lineHeight = 26.sp,
message.content, color = cs.onBackground, fontSize = 16.sp, lineHeight = 26.sp, fontWeight = FontWeight.W300,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = if (hasAttachments) 4.dp else 13.dp, bottom = 13.dp),
)
}
@ -784,10 +784,11 @@ fun ChatInput(
stringResource(R.string.chat_input_placeholder),
color = cs.onSurfaceVariant.copy(alpha = 0.5f),
fontSize = 16.sp,
fontWeight = FontWeight.W300,
)
BasicTextField(
value = value, onValueChange = onValueChange,
textStyle = TextStyle(color = cs.onBackground, fontSize = 16.sp, lineHeight = 24.sp),
textStyle = TextStyle(color = cs.onBackground, fontSize = 16.sp, lineHeight = 24.sp, fontWeight = FontWeight.W300),
cursorBrush = SolidColor(accent.primary), maxLines = 6, modifier = Modifier.fillMaxWidth(),
)
}

View file

@ -88,6 +88,9 @@ import android.media.MediaPlayer
import android.media.MediaRecorder
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
@ -858,14 +861,19 @@ fun ChatScreen(
.fillMaxSize()
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier),
contentPadding = PaddingValues(
top = 76.dp, // Leaves breathing room for the floating top menu button
bottom = 220.dp, // Clears the floating ModePills + two-row ChatInput + bottom margins
top = 100.dp,
bottom = 260.dp,
start = 16.dp,
end = 16.dp
),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
items(messages, key = { it.id }) { message ->
Box(Modifier.animateItem(
fadeInSpec = tween(350),
placementSpec = spring(stiffness = Spring.StiffnessMediumLow),
fadeOutSpec = tween(200),
)) {
MessageRow(
message = message,
onDelete = if (conversationId != null && !isStreaming) { wireId ->
@ -906,6 +914,7 @@ fun ChatScreen(
)
}
}
}
if (!isStreaming) {
val canScrollUp by remember { derivedStateOf { listState.canScrollBackward } }
@ -1014,7 +1023,7 @@ fun ChatScreen(
modelDisplayName(displayModel, models),
color = MaterialTheme.colorScheme.onBackground,
fontSize = 13.sp,
fontWeight = FontWeight.Medium,
fontWeight = FontWeight.W300,
maxLines = 1,
modifier = Modifier.widthIn(max = 180.dp),
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,

View file

@ -461,9 +461,9 @@ fun MarkdownText(text: String, streaming: Boolean = false, modifier: Modifier =
if (idx > 0) Spacer(Modifier.height(18.dp))
val rendered = inlineMarkdown(block.text + if (isLast) cursor else "", cs.onBackground, inlineCodeColor, inlineCodeBg, linkColor)
ClickableMarkdownText(rendered, context, cs.onBackground,
fontSize = when (block.level) { 1 -> 22.sp; 2 -> 19.sp; else -> 17.sp },
fontWeight = FontWeight.Bold,
lineHeight = when (block.level) { 1 -> 30.sp; 2 -> 27.sp; else -> 24.sp },
fontSize = when (block.level) { 1 -> 26.sp; 2 -> 22.sp; else -> 18.sp },
fontWeight = when (block.level) { 1 -> FontWeight.W600; 2 -> FontWeight.W600; else -> FontWeight.W500 },
lineHeight = when (block.level) { 1 -> 34.sp; 2 -> 30.sp; else -> 26.sp },
)
Spacer(Modifier.height(8.dp))
}
@ -592,7 +592,7 @@ fun MarkdownText(text: String, streaming: Boolean = false, modifier: Modifier =
val appendCursor = isLast && itemIdx == block.items.lastIndex
val rendered = inlineMarkdown(item.text + if (appendCursor) cursor else "", cs.onBackground, inlineCodeColor, inlineCodeBg, linkColor)
ClickableMarkdownText(rendered, context, cs.onBackground,
fontSize = 16.sp, lineHeight = 26.sp,
fontSize = 16.sp, lineHeight = 26.sp, fontWeight = FontWeight.W300,
modifier = Modifier.weight(1f),
)
}
@ -641,7 +641,7 @@ fun MarkdownText(text: String, streaming: Boolean = false, modifier: Modifier =
if (idx > 0) Spacer(Modifier.height(12.dp))
val rendered = inlineMarkdown(block.text + if (isLast) cursor else "", cs.onBackground, inlineCodeColor, inlineCodeBg, linkColor)
ClickableMarkdownText(rendered, context, cs.onBackground,
fontSize = 16.sp, lineHeight = 26.sp,
fontSize = 16.sp, lineHeight = 26.sp, fontWeight = FontWeight.W300,
)
}

View file

@ -88,14 +88,14 @@ val Typography = Typography(
),
bodyLarge = TextStyle(
fontFamily = InterVariable,
fontWeight = FontWeight.W400,
fontWeight = FontWeight.W300,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.08.sp,
),
bodyMedium = TextStyle(
fontFamily = InterVariable,
fontWeight = FontWeight.W400,
fontWeight = FontWeight.W300,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.11.sp,

View file

@ -12,7 +12,7 @@ val AmberTheme = KaizenThemeSpec(
label = "Amber",
light = KaizenAccentScheme(
primary = p3(0.811f, 0.608f, 0.126f), // oklch(0.72 0.14 83)
primaryDeep = p3(0.811f, 0.608f, 0.126f),
primaryDeep = p3(0.680f, 0.480f, 0.050f), // oklch(0.62 0.15 83) richer gold
onPrimary = p3(0.015f, 0.013f, 0.009f), // oklch(0.10 0.005 83)
ring = p3(0.811f, 0.608f, 0.126f),
blob1 = p3(0.992f, 0.767f, 0.282f), // oklch(0.85 0.15 83) gold

View file

@ -14,7 +14,7 @@ val AuroraTheme = KaizenThemeSpec(
label = "Aurora",
light = KaizenAccentScheme(
primary = p3(0.724f, 0.328f, 0.812f), // oklch(0.62 0.20 320) purple
primaryDeep = p3(0.724f, 0.328f, 0.812f),
primaryDeep = p3(0.580f, 0.200f, 0.700f), // oklch(0.50 0.22 320) deeper purple
onPrimary = p3(1.000f, 0.979f, 1.000f), // oklch(0.99 0.01 320)
ring = p3(0.724f, 0.328f, 0.812f),
blob1 = p3(0.855f, 0.540f, 0.926f), // oklch(0.75 0.16 320) purple

View file

@ -12,7 +12,7 @@ val BlauTheme = KaizenThemeSpec(
label = "Blau",
light = KaizenAccentScheme(
primary = p3(0.168f, 0.513f, 0.962f), // oklch(0.62 0.19 257)
primaryDeep = p3(0.168f, 0.513f, 0.962f),
primaryDeep = p3(0.090f, 0.380f, 0.820f), // oklch(0.52 0.20 257) deeper blue
onPrimary = p3(0.971f, 0.989f, 1.000f), // oklch(0.99 0.01 257)
ring = p3(0.168f, 0.513f, 0.962f),
blob1 = p3(0.464f, 0.725f, 1.000f), // oklch(0.78 0.15 257) sky

View file

@ -12,7 +12,7 @@ val MonoTheme = KaizenThemeSpec(
label = "Mono",
light = KaizenAccentScheme(
primary = p3(0.104f, 0.104f, 0.104f), // oklch(0.22 0 255) near-black
primaryDeep = p3(0.104f, 0.104f, 0.104f),
primaryDeep = p3(0.050f, 0.050f, 0.050f), // oklch(0.14 0 255) deeper black
onPrimary = p3(0.987f, 0.987f, 0.987f), // oklch(0.99 0 255) near-white
ring = p3(0.104f, 0.104f, 0.104f),
blob1 = p3(0.806f, 0.806f, 0.806f), // oklch(0.85 0 255) light gray

0
gradlew vendored Normal file → Executable file
View file