Compare commits

..

No commits in common. "f62cc4b7ac4bcf1000fc78241623c9e68cb4c972" and "6a8a76d9f8c88bc35830810b6dcc92e3dc9983ed" have entirely different histories.

2 changed files with 41 additions and 78 deletions

View file

@ -13,10 +13,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.isImeVisible
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -222,7 +219,6 @@ fun ChatScreen(
messages.clear() messages.clear()
conversationId = null conversationId = null
viewingLockedChat = false viewingLockedChat = false
usedTokens = 0
} }
fun openConversation(summary: ConversationSummary) { fun openConversation(summary: ConversationSummary) {
@ -273,7 +269,6 @@ fun ChatScreen(
return return
} }
viewingLockedChat = false viewingLockedChat = false
usedTokens = 0
scope.launch { scope.launch {
messages.clear() messages.clear()
val cachedList = chat.messageRepo.getCached(summary.id) val cachedList = chat.messageRepo.getCached(summary.id)
@ -660,10 +655,11 @@ fun ChatScreen(
ModelPill( ModelPill(
label = modelDisplayName(session.config?.model ?: "", models), label = modelDisplayName(session.config?.model ?: "", models),
onClick = { haptics.tick(); showModelSheet = true }, onClick = { haptics.tick(); showModelSheet = true },
modifier = Modifier.weight(1f, fill = false),
) )
Spacer(Modifier.weight(1f)) if (usedTokens > 0 || messages.isNotEmpty()) {
if (usedTokens > 0) {
val ctxLen = models.find { it.id == session.config?.model }?.context_length ?: 1_000_000 val ctxLen = models.find { it.id == session.config?.model }?.context_length ?: 1_000_000
Spacer(Modifier.width(8.dp))
TokenBadge(used = usedTokens, contextLength = ctxLen) TokenBadge(used = usedTokens, contextLength = ctxLen)
} }
} }
@ -708,9 +704,6 @@ fun ChatScreen(
.imePadding() .imePadding()
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier) .then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
) { ) {
@OptIn(ExperimentalLayoutApi::class)
val imeVisible = WindowInsets.isImeVisible
if (!imeVisible) {
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(24.dp))
ModePillsRow( ModePillsRow(
activeMode = activeMode, activeMode = activeMode,
@ -723,7 +716,6 @@ fun ChatScreen(
samplingPrefs = samplingPrefs, samplingPrefs = samplingPrefs,
onSamplingChange = { samplingPrefs = it }, onSamplingChange = { samplingPrefs = it },
) )
}
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
ChatInput( ChatInput(
value = input, value = input,

View file

@ -10,6 +10,8 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -48,7 +50,6 @@ import dev.kaizen.app.net.ToolEvent
import dev.kaizen.app.R import dev.kaizen.app.R
import dev.kaizen.app.ui.shape.KaizenShapes import dev.kaizen.app.ui.shape.KaizenShapes
import dev.kaizen.app.ui.theme.LocalKaizenAccent import dev.kaizen.app.ui.theme.LocalKaizenAccent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@Composable @Composable
@ -169,55 +170,36 @@ fun ToolEventsBlock(tools: List<ToolEvent>, streaming: Boolean = false) {
} }
} }
@OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun SourcesBlock(sources: List<SearchSource>, query: String = "") { fun SourcesBlock(sources: List<SearchSource>, query: String = "") {
val cs = MaterialTheme.colorScheme val cs = MaterialTheme.colorScheme
val isDark = isSystemInDarkTheme() val isDark = isSystemInDarkTheme()
val accent = LocalKaizenAccent.current
var expanded by remember { mutableStateOf(false) }
val context = LocalContext.current
Column { Column {
Row( if (query.isNotBlank()) {
Modifier Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 6.dp)) {
.clickable { expanded = !expanded } Icon(Icons.Rounded.Language, null, tint = cs.onSurfaceVariant, modifier = Modifier.size(14.dp))
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(Icons.Rounded.Language, null, tint = cs.onSurfaceVariant.copy(alpha = 0.6f), modifier = Modifier.size(13.dp))
Spacer(Modifier.width(6.dp)) Spacer(Modifier.width(6.dp))
Text( Text(query, color = cs.onSurfaceVariant, fontSize = 12.sp, fontWeight = FontWeight.Medium)
stringResource(R.string.stream_sources) + " (${sources.size})",
color = cs.onSurfaceVariant.copy(alpha = 0.6f),
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
)
Spacer(Modifier.width(4.dp))
Icon(
if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
null, tint = cs.onSurfaceVariant.copy(alpha = 0.4f), modifier = Modifier.size(12.dp),
)
} }
AnimatedVisibility(expanded, enter = expandVertically(), exit = shrinkVertically()) { }
Column(verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.padding(top = 4.dp)) { FlowRow(
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp),
) {
sources.forEach { source -> sources.forEach { source ->
SourceCard(source, isDark, accent) { SourceChip(source, isDark)
try {
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(source.url))
context.startActivity(intent)
} catch (_: Exception) { }
}
}
} }
} }
} }
} }
@Composable @Composable
private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen.app.ui.theme.themes.KaizenAccentScheme, onClick: () -> Unit) { private fun SourceChip(source: SearchSource, isDark: Boolean) {
val cs = MaterialTheme.colorScheme val cs = MaterialTheme.colorScheme
val bg = if (isDark) Color.White.copy(alpha = 0.05f) else Color.Black.copy(alpha = 0.03f) val bg = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.04f)
val borderColor = if (isDark) Color.White.copy(alpha = 0.06f) else Color.Black.copy(alpha = 0.05f) val borderColor = if (isDark) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.06f)
val domain = remember(source.url) { val domain = remember(source.url) {
try { try {
@ -227,38 +209,27 @@ private fun SourceCard(source: SearchSource, isDark: Boolean, accent: dev.kaizen
Column( Column(
Modifier Modifier
.fillMaxWidth() .width(140.dp)
.clip(KaizenShapes.sm) .clip(KaizenShapes.xs)
.background(bg) .background(bg)
.border(1.dp, borderColor, KaizenShapes.sm) .border(1.dp, borderColor, KaizenShapes.xs)
.clickable(onClick = onClick) .padding(horizontal = 10.dp, vertical = 8.dp),
.padding(horizontal = 12.dp, vertical = 10.dp),
) { ) {
Text( Text(
source.title.ifEmpty { domain }, source.title.ifEmpty { domain },
color = cs.onBackground, color = cs.onBackground,
fontSize = 13.sp, fontSize = 12.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
Spacer(Modifier.height(2.dp)) Spacer(Modifier.height(2.dp))
Text( Text(
source.url, domain,
color = accent.primary.copy(alpha = 0.7f), color = cs.onSurfaceVariant.copy(alpha = 0.6f),
fontSize = 11.sp, fontSize = 10.sp,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
if (source.snippet.isNotBlank()) {
Spacer(Modifier.height(3.dp))
Text(
source.snippet,
color = cs.onSurfaceVariant.copy(alpha = 0.5f),
fontSize = 11.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
} }
} }