ui: input bar reasoning chip tweaks (#673)
* ui: input bar reasoning chip tweaks - Remove emoji from reasoning level labels (dropdown + chip text) - Reduce reasoning chip horizontal padding (8dp -> 4dp) - Move reasoning chip to right side (after spacer, next to mic) - Increase model chip minimum width (120dp -> 140dp) - Remove unused width import * fix(ui): add missing widthIn import in ComposerToolbar Resolves 'Unresolved reference widthIn' compile error at ComposerToolbar.kt:95 that broke CI (unit-tests, android-lint, instrumented-tests all fail on compileDebugKotlin). * ui(chat): enhance input bar layout, reasoning menu header, and card shadow Give ChatInputBar a floating rounded container with shadow contrast. Make model chip take available space and add header to reasoning menu. * fix(ui): remove trailing blank line in ComposerToolbar for ktlint Fix standard:no-consecutive-blank-lines ktlint violation. * ui(model): enhance ModelPickerDialog with rounded cards and provider badges Apply card styling, rounded corners (24dp dialog, 12dp cards), provider badges, and shadow contrast to ModelPickerDialog. * feat(model): add in-dialog model pinning to ModelPickerDialog Add pin/unpin icon buttons to model cards in ModelPickerDialog across both ModelScreen and ChatScreen. * ui(model): remove provider badge chip and eliminate bottom whitespace in ModelPickerDialog Replace bottom Cancel button with top-right Close icon to eliminate bottom whitespace. Remove inline provider badge chip from model cards. * perf(model): optimize ModelPickerDialog load time with lazy items and O(1) set lookup Lazy-register individual model items in LazyColumn and memoize pinnedSet to prevent synchronous rendering lag when opening dialog. --------- Co-authored-by: M57 <Hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
56de3323f0
commit
0bb418b4a7
7 files changed
+255
-86
No files matched your search
@@ -563,6 +563,7 @@ fun ChatScreen(
|
||||
title = "Switch model (this chat)",
|
||||
isLoading = state.modelPickerLoading && state.modelPickerProviders.isEmpty(),
|
||||
pinnedModels = state.modelPickerPinned,
|
||||
onPinToggle = { provider, model -> viewModel.togglePinModel(provider, model) },
|
||||
onSelect = { provider, model ->
|
||||
viewModel.sendSlashModel(provider, model)
|
||||
},
|
||||
|
||||
@@ -1286,6 +1286,21 @@ class ChatViewModel(
|
||||
_uiState.update { it.copy(showModelPicker = false, modelPickerLoading = false) }
|
||||
}
|
||||
|
||||
fun togglePinModel(
|
||||
providerSlug: String,
|
||||
modelName: String,
|
||||
) {
|
||||
val currentPinned = AuthManager.getPinnedModels().toMutableList()
|
||||
val target = PinnedModel(providerSlug, modelName)
|
||||
if (currentPinned.contains(target)) {
|
||||
currentPinned.remove(target)
|
||||
} else {
|
||||
currentPinned.add(target)
|
||||
}
|
||||
AuthManager.savePinnedModels(currentPinned)
|
||||
_uiState.update { it.copy(modelPickerPinned = currentPinned) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Hot-swap the CURRENT session's model via the /model slash command.
|
||||
*
|
||||
|
||||
@@ -105,10 +105,19 @@ fun ChatInputBar(
|
||||
exit = slideOutVertically(targetOffsetY = { it }),
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
shape = RoundedCornerShape(20.dp),
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
tonalElevation = 3.dp,
|
||||
shadowElevation = 0.dp,
|
||||
border =
|
||||
BorderStroke(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.4f),
|
||||
),
|
||||
tonalElevation = 2.dp,
|
||||
shadowElevation = 4.dp,
|
||||
) {
|
||||
Column {
|
||||
// Commands hidden from the suggestion menu — desktop/CLI-only and
|
||||
|
||||
@@ -4,11 +4,10 @@ import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
@@ -17,6 +16,7 @@ import androidx.compose.material.icons.filled.Stop
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
@@ -36,7 +36,7 @@ import androidx.compose.ui.unit.dp
|
||||
/**
|
||||
* Bottom toolbar row for the chat composer.
|
||||
*
|
||||
* Layout: [📎 attach] [model chip] [reasoning chip] [←spacer→] [🎙 mic]
|
||||
* Layout: [📎 attach] [model chip] [←spacer→] [🧠 reasoning] [🎙 mic]
|
||||
*
|
||||
* The reasoning chip opens a dropdown menu to pick a level (instead of cycling).
|
||||
*/
|
||||
@@ -76,7 +76,7 @@ fun ComposerToolbar(
|
||||
)
|
||||
}
|
||||
|
||||
// Model chip — fixed width, text scrolls if too long
|
||||
// Model chip — takes available space, fixed height
|
||||
FilterChip(
|
||||
selected = currentSessionModel != null,
|
||||
onClick = onModelTap,
|
||||
@@ -93,11 +93,12 @@ fun ComposerToolbar(
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.width(120.dp)
|
||||
.weight(1f)
|
||||
.height(28.dp)
|
||||
.testTag("model_chip"),
|
||||
)
|
||||
|
||||
// Reasoning chip with dropdown menu
|
||||
// Reasoning chip with dropdown menu (right side, next to mic)
|
||||
Box {
|
||||
FilterChip(
|
||||
selected = reasoningLevel != null,
|
||||
@@ -110,23 +111,33 @@ fun ComposerToolbar(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.testTag("reasoning_chip"),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(28.dp)
|
||||
.testTag("reasoning_chip"),
|
||||
)
|
||||
|
||||
DropdownMenu(
|
||||
expanded = showReasoningMenu,
|
||||
onDismissRequest = { showReasoningMenu = false },
|
||||
) {
|
||||
Text(
|
||||
text = "Reasoning",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
)
|
||||
HorizontalDivider()
|
||||
val allLevels =
|
||||
listOf(
|
||||
"none" to "🧠 None",
|
||||
"minimal" to "🧠 Minimal",
|
||||
"low" to "🧠 Low",
|
||||
"medium" to "🧠 Med",
|
||||
"high" to "🧠 High",
|
||||
"xhigh" to "🧠 XHigh",
|
||||
"max" to "🧠 Max",
|
||||
"ultra" to "🧠 Ultra",
|
||||
"none" to "None",
|
||||
"minimal" to "Minimal",
|
||||
"low" to "Low",
|
||||
"medium" to "Med",
|
||||
"high" to "High",
|
||||
"xhigh" to "XHigh",
|
||||
"max" to "Max",
|
||||
"ultra" to "Ultra",
|
||||
)
|
||||
allLevels.forEach { (level, label) ->
|
||||
DropdownMenuItem(
|
||||
@@ -156,8 +167,6 @@ fun ComposerToolbar(
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Mic / Stop button
|
||||
IconButton(
|
||||
onClick = onMicTap,
|
||||
@@ -189,19 +198,19 @@ fun ComposerToolbar(
|
||||
*
|
||||
* @param level One of: "none", "minimal", "low", "medium", "high",
|
||||
* "xhigh", "max", "ultra", or null for model default.
|
||||
* @return Display string such as "🧠 None", "🧠 Low", "🧠 XHigh", "🧠 Ultra", etc.
|
||||
* @return Display string such as "None", "Low", "XHigh", "Ultra", etc.
|
||||
*/
|
||||
private fun buildReasoningLabel(level: String?): String {
|
||||
return when (level) {
|
||||
null -> "🧠 Med"
|
||||
"none" -> "🧠 None"
|
||||
"minimal" -> "🧠 Minimal"
|
||||
"low" -> "🧠 Low"
|
||||
"medium" -> "🧠 Med"
|
||||
"high" -> "🧠 High"
|
||||
"xhigh" -> "🧠 XHigh"
|
||||
"max" -> "🧠 Max"
|
||||
"ultra" -> "🧠 Ultra"
|
||||
else -> "🧠 $level"
|
||||
null -> "Med"
|
||||
"none" -> "None"
|
||||
"minimal" -> "Minimal"
|
||||
"low" -> "Low"
|
||||
"medium" -> "Med"
|
||||
"high" -> "High"
|
||||
"xhigh" -> "XHigh"
|
||||
"max" -> "Max"
|
||||
"ultra" -> "Ultra"
|
||||
else -> level
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user