git.iko.soy/opencode-android
on 325e359 help sign in
120 lines 5.3 kB history raw
1package soy.iko.opencode.ui.chat
3import androidx.compose.foundation.clickable
4import androidx.compose.foundation.layout.Column
5import androidx.compose.foundation.layout.Row
6import androidx.compose.foundation.layout.fillMaxWidth
7import androidx.compose.foundation.layout.heightIn
8import androidx.compose.foundation.layout.padding
9import androidx.compose.foundation.layout.size
10import androidx.compose.foundation.lazy.LazyColumn
11import androidx.compose.foundation.lazy.items
12import androidx.compose.material.icons.Icons
13import androidx.compose.material.icons.filled.Check
14import androidx.compose.material3.CircularProgressIndicator
15import androidx.compose.material3.ExperimentalMaterial3Api
16import androidx.compose.material3.Icon
17import androidx.compose.material3.MaterialTheme
18import androidx.compose.material3.ModalBottomSheet
19import androidx.compose.material3.Text
20import androidx.compose.runtime.Composable
21import androidx.compose.ui.Alignment
22import androidx.compose.ui.Modifier
23import androidx.compose.ui.res.stringResource
24import androidx.compose.ui.semantics.Role
25import androidx.compose.ui.semantics.contentDescription
26import androidx.compose.ui.semantics.role
27import androidx.compose.ui.semantics.selected
28import androidx.compose.ui.semantics.semantics
29import androidx.compose.ui.unit.dp
30import androidx.compose.foundation.layout.Box
31import soy.iko.opencode.data.model.ModelOption
32import soy.iko.opencode.R
34/** Bottom sheet that lists every provider/model and lets the user pick one. */
35@OptIn(ExperimentalMaterial3Api::class)
36@Composable
37fun ModelPickerSheet(
38 options: List<ModelOption>,
39 selected: ModelOption?,
40 loading: Boolean,
41 error: Boolean,
42 onSelect: (ModelOption) -> Unit,
43 onRetry: () -> Unit,
44 onDismiss: () -> Unit,
45) {
46 ModalBottomSheet(onDismissRequest = onDismiss) {
47 Text(
48 stringResource(R.string.model),
49 style = MaterialTheme.typography.titleMedium,
50 modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 8.dp),
51 )
52 if (loading) {
53 val loadingLabel = stringResource(R.string.loading)
54 Box(
55 modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp),
56 contentAlignment = Alignment.Center,
57 ) {
58 CircularProgressIndicator(Modifier.semantics { contentDescription = loadingLabel })
59 }
60 } else if (error && options.isEmpty()) {
61 Text(
62 stringResource(R.string.load_failed),
63 style = MaterialTheme.typography.bodyMedium,
64 color = MaterialTheme.colorScheme.error,
65 modifier = Modifier
66 .fillMaxWidth()
67 .clickable(role = Role.Button) { onRetry() }
68 .padding(horizontal = 24.dp, vertical = 16.dp),
69 )
70 } else if (options.isEmpty()) {
71 Text(
72 stringResource(R.string.no_models),
73 style = MaterialTheme.typography.bodyMedium,
74 color = MaterialTheme.colorScheme.onSurfaceVariant,
75 modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
76 )
77 } else {
78 LazyColumn(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
79 items(options, key = { it.providerID to it.modelID }) { option ->
80 val isSelected = option.providerID == selected?.providerID &&
81 option.modelID == selected?.modelID
82 Column(
83 modifier = Modifier
84 .fillMaxWidth()
85 .semantics(mergeDescendants = true) {
86 if (isSelected) this.selected = true
87 }
88 .clickable(role = Role.RadioButton) {
89 onSelect(option)
90 onDismiss()
91 }
92 .padding(horizontal = 24.dp, vertical = 12.dp),
93 ) {
94 Row(verticalAlignment = Alignment.CenterVertically) {
95 Text(
96 option.modelLabel,
97 style = MaterialTheme.typography.bodyLarge,
98 color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
99 modifier = Modifier.weight(1f),
100 )
101 if (isSelected) {
102 Icon(
103 Icons.Filled.Check,
104 contentDescription = null,
105 tint = MaterialTheme.colorScheme.primary,
106 modifier = Modifier.size(18.dp),
107 )
108 }
109 }
110 Text(
111 option.providerLabel,
112 style = MaterialTheme.typography.bodySmall,
113 color = MaterialTheme.colorScheme.onSurfaceVariant,
114 )
115 }
116 }
117 }
118 }
119 }