UI: polish ModelLoading screen
This commit is contained in:
parent
57b5001f5c
commit
d7afcc41d5
|
|
@ -17,11 +17,16 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -49,44 +54,70 @@ import java.util.Locale
|
|||
* architecture, quantization and file size in a compact card format.
|
||||
*
|
||||
* @param model The model information to display
|
||||
* @param onClick Action to perform when the card is clicked
|
||||
* @param isSelected Optional selection state (shows checkbox when not null)
|
||||
* @param isExpanded Whether additional details is expanded or shrunk
|
||||
* @param onExpanded Action to perform when the card is expanded or shrunk
|
||||
*/
|
||||
@Composable
|
||||
fun ModelCardCore(
|
||||
fun ModelCardCoreExpandable(
|
||||
model: ModelInfo,
|
||||
onClick: () -> Unit,
|
||||
isSelected: Boolean? = null,
|
||||
isExpanded: Boolean = false,
|
||||
onExpanded: ((Boolean) -> Unit)? = null,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick),
|
||||
colors = when (isSelected) {
|
||||
true -> CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
|
||||
.clickable { onExpanded?.invoke(!isExpanded) },
|
||||
colors = when (isExpanded) {
|
||||
true -> CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
false -> CardDefaults.cardColors()
|
||||
else -> CardDefaults.cardColors()
|
||||
},
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
||||
) {
|
||||
Row(
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Show checkbox if in selection mode
|
||||
if (isSelected != null) {
|
||||
Checkbox(
|
||||
checked = isSelected,
|
||||
onCheckedChange = { onClick() },
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
// Row 1: Model full name + chevron
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ModelCardContentTitleRow(model)
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMinimumInteractiveComponentSize provides Dp.Unspecified
|
||||
) {
|
||||
IconButton(onClick = { onExpanded?.invoke(!isExpanded) }) {
|
||||
Icon(
|
||||
imageVector = if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
|
||||
contentDescription = "Tap to ${if (isExpanded) "shrink" else "expand"} model card"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Core model info
|
||||
ModelCardContentCore(
|
||||
model = model,
|
||||
// Expandable content
|
||||
AnimatedVisibility(
|
||||
visible = isExpanded,
|
||||
enter = expandVertically() + fadeIn(),
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Row 2: Context length, size label
|
||||
ModelCardContentContextRow(model)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Row 3: Architecture, quantization, formatted size
|
||||
ModelCardContentArchitectureRow(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +137,7 @@ fun ModelCardCore(
|
|||
*/
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun ModelCardExpandable(
|
||||
fun ModelCardFullExpandable(
|
||||
model: ModelInfo,
|
||||
isSelected: Boolean? = null,
|
||||
onSelected: ((Boolean) -> Unit)? = null,
|
||||
|
|
@ -117,11 +148,11 @@ fun ModelCardExpandable(
|
|||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
onExpanded?.invoke(!isExpanded)
|
||||
},
|
||||
.clickable { onExpanded?.invoke(!isExpanded) },
|
||||
colors = when (isSelected) {
|
||||
true -> CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
|
||||
true -> CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
false -> CardDefaults.cardColors()
|
||||
else -> CardDefaults.cardColors()
|
||||
},
|
||||
|
|
@ -148,11 +179,7 @@ fun ModelCardExpandable(
|
|||
.padding(start = 16.dp, top = 16.dp, end = 16.dp)
|
||||
) {
|
||||
// Row 1: Model full name
|
||||
Text(
|
||||
text = model.formattedFullName,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
ModelCardContentTitleRow(model)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -283,11 +310,7 @@ fun ModelCardContentCore(
|
|||
) =
|
||||
Column(modifier = modifier) {
|
||||
// Row 1: Model full name
|
||||
Text(
|
||||
text = model.formattedFullName,
|
||||
style = MaterialTheme.typography.headlineSmall, // TODO
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
ModelCardContentTitleRow(model)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
|
|
@ -300,6 +323,14 @@ fun ModelCardContentCore(
|
|||
ModelCardContentArchitectureRow(model)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModelCardContentTitleRow(model: ModelInfo) =
|
||||
Text(
|
||||
text = model.formattedFullName,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun ModelCardContentContextRow(model: ModelInfo) =
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -21,26 +21,34 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.llama.revamp.engine.ModelLoadingMetrics
|
||||
import com.example.llama.revamp.ui.components.ModelCardCore
|
||||
import com.example.llama.revamp.ui.components.ModelCardCoreExpandable
|
||||
import com.example.llama.revamp.ui.components.ModelUnloadDialogHandler
|
||||
import com.example.llama.revamp.ui.theme.MonospacedTextStyle
|
||||
import com.example.llama.revamp.viewmodel.BenchmarkViewModel
|
||||
|
||||
@Composable
|
||||
fun BenchmarkScreen(
|
||||
// TODO-han.yin: Use loading metrics to show UI
|
||||
loadingMetrics: ModelLoadingMetrics,
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: BenchmarkViewModel
|
||||
) {
|
||||
// View model states
|
||||
val engineState by viewModel.engineState.collectAsState()
|
||||
val benchmarkResults by viewModel.benchmarkResults.collectAsState()
|
||||
val selectedModel by viewModel.selectedModel.collectAsState()
|
||||
val unloadDialogState by viewModel.unloadModelState.collectAsState()
|
||||
|
||||
// UI states
|
||||
var isModelCardExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
// Run benchmark when entering the screen
|
||||
LaunchedEffect(selectedModel) {
|
||||
viewModel.runBenchmark()
|
||||
|
|
@ -59,10 +67,10 @@ fun BenchmarkScreen(
|
|||
) {
|
||||
// Selected model card
|
||||
selectedModel?.let { model ->
|
||||
ModelCardCore(
|
||||
ModelCardCoreExpandable(
|
||||
model = model,
|
||||
onClick = { /* No action on click */ },
|
||||
isSelected = null
|
||||
isExpanded = isModelCardExpanded,
|
||||
onExpanded = { isModelCardExpanded = !isModelCardExpanded },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ import kotlinx.coroutines.launch
|
|||
*/
|
||||
@Composable
|
||||
fun ConversationScreen(
|
||||
// TODO-han.yin: Use loading metrics to show UI
|
||||
loadingMetrics: ModelLoadingMetrics,
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: ConversationViewModel
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.example.llama.revamp.data.model.SystemPrompt
|
||||
import com.example.llama.revamp.engine.ModelLoadingMetrics
|
||||
import com.example.llama.revamp.ui.components.ModelCardCore
|
||||
import com.example.llama.revamp.ui.components.ModelCardCoreExpandable
|
||||
import com.example.llama.revamp.ui.components.ModelUnloadDialogHandler
|
||||
import com.example.llama.revamp.viewmodel.ModelLoadingViewModel
|
||||
|
||||
|
|
@ -72,12 +72,15 @@ fun ModelLoadingScreen(
|
|||
onNavigateToConversation: (ModelLoadingMetrics) -> Unit,
|
||||
viewModel: ModelLoadingViewModel,
|
||||
) {
|
||||
// View model states
|
||||
val engineState by viewModel.engineState.collectAsState()
|
||||
val selectedModel by viewModel.selectedModel.collectAsState()
|
||||
val presetPrompts by viewModel.presetPrompts.collectAsState()
|
||||
val recentPrompts by viewModel.recentPrompts.collectAsState()
|
||||
val unloadDialogState by viewModel.unloadModelState.collectAsState()
|
||||
|
||||
// UI states
|
||||
var isModelCardExpanded by remember { mutableStateOf(false) }
|
||||
var selectedMode by remember { mutableStateOf<Mode?>(null) }
|
||||
var useSystemPrompt by remember { mutableStateOf(false) }
|
||||
var selectedPrompt by remember { mutableStateOf<SystemPrompt?>(null) }
|
||||
|
|
@ -116,11 +119,13 @@ fun ModelLoadingScreen(
|
|||
) {
|
||||
// Selected model card
|
||||
selectedModel?.let { model ->
|
||||
ModelCardCore(
|
||||
ModelCardCoreExpandable(
|
||||
model = model,
|
||||
onClick = { /* No action on click */ },
|
||||
isSelected = null
|
||||
isExpanded = isModelCardExpanded,
|
||||
onExpanded = { isModelCardExpanded = !isModelCardExpanded },
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
// Benchmark card
|
||||
|
|
@ -148,7 +153,7 @@ fun ModelLoadingScreen(
|
|||
)
|
||||
Text(
|
||||
text = "Benchmark",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -159,6 +164,12 @@ fun ModelLoadingScreen(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp)
|
||||
.selectable(
|
||||
selected = selectedMode == Mode.CONVERSATION,
|
||||
onClick = { selectedMode = Mode.CONVERSATION },
|
||||
enabled = !isLoading,
|
||||
role = Role.RadioButton
|
||||
)
|
||||
// Only fill height if system prompt is active
|
||||
.then(if (useSystemPrompt) Modifier.weight(1f) else Modifier)
|
||||
) {
|
||||
|
|
@ -173,12 +184,6 @@ fun ModelLoadingScreen(
|
|||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = selectedMode == Mode.CONVERSATION,
|
||||
onClick = { selectedMode = Mode.CONVERSATION },
|
||||
enabled = !isLoading,
|
||||
role = Role.RadioButton
|
||||
)
|
||||
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
|
@ -188,7 +193,7 @@ fun ModelLoadingScreen(
|
|||
)
|
||||
Text(
|
||||
text = "Conversation",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.llama.revamp.data.model.ModelInfo
|
||||
import com.example.llama.revamp.ui.components.ModelCardExpandable
|
||||
import com.example.llama.revamp.ui.components.ModelCardFullExpandable
|
||||
import com.example.llama.revamp.viewmodel.ModelSelectionViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -65,7 +65,7 @@ fun ModelSelectionScreen(
|
|||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(items = models, key = { it.id }) { model ->
|
||||
ModelCardExpandable(
|
||||
ModelCardFullExpandable(
|
||||
model = model,
|
||||
isSelected = if (model == preselectedModel) true else null,
|
||||
onSelected = { selected ->
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.example.llama.revamp.data.model.ModelInfo
|
||||
import com.example.llama.revamp.ui.components.ModelCardExpandable
|
||||
import com.example.llama.revamp.ui.components.ModelCardFullExpandable
|
||||
import com.example.llama.revamp.ui.components.ScaffoldEvent
|
||||
import com.example.llama.revamp.util.formatFileByteSize
|
||||
import com.example.llama.revamp.viewmodel.ModelManagementState
|
||||
|
|
@ -84,7 +84,7 @@ fun ModelsManagementScreen(
|
|||
items(items = sortedModels, key = { it.id }) { model ->
|
||||
val isSelected = if (isMultiSelectionMode) selectedModels.contains(model.id) else null
|
||||
|
||||
ModelCardExpandable(
|
||||
ModelCardFullExpandable(
|
||||
model = model,
|
||||
isSelected = isSelected,
|
||||
onSelected = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue