Compare commits
No commits in common. "70aa5c56dadc1382b3be144b6bd0a0ca77eaf556" and "f7031960877ac2dd21011a5359c250b4a3ba3485" have entirely different histories.
70aa5c56da
...
f703196087
5 changed files with 23 additions and 47 deletions
|
|
@ -41,25 +41,20 @@ import dev.kaizen.app.ui.theme.rememberDitherBrush
|
||||||
|
|
||||||
/** Mesh/blob background: large, heavily blurred color circles that drift slowly. */
|
/** Mesh/blob background: large, heavily blurred color circles that drift slowly. */
|
||||||
@Composable
|
@Composable
|
||||||
fun MeshBackground(animateBlobs: Boolean = true, content: @Composable BoxScope.() -> Unit) {
|
fun MeshBackground(content: @Composable BoxScope.() -> Unit) {
|
||||||
val dark = isSystemInDarkTheme()
|
val dark = isSystemInDarkTheme()
|
||||||
val accent = LocalKaizenAccent.current
|
val accent = LocalKaizenAccent.current
|
||||||
val base = MaterialTheme.colorScheme.background
|
val base = MaterialTheme.colorScheme.background
|
||||||
|
// Blobs are subtler in light mode.
|
||||||
val factor = if (dark) 1f else 0.65f
|
val factor = if (dark) 1f else 0.65f
|
||||||
|
val transition = rememberInfiniteTransition(label = "blobs")
|
||||||
val drift = if (animateBlobs) {
|
val drift by transition.animateFloat(
|
||||||
val transition = rememberInfiniteTransition(label = "blobs")
|
initialValue = 0f,
|
||||||
val d by transition.animateFloat(
|
targetValue = 1f,
|
||||||
initialValue = 0f,
|
animationSpec = infiniteRepeatable(tween(22000, easing = LinearEasing), RepeatMode.Reverse),
|
||||||
targetValue = 1f,
|
label = "drift",
|
||||||
animationSpec = infiniteRepeatable(tween(22000, easing = LinearEasing), RepeatMode.Reverse),
|
)
|
||||||
label = "drift",
|
// Dither overlay (drawn last, over everything) — kills 8-bit gradient banding.
|
||||||
)
|
|
||||||
d
|
|
||||||
} else {
|
|
||||||
0.5f
|
|
||||||
}
|
|
||||||
|
|
||||||
val ditherBrush = rememberDitherBrush()
|
val ditherBrush = rememberDitherBrush()
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -575,30 +575,23 @@ fun ChatInput(
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Box(
|
Box(
|
||||||
Modifier.size(38.dp).clip(KaizenShapes.circle).background(iconBg).clickable(onClick = onAddClick),
|
Modifier.size(40.dp).clip(KaizenShapes.circle).background(iconBg).clickable(onClick = onAddClick),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Icon(KaizenIcons.Plus, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(19.dp))
|
Icon(KaizenIcons.Plus, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(20.dp))
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
Box(
|
Box(
|
||||||
Modifier.size(38.dp).clip(KaizenShapes.circle).background(iconBg),
|
Modifier.size(40.dp).clip(KaizenShapes.circle).background(iconBg),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Icon(KaizenIcons.Mic, stringResource(R.string.chat_attachment), tint = cs.onSurfaceVariant, modifier = Modifier.size(19.dp))
|
Icon(KaizenIcons.Mic, stringResource(R.string.chat_call), tint = cs.onSurfaceVariant, modifier = Modifier.size(20.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
if (hasContent) {
|
if (hasContent) {
|
||||||
SendButton(enabled = canSend, onClick = onSend)
|
SendButton(enabled = canSend, onClick = onSend)
|
||||||
} else {
|
|
||||||
Box(
|
|
||||||
Modifier.size(38.dp).clip(KaizenShapes.circle).background(iconBg),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
Icon(KaizenIcons.AudioLines, stringResource(R.string.chat_call), tint = cs.onSurfaceVariant, modifier = Modifier.size(19.dp))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -650,31 +643,20 @@ private fun PendingFileChip(pf: PendingFile, onRemove: () -> Unit) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun SendButton(enabled: Boolean, onClick: () -> Unit) {
|
private fun SendButton(enabled: Boolean, onClick: () -> Unit) {
|
||||||
val cs = MaterialTheme.colorScheme
|
val cs = MaterialTheme.colorScheme
|
||||||
val isDark = isSystemInDarkTheme()
|
|
||||||
val interaction = remember { MutableInteractionSource() }
|
val interaction = remember { MutableInteractionSource() }
|
||||||
val pressed by interaction.collectIsPressedAsState()
|
val pressed by interaction.collectIsPressedAsState()
|
||||||
val pressScale by animateFloatAsState(if (pressed) 0.88f else 1f, label = "send")
|
val pressScale by animateFloatAsState(if (pressed) 0.86f else 1f, label = "send")
|
||||||
val accent = LocalKaizenAccent.current
|
val accent = LocalKaizenAccent.current
|
||||||
|
val fill = if (enabled) Modifier.background(Brush.linearGradient(listOf(accent.primary, accent.primaryDeep)))
|
||||||
val fill = if (enabled) {
|
else Modifier.background(cs.surface)
|
||||||
Modifier.background(
|
|
||||||
Brush.verticalGradient(
|
|
||||||
listOf(
|
|
||||||
accent.primary.copy(alpha = if (isDark) 0.85f else 0.80f),
|
|
||||||
accent.primaryDeep.copy(alpha = if (isDark) 0.90f else 0.85f),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Modifier.background(cs.surfaceVariant.copy(alpha = 0.5f))
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
Modifier.size(38.dp).scale(pressScale).clip(KaizenShapes.circle).then(fill)
|
Modifier.size(44.dp).scale(pressScale).clip(KaizenShapes.circle).then(fill)
|
||||||
|
.border(1.dp, cs.outline, KaizenShapes.circle)
|
||||||
.clickable(interactionSource = interaction, indication = null, enabled = enabled, onClick = onClick),
|
.clickable(interactionSource = interaction, indication = null, enabled = enabled, onClick = onClick),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
SendArrow(color = if (enabled) accent.onPrimary else cs.onSurfaceVariant.copy(alpha = 0.5f))
|
SendArrow(color = if (enabled) accent.onPrimary else cs.onSurfaceVariant)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ fun ChatScreen(
|
||||||
},
|
},
|
||||||
gesturesEnabled = true // Allows swiping from the screen's left edge to open the drawer
|
gesturesEnabled = true // Allows swiping from the screen's left edge to open the drawer
|
||||||
) {
|
) {
|
||||||
MeshBackground(animateBlobs = messages.isEmpty()) {
|
MeshBackground {
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
// 1. Scrollable Chat Content
|
// 1. Scrollable Chat Content
|
||||||
// Stretches to the full screen, allowing bubbles to scroll cleanly behind the floating bottom dock.
|
// Stretches to the full screen, allowing bubbles to scroll cleanly behind the floating bottom dock.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ object KaizenIcons {
|
||||||
// ── Chat ────────────────────────────────────────────────────────────────
|
// ── Chat ────────────────────────────────────────────────────────────────
|
||||||
val Mic: ImageVector get() = Lucide.Mic
|
val Mic: ImageVector get() = Lucide.Mic
|
||||||
val Paperclip: ImageVector get() = Lucide.Paperclip
|
val Paperclip: ImageVector get() = Lucide.Paperclip
|
||||||
val AudioLines: ImageVector get() = Lucide.AudioLines
|
|
||||||
val Plus: ImageVector get() = Lucide.Plus
|
val Plus: ImageVector get() = Lucide.Plus
|
||||||
val Send: ImageVector get() = Lucide.SendHorizontal
|
val Send: ImageVector get() = Lucide.SendHorizontal
|
||||||
val Sparkles: ImageVector get() = Lucide.Sparkles
|
val Sparkles: ImageVector get() = Lucide.Sparkles
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ fun rememberTiltState(): State<Offset> {
|
||||||
Lifecycle.Event.ON_START -> {
|
Lifecycle.Event.ON_START -> {
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
sensorManager.registerListener(
|
sensorManager.registerListener(
|
||||||
listener, sensor, SensorManager.SENSOR_DELAY_UI
|
listener, sensor, SensorManager.SENSOR_DELAY_GAME
|
||||||
)
|
)
|
||||||
registered = true
|
registered = true
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +131,7 @@ fun rememberTiltState(): State<Offset> {
|
||||||
|
|
||||||
lifecycle.addObserver(lifecycleObserver)
|
lifecycle.addObserver(lifecycleObserver)
|
||||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||||
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI)
|
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME)
|
||||||
registered = true
|
registered = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue