UI: code polish
This commit is contained in:
parent
fddf060d92
commit
e8b84c6ebf
|
|
@ -278,6 +278,7 @@ fun AppContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Model unload confirmation dialog
|
// Model unload confirmation dialog
|
||||||
|
// TODO-han.yin: show a progress indicator until the model success unloads?
|
||||||
if (showUnloadDialog) {
|
if (showUnloadDialog) {
|
||||||
UnloadModelConfirmationDialog(
|
UnloadModelConfirmationDialog(
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,11 @@ sealed class SystemPrompt {
|
||||||
* Creates a list of sample presets.
|
* Creates a list of sample presets.
|
||||||
*/
|
*/
|
||||||
val STUB_PRESETS = listOf(
|
val STUB_PRESETS = listOf(
|
||||||
|
Preset(
|
||||||
|
id = "haiku",
|
||||||
|
name = "Matsuo Bashō",
|
||||||
|
content = "You are a wise and contemplative Japanese poet in the spirit of Matsuo Bashō. You speak only through haiku—short poems that capture fleeting moments, nature’s beauty, or quiet reflections of life. Each of your responses must follow the traditional haiku format: 3 lines; 5 syllables in the first line; 7 syllables in the second line; 5 syllables in the third line. Your words are serene, subtle, and full of meaning. You draw on imagery from nature, emotion, and the impermanence of all things. You do not explain or elaborate. You let the silence between the words speak for itself. Never break character. Never explain your form. Only respond in haiku."
|
||||||
|
),
|
||||||
Preset(
|
Preset(
|
||||||
id = "assistant",
|
id = "assistant",
|
||||||
name = "Helpful Assistant",
|
name = "Helpful Assistant",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class PerformanceMonitor(private val context: Context) {
|
||||||
/**
|
/**
|
||||||
* Provides a flow of memory usage information that updates at the specified interval.
|
* Provides a flow of memory usage information that updates at the specified interval.
|
||||||
*/
|
*/
|
||||||
fun monitorMemoryUsage(intervalMs: Long = 5000): Flow<MemoryMetrics> = flow {
|
fun monitorMemoryUsage(intervalMs: Long = MEMORY_POLLING_INTERVAL): Flow<MemoryMetrics> = flow {
|
||||||
while(true) {
|
while(true) {
|
||||||
emit(getMemoryInfo())
|
emit(getMemoryInfo())
|
||||||
delay(intervalMs)
|
delay(intervalMs)
|
||||||
|
|
@ -31,7 +31,7 @@ class PerformanceMonitor(private val context: Context) {
|
||||||
/**
|
/**
|
||||||
* Provides a flow of battery information that updates at the specified interval.
|
* Provides a flow of battery information that updates at the specified interval.
|
||||||
*/
|
*/
|
||||||
fun monitorBattery(intervalMs: Long = 10000): Flow<BatteryMetrics> = flow {
|
fun monitorBattery(intervalMs: Long = BATTERY_POLLING_INTERVAL): Flow<BatteryMetrics> = flow {
|
||||||
while(true) {
|
while(true) {
|
||||||
emit(getBatteryInfo())
|
emit(getBatteryInfo())
|
||||||
delay(intervalMs)
|
delay(intervalMs)
|
||||||
|
|
@ -41,7 +41,7 @@ class PerformanceMonitor(private val context: Context) {
|
||||||
/**
|
/**
|
||||||
* Provides a flow of temperature information that updates at the specified interval.
|
* Provides a flow of temperature information that updates at the specified interval.
|
||||||
*/
|
*/
|
||||||
fun monitorTemperature(intervalMs: Long = 10000): Flow<TemperatureMetrics> = flow {
|
fun monitorTemperature(intervalMs: Long = TEMP_POLLING_INTERVAL): Flow<TemperatureMetrics> = flow {
|
||||||
while(true) {
|
while(true) {
|
||||||
emit(getTemperatureInfo())
|
emit(getTemperatureInfo())
|
||||||
delay(intervalMs)
|
delay(intervalMs)
|
||||||
|
|
@ -122,6 +122,12 @@ class PerformanceMonitor(private val context: Context) {
|
||||||
repeat(decimals) { multiplier *= 10 }
|
repeat(decimals) { multiplier *= 10 }
|
||||||
return (this * multiplier).roundToInt() / multiplier
|
return (this * multiplier).roundToInt() / multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MEMORY_POLLING_INTERVAL = 5000L
|
||||||
|
private const val BATTERY_POLLING_INTERVAL = 10000L
|
||||||
|
private const val TEMP_POLLING_INTERVAL = 10000L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,11 @@ fun AppScaffold(
|
||||||
val temperatureInfo by performanceViewModel.temperatureInfo.collectAsState()
|
val temperatureInfo by performanceViewModel.temperatureInfo.collectAsState()
|
||||||
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
|
val useFahrenheit by performanceViewModel.useFahrenheitUnit.collectAsState()
|
||||||
|
|
||||||
// Formatted memory usage
|
|
||||||
val memoryText = String.format("%.1f / %.1f GB", memoryUsage.availableGb, memoryUsage.totalGb)
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
SystemStatusTopBar(
|
SystemStatusTopBar(
|
||||||
title = title,
|
title = title,
|
||||||
memoryUsage = memoryText,
|
memoryUsage = memoryUsage,
|
||||||
batteryLevel = batteryInfo.level,
|
batteryLevel = batteryInfo.level,
|
||||||
temperature = temperatureInfo.temperature,
|
temperature = temperatureInfo.temperature,
|
||||||
useFahrenheit = useFahrenheit,
|
useFahrenheit = useFahrenheit,
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ private fun DrawerContent(
|
||||||
|
|
||||||
DrawerNavigationItem(
|
DrawerNavigationItem(
|
||||||
icon = Icons.Default.Folder,
|
icon = Icons.Default.Folder,
|
||||||
label = "Models Management",
|
label = "Models",
|
||||||
isSelected = currentRoute == com.example.llama.revamp.navigation.AppDestinations.MODELS_MANAGEMENT_ROUTE,
|
isSelected = currentRoute == com.example.llama.revamp.navigation.AppDestinations.MODELS_MANAGEMENT_ROUTE,
|
||||||
onClick = { onNavigate { navigationActions.navigateToModelsManagement() } }
|
onClick = { onNavigate { navigationActions.navigateToModelsManagement() } }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,28 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.BatteryAlert
|
import androidx.compose.material.icons.filled.BatteryAlert
|
||||||
import androidx.compose.material.icons.filled.BatteryFull
|
import androidx.compose.material.icons.filled.BatteryFull
|
||||||
import androidx.compose.material.icons.filled.BatteryStd
|
import androidx.compose.material.icons.filled.BatteryStd
|
||||||
|
import androidx.compose.material.icons.filled.Memory
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.Share
|
import androidx.compose.material.icons.filled.Share
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
import androidx.compose.material.icons.filled.Thermostat
|
||||||
|
import androidx.compose.material.icons.filled.WarningAmber
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.example.llama.revamp.monitoring.MemoryMetrics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top app bar that displays system status information and navigation controls.
|
* Top app bar that displays system status information and navigation controls.
|
||||||
|
|
@ -32,7 +35,7 @@ import androidx.compose.ui.unit.dp
|
||||||
@Composable
|
@Composable
|
||||||
fun SystemStatusTopBar(
|
fun SystemStatusTopBar(
|
||||||
title: String,
|
title: String,
|
||||||
memoryUsage: String,
|
memoryUsage: MemoryMetrics,
|
||||||
batteryLevel: Int,
|
batteryLevel: Int,
|
||||||
temperature: Float,
|
temperature: Float,
|
||||||
useFahrenheit: Boolean = false,
|
useFahrenheit: Boolean = false,
|
||||||
|
|
@ -41,14 +44,14 @@ fun SystemStatusTopBar(
|
||||||
onRerunPressed: (() -> Unit)? = null,
|
onRerunPressed: (() -> Unit)? = null,
|
||||||
onSharePressed: (() -> Unit)? = null
|
onSharePressed: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
CenterAlignedTopAppBar(
|
TopAppBar(
|
||||||
title = { Text(title) },
|
title = { Text(title) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
when {
|
when {
|
||||||
onBackPressed != null -> {
|
onBackPressed != null -> {
|
||||||
IconButton(onClick = onBackPressed) {
|
IconButton(onClick = onBackPressed) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.ArrowBack,
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
contentDescription = "Back"
|
contentDescription = "Back"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -64,15 +67,28 @@ fun SystemStatusTopBar(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
// Memory usage
|
// Memory usage
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Memory,
|
||||||
|
contentDescription = "RAM space",
|
||||||
|
tint = when {
|
||||||
|
memoryUsage.availableGb < 1 -> MaterialTheme.colorScheme.error
|
||||||
|
memoryUsage.availableGb < 3 -> MaterialTheme.colorScheme.tertiary
|
||||||
|
else -> MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(2.dp))
|
||||||
|
|
||||||
|
val memoryText = String.format("%.1f / %.1f GB", memoryUsage.availableGb, memoryUsage.totalGb)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = memoryUsage,
|
text = memoryText,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
modifier = Modifier.padding(end = 8.dp)
|
modifier = Modifier.padding(end = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Battery and temperature
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
// Battery icon and percentage
|
// Battery icon and percentage
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = when {
|
imageVector = when {
|
||||||
|
|
@ -92,9 +108,21 @@ fun SystemStatusTopBar(
|
||||||
style = MaterialTheme.typography.bodySmall
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(2.dp))
|
||||||
|
|
||||||
|
// Temperature icon and display
|
||||||
|
Icon(
|
||||||
|
imageVector = when {
|
||||||
|
temperature > 40 -> Icons.Default.WarningAmber
|
||||||
|
else -> Icons.Default.Thermostat
|
||||||
|
},
|
||||||
|
contentDescription = "Device temperature",
|
||||||
|
tint = when {
|
||||||
|
temperature > 40 -> MaterialTheme.colorScheme.error
|
||||||
|
else -> MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Temperature display
|
|
||||||
val tempDisplay = if (useFahrenheit) {
|
val tempDisplay = if (useFahrenheit) {
|
||||||
"${(temperature * 9/5 + 32).toInt()}°F"
|
"${(temperature * 9/5 + 32).toInt()}°F"
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -103,7 +131,7 @@ fun SystemStatusTopBar(
|
||||||
|
|
||||||
val tempTint = when {
|
val tempTint = when {
|
||||||
temperature >= 45 -> MaterialTheme.colorScheme.error
|
temperature >= 45 -> MaterialTheme.colorScheme.error
|
||||||
temperature >= 40 -> Color(0xFFFFA500) // Orange warning color
|
temperature >= 35 -> MaterialTheme.colorScheme.tertiary
|
||||||
else -> MaterialTheme.colorScheme.onSurface
|
else -> MaterialTheme.colorScheme.onSurface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,9 @@ import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.example.llama.revamp.engine.InferenceEngine
|
import com.example.llama.revamp.engine.InferenceEngine
|
||||||
import com.example.llama.revamp.navigation.NavigationActions
|
import com.example.llama.revamp.navigation.NavigationActions
|
||||||
import com.example.llama.revamp.ui.components.AppScaffold
|
import com.example.llama.revamp.ui.components.AppScaffold
|
||||||
|
|
@ -123,7 +121,7 @@ fun ConversationScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
title = selectedModel?.name ?: "Conversation",
|
title = "Chat",
|
||||||
drawerState = drawerState,
|
drawerState = drawerState,
|
||||||
navigationActions = navigationActions,
|
navigationActions = navigationActions,
|
||||||
onBackPressed = onBackPressed
|
onBackPressed = onBackPressed
|
||||||
|
|
@ -134,7 +132,7 @@ fun ConversationScreen(
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
) {
|
) {
|
||||||
// System prompt display (collapsible)
|
// System prompt display (collapsible)
|
||||||
AnimatedSystemPrompt(systemPrompt)
|
AnimatedSystemPrompt(selectedModel?.name, systemPrompt)
|
||||||
|
|
||||||
// Messages list
|
// Messages list
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -165,15 +163,19 @@ fun ConversationScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AnimatedSystemPrompt(systemPrompt: String?) {
|
fun AnimatedSystemPrompt(modelName: String?, systemPrompt: String?) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// TODO-han.yin: add model name into this card, on top of system prompt!
|
||||||
|
|
||||||
if (!systemPrompt.isNullOrBlank()) {
|
if (!systemPrompt.isNullOrBlank()) {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
onClick = { expanded = !expanded }
|
onClick = {
|
||||||
|
expanded = !expanded
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(16.dp)
|
modifier = Modifier.padding(16.dp)
|
||||||
|
|
@ -369,7 +371,7 @@ fun AssistantMessageBubble(
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = if (isThinking) "Thinking..." else "",
|
text = if (isThinking) "Thinking" else "",
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -120,14 +120,17 @@ fun ModelLoadingScreen(
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
// Mode selection cards
|
// Benchmark card
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 8.dp)
|
.padding(bottom = 8.dp)
|
||||||
.selectable(
|
.selectable(
|
||||||
selected = selectedMode == Mode.BENCHMARK,
|
selected = selectedMode == Mode.BENCHMARK,
|
||||||
onClick = { selectedMode = Mode.BENCHMARK },
|
onClick = {
|
||||||
|
selectedMode = Mode.BENCHMARK
|
||||||
|
useSystemPrompt = false
|
||||||
|
},
|
||||||
enabled = !isLoading,
|
enabled = !isLoading,
|
||||||
role = Role.RadioButton
|
role = Role.RadioButton
|
||||||
)
|
)
|
||||||
|
|
@ -142,23 +145,24 @@ fun ModelLoadingScreen(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Benchmark",
|
text = "Benchmark",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.padding(start = 8.dp)
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversation card with integrated system prompt
|
// Conversation card with integrated system prompt picker & editor
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 8.dp)
|
.padding(bottom = 4.dp)
|
||||||
// Only use weight if system prompt is active, otherwise wrap content
|
// Only use weight if system prompt is active, otherwise wrap content
|
||||||
.then(if (useSystemPrompt) Modifier.weight(1f) else Modifier)
|
.then(if (useSystemPrompt) Modifier.weight(1f) else Modifier)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp)
|
||||||
// Only fill height if system prompt is active
|
// Only fill height if system prompt is active
|
||||||
.then(if (useSystemPrompt) Modifier.fillMaxSize() else Modifier)
|
.then(if (useSystemPrompt) Modifier.fillMaxSize() else Modifier)
|
||||||
) {
|
) {
|
||||||
|
|
@ -172,7 +176,7 @@ fun ModelLoadingScreen(
|
||||||
enabled = !isLoading,
|
enabled = !isLoading,
|
||||||
role = Role.RadioButton
|
role = Role.RadioButton
|
||||||
)
|
)
|
||||||
.padding(16.dp),
|
.padding(top = 16.dp, start = 16.dp, end = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
RadioButton(
|
RadioButton(
|
||||||
|
|
@ -181,7 +185,7 @@ fun ModelLoadingScreen(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Conversation",
|
text = "Conversation",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.padding(start = 8.dp)
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -190,11 +194,11 @@ fun ModelLoadingScreen(
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "System prompt",
|
text = "Use system prompt",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 32.dp) // Align with radio text
|
.padding(start = 32.dp) // Align with radio text
|
||||||
|
|
@ -222,10 +226,13 @@ fun ModelLoadingScreen(
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.fillMaxSize() // Fill remaining card space
|
.fillMaxSize()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(start = 48.dp, end = 16.dp)
|
||||||
) {
|
) {
|
||||||
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
HorizontalDivider(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 4.dp, bottom = 8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
// Tab selector using SegmentedButton
|
// Tab selector using SegmentedButton
|
||||||
SingleChoiceSegmentedButtonRow(
|
SingleChoiceSegmentedButtonRow(
|
||||||
|
|
@ -277,7 +284,7 @@ fun ModelLoadingScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
// Content based on selected tab
|
// Content based on selected tab
|
||||||
when (selectedTab) {
|
when (selectedTab) {
|
||||||
|
|
@ -353,6 +360,8 @@ fun ModelLoadingScreen(
|
||||||
// Flexible spacer when system prompt is not active
|
// Flexible spacer when system prompt is not active
|
||||||
if (!useSystemPrompt) {
|
if (!useSystemPrompt) {
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
} else {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start button
|
// Start button
|
||||||
|
|
@ -369,8 +378,10 @@ fun ModelLoadingScreen(
|
||||||
viewModel.savePromptToRecents(prompt)
|
viewModel.savePromptToRecents(prompt)
|
||||||
prompt.content
|
prompt.content
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemPromptTab.CUSTOM ->
|
SystemPromptTab.CUSTOM ->
|
||||||
customPromptText.takeIf { it.isNotBlank() }?.also { promptText ->
|
customPromptText.takeIf { it.isNotBlank() }
|
||||||
|
?.also { promptText ->
|
||||||
// Save custom prompt to database
|
// Save custom prompt to database
|
||||||
viewModel.saveCustomPromptToRecents(promptText)
|
viewModel.saveCustomPromptToRecents(promptText)
|
||||||
}
|
}
|
||||||
|
|
@ -378,7 +389,9 @@ fun ModelLoadingScreen(
|
||||||
} else null
|
} else null
|
||||||
onConversationSelected(systemPrompt)
|
onConversationSelected(systemPrompt)
|
||||||
}
|
}
|
||||||
null -> { /* No mode selected */ }
|
|
||||||
|
null -> { /* No mode selected */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -400,10 +413,11 @@ fun ModelLoadingScreen(
|
||||||
is InferenceEngine.State.ProcessingSystemPrompt -> "Processing system prompt..."
|
is InferenceEngine.State.ProcessingSystemPrompt -> "Processing system prompt..."
|
||||||
is InferenceEngine.State.ModelLoaded -> "Preparing conversation..."
|
is InferenceEngine.State.ModelLoaded -> "Preparing conversation..."
|
||||||
else -> "Processing..."
|
else -> "Processing..."
|
||||||
}
|
},
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Text("Start")
|
Text(text = "Start", style = MaterialTheme.typography.titleMedium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +437,7 @@ fun PromptList(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.fillMaxSize(), // Fill available space
|
.fillMaxSize(), // Fill available space
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = prompts,
|
items = prompts,
|
||||||
|
|
@ -480,7 +494,7 @@ fun PromptList(
|
||||||
|
|
||||||
if (prompt.id != prompts.last().id) {
|
if (prompt.id != prompts.last().id) {
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.padding(top = 8.dp, start = 40.dp)
|
modifier = Modifier.padding(top = 8.dp, start = 32.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,6 @@ fun ModelSelectionScreen(
|
||||||
Text("Manage Models")
|
Text("Manage Models")
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "Downloaded Models",
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(models) { model ->
|
items(models) { model ->
|
||||||
ModelCard(
|
ModelCard(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.example.llama.revamp.ui.screens
|
package com.example.llama.revamp.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
|
@ -8,18 +7,13 @@ 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.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.DrawerState
|
import androidx.compose.material3.DrawerState
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -27,7 +21,6 @@ import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -57,7 +50,7 @@ fun ModelsManagementScreen(
|
||||||
val installedModels = remember { ModelInfo.getSampleModels() }
|
val installedModels = remember { ModelInfo.getSampleModels() }
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
title = "Models Management",
|
title = "Models",
|
||||||
navigationActions = navigationActions,
|
navigationActions = navigationActions,
|
||||||
onBackPressed = onBackPressed,
|
onBackPressed = onBackPressed,
|
||||||
onMenuPressed = onMenuClicked
|
onMenuPressed = onMenuClicked
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@ fun SettingsGeneralScreen(
|
||||||
title = "Dark Theme",
|
title = "Dark Theme",
|
||||||
description = "Use dark theme throughout the app",
|
description = "Use dark theme throughout the app",
|
||||||
checked = true, // This would be connected to theme state in a real app
|
checked = true, // This would be connected to theme state in a real app
|
||||||
onCheckedChange = { /* TODO: Implement theme switching */ }
|
onCheckedChange = {
|
||||||
|
/* TODO-hyin: Implement theme switching between Auto, Light and Dark */
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,5 +66,6 @@ val md_theme_dark_outlineVariant = Color(0xFF49454E)
|
||||||
val md_theme_dark_scrim = Color(0xFF000000)
|
val md_theme_dark_scrim = Color(0xFF000000)
|
||||||
|
|
||||||
// Additional app-specific colors
|
// Additional app-specific colors
|
||||||
|
val CriticalColor = Color(0xFFFF0000)
|
||||||
val WarningColor = Color(0xFFFFA000)
|
val WarningColor = Color(0xFFFFA000)
|
||||||
val SuccessColor = Color(0xFF388E3C)
|
val SuccessColor = Color(0xFF388E3C)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue