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)
38 options: List<ModelOption>,
39 selected: ModelOption?,
42 onSelect: (ModelOption) -> Unit,
44 onDismiss: () -> Unit,
46 ModalBottomSheet(onDismissRequest = onDismiss) {
48 stringResource(R.string.model),
49 style = MaterialTheme.typography.titleMedium,
50 modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 8.dp),
53 val loadingLabel = stringResource(R.string.loading)
55 modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp),
56 contentAlignment = Alignment.Center,
58 CircularProgressIndicator(Modifier.semantics { contentDescription = loadingLabel })
60 } else if (error && options.isEmpty()) {
62 stringResource(R.string.load_failed),
63 style = MaterialTheme.typography.bodyMedium,
64 color = MaterialTheme.colorScheme.error,
67 .clickable(role = Role.Button) { onRetry() }
68 .padding(horizontal = 24.dp, vertical = 16.dp),
70 } else if (options.isEmpty()) {
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),
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
85 .semantics(mergeDescendants = true) {
86 if (isSelected) this.selected = true
88 .clickable(role = Role.RadioButton) {
92 .padding(horizontal = 24.dp, vertical = 12.dp),
94 Row(verticalAlignment = Alignment.CenterVertically) {
97 style = MaterialTheme.typography.bodyLarge,
98 color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
99 modifier = Modifier.weight(1f),
104 contentDescription = null,
105 tint = MaterialTheme.colorScheme.primary,
106 modifier = Modifier.size(18.dp),
111 option.providerLabel,
112 style = MaterialTheme.typography.bodySmall,
113 color = MaterialTheme.colorScheme.onSurfaceVariant,
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list