diff --git a/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt b/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt index 1cadcf7..16e3bd4 100644 --- a/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt +++ b/app/src/main/java/dev/kaizen/app/auth/LoginScreen.kt @@ -49,9 +49,7 @@ import dev.kaizen.app.chat.MeshBackground import dev.kaizen.app.net.DEFAULT_BASE_URL import dev.kaizen.app.net.LoginResult import dev.kaizen.app.net.SessionViewModel -import dev.kaizen.app.ui.theme.Amber -import dev.kaizen.app.ui.theme.AmberDeep -import dev.kaizen.app.ui.theme.OnAmber +import dev.kaizen.app.ui.theme.LocalKaizenAccent import androidx.compose.ui.res.stringResource import dev.kaizen.app.R import kotlinx.coroutines.launch @@ -65,6 +63,7 @@ import kotlinx.coroutines.launch @Composable fun LoginScreen(session: SessionViewModel, modifier: Modifier = Modifier) { val cs = MaterialTheme.colorScheme + val accent = LocalKaizenAccent.current val scope = rememberCoroutineScope() val ctx = androidx.compose.ui.platform.LocalContext.current @@ -165,6 +164,7 @@ private fun GlassField( ) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val glassBg = if (isDark) { Brush.verticalGradient(listOf(Color(0xFF1E293B).copy(alpha = 0.72f), Color(0xFF0F172A).copy(alpha = 0.85f))) @@ -196,7 +196,7 @@ private fun GlassField( value = value, onValueChange = onValueChange, textStyle = TextStyle(color = cs.onBackground, fontSize = 16.sp), - cursorBrush = SolidColor(Amber), + cursorBrush = SolidColor(accent.primary), singleLine = true, visualTransformation = if (password) PasswordVisualTransformation() else androidx.compose.ui.text.input.VisualTransformation.None, keyboardOptions = KeyboardOptions(keyboardType = keyboardType, imeAction = imeAction), @@ -209,8 +209,9 @@ private fun GlassField( @Composable private fun SignInButton(enabled: Boolean, loading: Boolean, onClick: () -> Unit) { val cs = MaterialTheme.colorScheme + val accent = LocalKaizenAccent.current val fill = if (enabled || loading) { - Modifier.background(Brush.linearGradient(listOf(Amber, AmberDeep))) + Modifier.background(Brush.linearGradient(listOf(accent.primary, accent.primaryDeep))) } else { Modifier.background(cs.surface) } @@ -227,11 +228,11 @@ private fun SignInButton(enabled: Boolean, loading: Boolean, onClick: () -> Unit contentAlignment = Alignment.Center, ) { if (loading) { - CircularProgressIndicator(color = OnAmber, strokeWidth = 2.4.dp, modifier = Modifier.size(22.dp)) + CircularProgressIndicator(color = accent.onPrimary, strokeWidth = 2.4.dp, modifier = Modifier.size(22.dp)) } else { Text( stringResource(R.string.login_submit), - color = if (enabled) OnAmber else cs.onSurfaceVariant, + color = if (enabled) accent.onPrimary else cs.onSurfaceVariant, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, ) diff --git a/app/src/main/java/dev/kaizen/app/chat/Background.kt b/app/src/main/java/dev/kaizen/app/chat/Background.kt index 42b29bc..1647960 100644 --- a/app/src/main/java/dev/kaizen/app/chat/Background.kt +++ b/app/src/main/java/dev/kaizen/app/chat/Background.kt @@ -35,11 +35,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import dev.kaizen.app.ui.motion.Durations import dev.kaizen.app.ui.sensor.rememberTiltState -import dev.kaizen.app.ui.theme.Amber -import dev.kaizen.app.ui.theme.AmberDeep -import dev.kaizen.app.ui.theme.BlobAmber -import dev.kaizen.app.ui.theme.BlobCoral -import dev.kaizen.app.ui.theme.BlobTeal +import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.ui.theme.DITHER_ALPHA import dev.kaizen.app.ui.theme.rememberDitherBrush @@ -47,6 +43,7 @@ import dev.kaizen.app.ui.theme.rememberDitherBrush @Composable fun MeshBackground(content: @Composable BoxScope.() -> Unit) { val dark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val base = MaterialTheme.colorScheme.background // Blobs are subtler in light mode. val factor = if (dark) 1f else 0.55f @@ -68,9 +65,9 @@ fun MeshBackground(content: @Composable BoxScope.() -> Unit) { drawRect(brush = ditherBrush, alpha = DITHER_ALPHA) }, ) { - Blob(BlobAmber, 360.dp, (-60f + drift * 30f).dp, (-40f + drift * 24f).dp, 0.30f * factor) - Blob(BlobCoral, 380.dp, (170f - drift * 44f).dp, (150f + drift * 40f).dp, 0.24f * factor) - Blob(BlobTeal, 300.dp, (30f + drift * 50f).dp, (560f - drift * 36f).dp, 0.22f * factor) + Blob(accent.blob1, 360.dp, (-60f + drift * 30f).dp, (-40f + drift * 24f).dp, 0.30f * factor) + Blob(accent.blob2, 380.dp, (170f - drift * 44f).dp, (150f + drift * 40f).dp, 0.24f * factor) + Blob(accent.blob4, 300.dp, (30f + drift * 50f).dp, (560f - drift * 36f).dp, 0.22f * factor) // Vignette: darken the edges so the center comes forward (dark mode only). if (dark) { Box( @@ -113,6 +110,7 @@ fun KaizenOrb( amplitude: Float? = null ) { val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val transition = rememberInfiniteTransition(label = "orb") // Slow breathing loop (7s for idle, 2.4s fast breath for streaming/recording thinking) @@ -151,7 +149,7 @@ fun KaizenOrb( colors = if (recording) { listOf(Color(0xFFE0A23E).copy(alpha = 0.45f), Color.Transparent) } else { - listOf(Amber.copy(alpha = if (streaming) 0.55f else 0.32f), Color.Transparent) + listOf(accent.primary.copy(alpha = if (streaming) 0.55f else 0.32f), Color.Transparent) } ), CircleShape, @@ -179,7 +177,7 @@ fun KaizenOrb( colorStops = arrayOf( 0.0f to Color(0xFFFFF1D6), // Ultra bright glowing cream-white core 0.35f to Color(0xFFFFCC80), // Rich luminous warm amber - 0.75f to AmberDeep, // Saturated golden amber + 0.75f to accent.primaryDeep, // Saturated accent 1.0f to Color(0xFF8D4F00) // Deep bronze-amber shadow edge (creates high contrast!) ), center = baseGradientCenter, @@ -210,10 +208,10 @@ fun KaizenOrb( val sweepBrush1 = Brush.sweepGradient( colorStops = arrayOf( 0.0f to Color.Transparent, - 0.16f to Amber.copy(alpha = 0.65f), + 0.16f to accent.primary.copy(alpha = 0.65f), 0.33f to Color.White.copy(alpha = 0.55f), 0.50f to Color.Transparent, - 0.66f to AmberDeep.copy(alpha = 0.50f), + 0.66f to accent.primaryDeep.copy(alpha = 0.50f), 1.0f to Color.Transparent ), center = coreCenter @@ -230,7 +228,7 @@ fun KaizenOrb( 0.0f to Color.Transparent, 0.25f to Color.White.copy(alpha = 0.40f), 0.50f to Color.Transparent, - 0.75f to Amber.copy(alpha = 0.50f), + 0.75f to accent.primary.copy(alpha = 0.50f), 1.0f to Color.Transparent ), center = coreCenter diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt index 50d81dc..e5ca7a8 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt @@ -64,9 +64,7 @@ import dev.kaizen.app.ui.effect.GlassTiers import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.motion.Durations import dev.kaizen.app.ui.shape.KaizenShapes -import dev.kaizen.app.ui.theme.Amber -import dev.kaizen.app.ui.theme.AmberDeep -import dev.kaizen.app.ui.theme.OnAmber +import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.R import androidx.compose.ui.res.stringResource import java.time.LocalDateTime @@ -189,13 +187,14 @@ fun ModePillsRow(selected: Set, onToggle: (Int) -> Unit, modifier: Modifier fun MessageRow(message: Message) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val hasAttachments = message.attachments.isNotEmpty() if (message.role == Role.User) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { val shape = KaizenShapes.lg - val borderBrush = remember { Brush.verticalGradient(listOf(Amber.copy(alpha = 0.45f), AmberDeep.copy(alpha = 0.15f))) } - val bgBrush = remember { Brush.verticalGradient(listOf(Amber.copy(alpha = 0.18f), AmberDeep.copy(alpha = 0.08f))) } + val borderBrush = remember(accent) { Brush.verticalGradient(listOf(accent.primary.copy(alpha = 0.45f), accent.primaryDeep.copy(alpha = 0.15f))) } + val bgBrush = remember(accent) { Brush.verticalGradient(listOf(accent.primary.copy(alpha = 0.18f), accent.primaryDeep.copy(alpha = 0.08f))) } Box( Modifier @@ -312,10 +311,11 @@ fun ChatInput( } Box(Modifier.weight(1f).heightIn(min = 36.dp).padding(horizontal = 4.dp), contentAlignment = Alignment.CenterStart) { if (value.isEmpty()) Text(stringResource(R.string.chat_input_placeholder), color = cs.onSurfaceVariant, fontSize = 16.sp) + val accent = LocalKaizenAccent.current BasicTextField( value = value, onValueChange = onValueChange, textStyle = TextStyle(color = cs.onBackground, fontSize = 16.sp, lineHeight = 22.sp), - cursorBrush = SolidColor(Amber), maxLines = 6, modifier = Modifier.fillMaxWidth(), + cursorBrush = SolidColor(accent.primary), maxLines = 6, modifier = Modifier.fillMaxWidth(), ) } GhostIconButton(Icons.Rounded.Call, "Anruf") @@ -383,7 +383,8 @@ private fun SendButton(enabled: Boolean, onClick: () -> Unit) { val interaction = remember { MutableInteractionSource() } val pressed by interaction.collectIsPressedAsState() val pressScale by animateFloatAsState(if (pressed) 0.86f else 1f, label = "send") - val fill = if (enabled) Modifier.background(Brush.linearGradient(listOf(Amber, AmberDeep))) + val accent = LocalKaizenAccent.current + val fill = if (enabled) Modifier.background(Brush.linearGradient(listOf(accent.primary, accent.primaryDeep))) else Modifier.background(cs.surface) Box( @@ -392,7 +393,7 @@ private fun SendButton(enabled: Boolean, onClick: () -> Unit) { .clickable(interactionSource = interaction, indication = null, enabled = enabled, onClick = onClick), contentAlignment = Alignment.Center, ) { - SendArrow(color = if (enabled) OnAmber else cs.onSurfaceVariant) + SendArrow(color = if (enabled) accent.onPrimary else cs.onSurfaceVariant) } } diff --git a/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt b/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt index 9217ff1..ca639f3 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ModelSheet.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import dev.kaizen.app.net.KaizenModel -import dev.kaizen.app.ui.theme.Amber +import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.haptics.rememberHaptics /** Human label + composite-id resolver for the top pill — falls back to a de-prefixed id. */ @@ -32,6 +32,7 @@ fun modelDisplayName(modelId: String, models: List): String = @Composable fun ModelPill(label: String, onClick: () -> Unit, modifier: Modifier = Modifier) { val cs = MaterialTheme.colorScheme + val accent = LocalKaizenAccent.current Row( modifier = modifier .clip(KaizenShapes.xl) @@ -108,6 +109,7 @@ fun ModelSheet( onDismiss: () -> Unit, ) { val cs = MaterialTheme.colorScheme + val accent = LocalKaizenAccent.current val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) var query by remember { mutableStateOf("") } val groups = remember(models, favorites, query) { buildGroups(models, favorites, query) } @@ -152,7 +154,7 @@ fun ModelSheet( value = query, onValueChange = { query = it }, singleLine = true, - cursorBrush = SolidColor(Amber), + cursorBrush = SolidColor(accent.primary), textStyle = androidx.compose.ui.text.TextStyle( color = cs.onBackground, fontSize = 14.sp @@ -221,6 +223,7 @@ private fun ModelRow( onToggleFavorite: () -> Unit, ) { val cs = MaterialTheme.colorScheme + val accent = LocalKaizenAccent.current val haptics = rememberHaptics() val providerInfo = remember(model.id, model.provider) { getProviderInfo(model.id, model.provider) } @@ -231,14 +234,14 @@ private fun ModelRow( .clip(KaizenShapes.md) .background( if (selected) { - Amber.copy(alpha = 0.08f) + accent.primary.copy(alpha = 0.08f) } else { cs.surfaceVariant.copy(alpha = 0.15f) } ) .border( width = 1.dp, - color = if (selected) Amber.copy(alpha = 0.25f) else Color.Transparent, + color = if (selected) accent.primary.copy(alpha = 0.25f) else Color.Transparent, shape = KaizenShapes.md ) .clickable { haptics.tick(); onClick() } @@ -290,7 +293,7 @@ private fun ModelRow( Icon( imageVector = if (favorite) Icons.Rounded.Star else Icons.Rounded.StarBorder, contentDescription = "Favorit", - tint = if (favorite) Amber else cs.onSurfaceVariant.copy(alpha = 0.35f), + tint = if (favorite) accent.primary else cs.onSurfaceVariant.copy(alpha = 0.35f), modifier = Modifier.size(20.dp), ) } @@ -300,7 +303,7 @@ private fun ModelRow( Icon( Icons.Rounded.Check, contentDescription = "Ausgewählt", - tint = Amber, + tint = accent.primary, modifier = Modifier.size(20.dp) ) } diff --git a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt index 4b9dd5d..156fd56 100644 --- a/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt +++ b/app/src/main/java/dev/kaizen/app/chat/Sidebar.kt @@ -46,7 +46,7 @@ import dev.kaizen.app.ui.effect.GlassTiers import dev.kaizen.app.ui.effect.KaizenShadows import dev.kaizen.app.ui.shape.KaizenShapes import dev.kaizen.app.R -import dev.kaizen.app.ui.theme.Amber +import dev.kaizen.app.ui.theme.LocalKaizenAccent import androidx.compose.ui.res.stringResource import java.time.Instant import java.time.LocalDate @@ -243,14 +243,15 @@ fun KaizenSidebar( private fun HistoryItemRow(item: ConversationSummary, selected: Boolean, onClick: () -> Unit) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val itemBg = when { - selected -> Amber.copy(alpha = 0.14f) + selected -> accent.primary.copy(alpha = 0.14f) isDark -> Color(0x0AFFFFFF) else -> Color(0x05000000) } val itemBorder = when { - selected -> Amber.copy(alpha = 0.35f) + selected -> accent.primary.copy(alpha = 0.35f) isDark -> Color.White.copy(alpha = 0.04f) else -> Color.Black.copy(alpha = 0.03f) } @@ -266,7 +267,7 @@ private fun HistoryItemRow(item: ConversationSummary, selected: Boolean, onClick verticalAlignment = Alignment.CenterVertically ) { if (item.pinned) { - Icon(Icons.Rounded.Star, null, tint = Amber, modifier = Modifier.size(14.dp)) + Icon(Icons.Rounded.Star, null, tint = accent.primary, modifier = Modifier.size(14.dp)) Spacer(Modifier.width(8.dp)) } Text( @@ -279,7 +280,7 @@ private fun HistoryItemRow(item: ConversationSummary, selected: Boolean, onClick ) if (item.locked) { Spacer(Modifier.width(8.dp)) - Icon(Icons.Rounded.Lock, stringResource(R.string.chat_locked_badge), tint = Amber, modifier = Modifier.size(15.dp)) + Icon(Icons.Rounded.Lock, stringResource(R.string.chat_locked_badge), tint = accent.primary, modifier = Modifier.size(15.dp)) } } } diff --git a/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt b/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt index 0764fc5..e5b81ad 100644 --- a/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt +++ b/app/src/main/java/dev/kaizen/app/chat/StreamBlocks.kt @@ -46,12 +46,13 @@ import androidx.compose.ui.unit.sp import dev.kaizen.app.net.SearchSource import dev.kaizen.app.net.ToolEvent import dev.kaizen.app.ui.shape.KaizenShapes -import dev.kaizen.app.ui.theme.Amber +import dev.kaizen.app.ui.theme.LocalKaizenAccent @Composable fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + 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) @@ -71,7 +72,7 @@ fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { .padding(horizontal = 12.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { - Icon(Icons.Rounded.Psychology, null, tint = Amber, modifier = Modifier.size(16.dp)) + Icon(Icons.Rounded.Psychology, null, tint = accent.primary, modifier = Modifier.size(16.dp)) Spacer(Modifier.width(8.dp)) Text( "Gedankengang", @@ -101,6 +102,7 @@ fun ReasoningBlock(reasoning: String, streaming: Boolean = false) { fun ToolEventsBlock(tools: List, streaming: Boolean = false) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val bg = if (isDark) Color.White.copy(alpha = 0.04f) else Color.Black.copy(alpha = 0.02f) val borderColor = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.04f) @@ -123,7 +125,7 @@ fun ToolEventsBlock(tools: List, streaming: Boolean = false) { val tint = when (tool.type) { "end" -> Color(0xFF10B981) "error" -> Color(0xFFEF4444) - else -> Amber + else -> accent.primary } Icon(icon, null, tint = tint, modifier = Modifier.size(14.dp)) Spacer(Modifier.width(8.dp)) diff --git a/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt b/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt index 79c2034..ad71181 100644 --- a/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt +++ b/app/src/main/java/dev/kaizen/app/settings/SettingsComponents.kt @@ -54,8 +54,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.kaizen.app.ui.theme.Amber -import dev.kaizen.app.ui.theme.AmberDeep +import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.ui.theme.Oklab import dev.kaizen.app.ui.theme.themes.KaizenThemeId @@ -159,6 +158,7 @@ fun SettingsInputCapsule( ) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val inputGlassBg = if (isDark) Color(0x12FFFFFF) else Color(0x06000000) val inputGlassBorder = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.04f) @@ -180,7 +180,7 @@ fun SettingsInputCapsule( fontSize = 14.sp, fontWeight = FontWeight.Medium ), - cursorBrush = SolidColor(Amber), + cursorBrush = SolidColor(accent.primary), singleLine = true, modifier = Modifier.fillMaxWidth() ) @@ -196,6 +196,7 @@ fun ThemeOptionPill( modifier: Modifier = Modifier ) { val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current // Exact OKLCH brand stops sourced directly from amber.css, blue.css, aurora.css, and mono.css val (colorStart, colorEnd) = remember(themeId, isDark) { @@ -231,7 +232,7 @@ fun ThemeOptionPill( .border( width = if (selected) 2.5.dp else 1.2.dp, brush = if (selected) { - Brush.verticalGradient(listOf(Amber, AmberDeep)) + Brush.verticalGradient(listOf(accent.primary, accent.primaryDeep)) } else { Brush.verticalGradient(listOf(Color.White.copy(alpha = 0.35f), Color.White.copy(alpha = 0.05f))) }, @@ -269,6 +270,7 @@ fun AppearanceTogglePill( ) { val cs = MaterialTheme.colorScheme val isDark = isSystemInDarkTheme() + val accent = LocalKaizenAccent.current val glassBorder = remember(isDark) { if (isDark) { @@ -284,7 +286,7 @@ fun AppearanceTogglePill( .clip(KaizenShapes.circle) .background( if (selected) { - Amber.copy(alpha = 0.16f) + accent.primary.copy(alpha = 0.16f) } else { if (isDark) Color(0x0AFFFFFF) else Color(0x82FFFFFF) } @@ -292,7 +294,7 @@ fun AppearanceTogglePill( .border( width = if (selected) 1.5.dp else 1.dp, brush = if (selected) { - Brush.verticalGradient(listOf(Amber.copy(alpha = 0.50f), Amber.copy(alpha = 0.15f))) + Brush.verticalGradient(listOf(accent.primary.copy(alpha = 0.50f), accent.primary.copy(alpha = 0.15f))) } else { glassBorder },