git.iko.soy/opencode-android
on 325e359 help sign in
106 lines 4.6 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.fillMaxWidth
6import androidx.compose.foundation.layout.heightIn
7import androidx.compose.foundation.layout.padding
8import androidx.compose.foundation.lazy.LazyColumn
9import androidx.compose.foundation.lazy.items
10import androidx.compose.material.icons.Icons
11import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
12import androidx.compose.material3.CircularProgressIndicator
13import androidx.compose.material3.ExperimentalMaterial3Api
14import androidx.compose.material3.Icon
15import androidx.compose.material3.MaterialTheme
16import androidx.compose.material3.ModalBottomSheet
17import androidx.compose.material3.Text
18import androidx.compose.runtime.Composable
19import androidx.compose.ui.Alignment
20import androidx.compose.ui.Modifier
21import androidx.compose.ui.res.stringResource
22import androidx.compose.ui.semantics.Role
23import androidx.compose.ui.semantics.contentDescription
24import androidx.compose.ui.semantics.role
25import androidx.compose.ui.semantics.semantics
26import androidx.compose.ui.text.style.TextOverflow
27import androidx.compose.ui.unit.dp
28import androidx.compose.foundation.layout.Box
29import soy.iko.opencode.data.model.Command
30import soy.iko.opencode.R
32/** Bottom sheet that lists slash-commands; selecting one invokes it. */
33@OptIn(ExperimentalMaterial3Api::class)
34@Composable
35fun CommandPickerSheet(
36 commands: List<Command>,
37 loading: Boolean,
38 error: Boolean,
39 onSelect: (Command) -> Unit,
40 onRetry: () -> Unit,
41 onDismiss: () -> Unit,
42) {
43 ModalBottomSheet(onDismissRequest = onDismiss) {
44 Text(
45 stringResource(R.string.commands),
46 style = MaterialTheme.typography.titleMedium,
47 modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 8.dp),
48 )
49 if (loading) {
50 val loadingLabel = stringResource(R.string.loading)
51 Box(
52 modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp),
53 contentAlignment = Alignment.Center,
54 ) {
55 CircularProgressIndicator(Modifier.semantics { contentDescription = loadingLabel })
56 }
57 } else if (error && commands.isEmpty()) {
58 Text(
59 stringResource(R.string.load_failed),
60 style = MaterialTheme.typography.bodyMedium,
61 color = MaterialTheme.colorScheme.error,
62 modifier = Modifier
63 .fillMaxWidth()
64 .clickable(role = Role.Button) { onRetry() }
65 .padding(horizontal = 24.dp, vertical = 16.dp),
66 )
67 } else if (commands.isEmpty()) {
68 Text(
69 stringResource(R.string.no_commands),
70 style = MaterialTheme.typography.bodyMedium,
71 color = MaterialTheme.colorScheme.onSurfaceVariant,
72 modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
73 )
74 } else {
75 LazyColumn(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
76 items(commands, key = { it.name }) { cmd ->
77 Column(
78 modifier = Modifier
79 .fillMaxWidth()
80 .clickable(role = Role.Button) { onSelect(cmd); onDismiss() }
81 .padding(horizontal = 24.dp, vertical = 12.dp),
82 ) {
83 androidx.compose.foundation.layout.Row(
84 verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
85 ) {
86 Icon(Icons.AutoMirrored.Filled.PlaylistPlay, contentDescription = null)
87 Text(
88 "/${cmd.name}",
89 style = MaterialTheme.typography.bodyLarge,
90 modifier = Modifier.padding(start = 8.dp),
91 )
92 }
93 Text(
94 cmd.displayDescription,
95 style = MaterialTheme.typography.bodySmall,
96 color = MaterialTheme.colorScheme.onSurfaceVariant,
97 maxLines = 2,
98 overflow = TextOverflow.Ellipsis,
99 modifier = Modifier.padding(start = 28.dp, top = 2.dp),
100 )
101 }
102 }
103 }
104 }
105 }