feat(chat): add configurable message stats (#1134)

This commit is contained in:
M57 authored and GitHub committed 2026-09-16 00:00:09 +03:00
1 parent afffcd95e8
commit 529b424eb4
20 files changed
+606 -44

No files matched your search

@@ -199,6 +199,14 @@ internal fun SettingsChatPage(
onTypingEffectDelayMsChange = viewModel::onTypingEffectDelayMsChange,
chatFontScale = state.chatFontScale,
onChatFontScaleChange = viewModel::onChatFontScaleChange,
messageStatsEnabled = state.messageStatsEnabled,
onMessageStatsEnabledChange = viewModel::onMessageStatsEnabledChange,
showUserMessageTokens = state.showUserMessageTokens,
onUserMessageTokensChange = viewModel::onUserMessageTokensChange,
showAssistantMessageTokens = state.showAssistantMessageTokens,
onAssistantMessageTokensChange = viewModel::onAssistantMessageTokensChange,
showTokensPerSecond = state.showTokensPerSecond,
onTokensPerSecondChange = viewModel::onTokensPerSecondChange,
)
}
}
@@ -38,6 +38,10 @@ data class SettingsUiState(
val typingEffectEnabled: Boolean = false,
val typingEffectDelayMs: Int = 30,
val chatFontScale: Float = 1.0f,
val messageStatsEnabled: Boolean = false,
val showUserMessageTokens: Boolean = true,
val showAssistantMessageTokens: Boolean = true,
val showTokensPerSecond: Boolean = true,
val profiles: List<ConnectionProfile> = emptyList(),
val selectedProfileId: String? = null,
val renameProfileName: String = "",
@@ -80,6 +84,10 @@ class SettingsViewModel(
val typingEffectEnabled = AuthManager.isTypingEffectEnabled()
val typingEffectDelayMs = AuthManager.getTypingEffectDelayMs()
val chatFontScale = AuthManager.getChatFontScale()
val messageStatsEnabled = AuthManager.isMessageStatsEnabled()
val showUserMessageTokens = AuthManager.isUserMessageTokensEnabled()
val showAssistantMessageTokens = AuthManager.isAssistantMessageTokensEnabled()
val showTokensPerSecond = AuthManager.isTokensPerSecondEnabled()
val profiles = AuthManager.getConnectionProfiles()
val appLanguage = AuthManager.getAppLanguage()
val renameProfileName =
@@ -101,6 +109,10 @@ class SettingsViewModel(
typingEffectEnabled = typingEffectEnabled,
typingEffectDelayMs = typingEffectDelayMs,
chatFontScale = chatFontScale,
messageStatsEnabled = messageStatsEnabled,
showUserMessageTokens = showUserMessageTokens,
showAssistantMessageTokens = showAssistantMessageTokens,
showTokensPerSecond = showTokensPerSecond,
profiles = profiles,
selectedProfileId = selectedId,
renameProfileName = renameProfileName,
@@ -327,6 +339,26 @@ class SettingsViewModel(
AuthManager.setChatFontScale(scale)
}
fun onMessageStatsEnabledChange(enabled: Boolean) {
_uiState.update { it.copy(messageStatsEnabled = enabled, isSaved = false) }
AuthManager.setMessageStatsEnabled(enabled)
}
fun onUserMessageTokensChange(enabled: Boolean) {
_uiState.update { it.copy(showUserMessageTokens = enabled, isSaved = false) }
AuthManager.setUserMessageTokensEnabled(enabled)
}
fun onAssistantMessageTokensChange(enabled: Boolean) {
_uiState.update { it.copy(showAssistantMessageTokens = enabled, isSaved = false) }
AuthManager.setAssistantMessageTokensEnabled(enabled)
}
fun onTokensPerSecondChange(enabled: Boolean) {
_uiState.update { it.copy(showTokensPerSecond = enabled, isSaved = false) }
AuthManager.setTokensPerSecondEnabled(enabled)
}
/** Clear all auth credentials — logs out and returns to landing screen. */
fun logout() {
AuthManager.setToken(null)
@@ -355,6 +387,10 @@ class SettingsViewModel(
AuthManager.setThemePreset(state.themePreset)
AuthManager.setTypingEffectEnabled(state.typingEffectEnabled)
AuthManager.setTypingEffectDelayMs(state.typingEffectDelayMs)
AuthManager.setMessageStatsEnabled(state.messageStatsEnabled)
AuthManager.setUserMessageTokensEnabled(state.showUserMessageTokens)
AuthManager.setAssistantMessageTokensEnabled(state.showAssistantMessageTokens)
AuthManager.setTokensPerSecondEnabled(state.showTokensPerSecond)
ApiClient.rebuild()
viewModelScope.launch(ioDispatcher) {
@@ -32,6 +32,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Density
@@ -204,6 +205,14 @@ internal fun ChatSection(
onTypingEffectDelayMsChange: (Int) -> Unit,
chatFontScale: Float = 1.0f,
onChatFontScaleChange: (Float) -> Unit = {},
messageStatsEnabled: Boolean = false,
onMessageStatsEnabledChange: (Boolean) -> Unit = {},
showUserMessageTokens: Boolean = true,
onUserMessageTokensChange: (Boolean) -> Unit = {},
showAssistantMessageTokens: Boolean = true,
onAssistantMessageTokensChange: (Boolean) -> Unit = {},
showTokensPerSecond: Boolean = true,
onTokensPerSecondChange: (Boolean) -> Unit = {},
) {
val fontScaleOptions = listOf(0.85f, 1.0f, 1.15f, 1.30f, 1.50f)
val currentIndex =
@@ -369,6 +378,53 @@ internal fun ChatSection(
Spacer(modifier = Modifier.height(20.dp))
Text(
text = stringResource(R.string.settings_item_message_stats),
style = MaterialTheme.typography.titleMedium,
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(R.string.settings_desc_message_stats),
modifier = Modifier.weight(1f),
style =
MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
Switch(
checked = messageStatsEnabled,
onCheckedChange = onMessageStatsEnabledChange,
modifier = Modifier.testTag("settings_message_stats"),
)
}
MessageStatsOption(
label = stringResource(R.string.settings_item_user_message_tokens),
checked = showUserMessageTokens,
enabled = messageStatsEnabled,
onCheckedChange = onUserMessageTokensChange,
testTag = "settings_user_message_tokens",
)
MessageStatsOption(
label = stringResource(R.string.settings_item_assistant_message_tokens),
checked = showAssistantMessageTokens,
enabled = messageStatsEnabled,
onCheckedChange = onAssistantMessageTokensChange,
testTag = "settings_assistant_message_tokens",
)
MessageStatsOption(
label = stringResource(R.string.settings_item_tokens_per_second),
checked = showTokensPerSecond,
enabled = messageStatsEnabled,
onCheckedChange = onTokensPerSecondChange,
testTag = "settings_tps",
)
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
@@ -440,3 +496,37 @@ internal fun ChatSection(
}
}
}
@Composable
private fun MessageStatsOption(
label: String,
checked: Boolean,
enabled: Boolean,
onCheckedChange: (Boolean) -> Unit,
testTag: String,
) {
Row(
modifier = Modifier.fillMaxWidth().padding(start = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = label,
style =
MaterialTheme.typography.bodyMedium.copy(
color =
if (enabled) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
},
),
)
Switch(
checked = checked,
enabled = enabled,
onCheckedChange = onCheckedChange,
modifier = Modifier.testTag(testTag),
)
}
}