Compare commits
2 commits
c014d6d90c
...
163a2cee28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
163a2cee28 | ||
|
|
0db20bfda8 |
3 changed files with 26 additions and 19 deletions
|
|
@ -231,7 +231,7 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo
|
||||||
- Mesh gradient background with dithered blobs
|
- Mesh gradient background with dithered blobs
|
||||||
- Phase-aware haptics (click → thinkingStart → responseStart → responseEnd)
|
- Phase-aware haptics (click → thinkingStart → responseStart → responseEnd)
|
||||||
- Tablet/landscape optimization (width capping)
|
- Tablet/landscape optimization (width capping)
|
||||||
- Dynamic keyboard handling (suggestion pills hide, spacer shrinks)
|
- Dynamic keyboard handling: `WindowInsets.ime` offset (NOT `imePadding()` — that inflates the dock; NOT `adjustResize` — hides the dock with edge-to-edge). Mode pills hide when keyboard open. `windowSoftInputMode=adjustNothing` in manifest.
|
||||||
- Settings screen with SettingsViewModel
|
- Settings screen with SettingsViewModel
|
||||||
|
|
||||||
### Bugs fixed (2026-06-19/20/21)
|
### Bugs fixed (2026-06-19/20/21)
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ fun ChatScreen(
|
||||||
var reasoningPreset by remember { mutableStateOf(ReasoningPreset.Standard) }
|
var reasoningPreset by remember { mutableStateOf(ReasoningPreset.Standard) }
|
||||||
var samplingPrefs by remember { mutableStateOf(SamplingPrefs()) }
|
var samplingPrefs by remember { mutableStateOf(SamplingPrefs()) }
|
||||||
var usedTokens by remember { mutableStateOf(0) }
|
var usedTokens by remember { mutableStateOf(0) }
|
||||||
|
var chatModel by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
// Navigation State
|
// Navigation State
|
||||||
var currentScreen by remember { mutableStateOf(AppScreen.Chat) }
|
var currentScreen by remember { mutableStateOf(AppScreen.Chat) }
|
||||||
|
|
@ -225,6 +226,7 @@ fun ChatScreen(
|
||||||
conversationId = null
|
conversationId = null
|
||||||
viewingLockedChat = false
|
viewingLockedChat = false
|
||||||
usedTokens = 0
|
usedTokens = 0
|
||||||
|
chatModel = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unlockAndOpen(summary: ConversationSummary) {
|
fun unlockAndOpen(summary: ConversationSummary) {
|
||||||
|
|
@ -314,6 +316,9 @@ fun ChatScreen(
|
||||||
if (stored.isNotEmpty()) {
|
if (stored.isNotEmpty()) {
|
||||||
val entities = stored.mapIndexed { i, m -> m.toEntity(summary.id, i) }
|
val entities = stored.mapIndexed { i, m -> m.toEntity(summary.id, i) }
|
||||||
chat.messageRepo.cacheMessages(summary.id, entities)
|
chat.messageRepo.cacheMessages(summary.id, entities)
|
||||||
|
val lastAssistant = stored.lastOrNull { it.role == "assistant" }
|
||||||
|
lastAssistant?.model?.let { chatModel = it }
|
||||||
|
usedTokens = stored.sumOf { (it.inputTokens ?: 0) + (it.outputTokens ?: 0) }
|
||||||
if (stored.size != cachedList.size || stored.zip(cachedList).any { (s, c) -> s.id != c.id || s.content != c.content }) {
|
if (stored.size != cachedList.size || stored.zip(cachedList).any { (s, c) -> s.id != c.id || s.content != c.content }) {
|
||||||
messages.clear()
|
messages.clear()
|
||||||
stored.forEach { m ->
|
stored.forEach { m ->
|
||||||
|
|
@ -673,12 +678,13 @@ fun ChatScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
|
val displayModel = chatModel ?: session.config?.model ?: ""
|
||||||
ModelPill(
|
ModelPill(
|
||||||
label = modelDisplayName(session.config?.model ?: "", models),
|
label = modelDisplayName(displayModel, models),
|
||||||
onClick = { haptics.tick(); showModelSheet = true },
|
onClick = { haptics.tick(); showModelSheet = true },
|
||||||
)
|
)
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
val ctxLen = models.find { it.id == session.config?.model }?.context_length ?: 1_000_000
|
val ctxLen = models.find { it.id == displayModel }?.context_length ?: 1_000_000
|
||||||
TokenBadge(used = usedTokens, contextLength = ctxLen)
|
TokenBadge(used = usedTokens, contextLength = ctxLen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -730,8 +736,7 @@ fun ChatScreen(
|
||||||
)
|
)
|
||||||
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
|
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
|
||||||
) {
|
) {
|
||||||
if (!imeVisible) {
|
Spacer(Modifier.height(if (imeVisible) 6.dp else 24.dp))
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
ModePillsRow(
|
ModePillsRow(
|
||||||
activeMode = activeMode,
|
activeMode = activeMode,
|
||||||
onToggle = { mode ->
|
onToggle = { mode ->
|
||||||
|
|
@ -743,8 +748,7 @@ fun ChatScreen(
|
||||||
samplingPrefs = samplingPrefs,
|
samplingPrefs = samplingPrefs,
|
||||||
onSamplingChange = { samplingPrefs = it },
|
onSamplingChange = { samplingPrefs = it },
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(10.dp))
|
Spacer(Modifier.height(if (imeVisible) 6.dp else 10.dp))
|
||||||
}
|
|
||||||
ChatInput(
|
ChatInput(
|
||||||
value = input,
|
value = input,
|
||||||
onValueChange = { input = it },
|
onValueChange = { input = it },
|
||||||
|
|
@ -791,7 +795,7 @@ fun ChatScreen(
|
||||||
models = models,
|
models = models,
|
||||||
selectedModelId = session.config?.model ?: "",
|
selectedModelId = session.config?.model ?: "",
|
||||||
favorites = session.favorites,
|
favorites = session.favorites,
|
||||||
onSelectModel = { session.setModel(it); haptics.tick() },
|
onSelectModel = { session.setModel(it); chatModel = it; haptics.tick() },
|
||||||
onToggleFavorite = { session.toggleFavorite(it) },
|
onToggleFavorite = { session.toggleFavorite(it) },
|
||||||
onDismiss = { showModelSheet = false },
|
onDismiss = { showModelSheet = false },
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@ data class StoredMessage(
|
||||||
val attachments: List<Attachment> = emptyList(),
|
val attachments: List<Attachment> = emptyList(),
|
||||||
val reasoning: String? = null,
|
val reasoning: String? = null,
|
||||||
val sources: List<SearchSource>? = null,
|
val sources: List<SearchSource>? = null,
|
||||||
|
val model: String? = null,
|
||||||
|
val inputTokens: Int? = null,
|
||||||
|
val outputTokens: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable private data class ConversationDetail(val messages: List<StoredMessage> = emptyList())
|
@Serializable private data class ConversationDetail(val messages: List<StoredMessage> = emptyList())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue