@@ -7,23 +7,31 @@ import androidx.compose.foundation.layout.heightIn
7import androidx.compose.foundation.layout.padding
7import androidx.compose.foundation.layout.padding
8import androidx.compose.foundation.lazy.LazyColumn
8import androidx.compose.foundation.lazy.LazyColumn
9import androidx.compose.foundation.lazy.items
9import androidx.compose.foundation.lazy.items
10+import androidx.compose.foundation.text.KeyboardOptions
10import androidx.compose.material.icons.Icons
11import androidx.compose.material.icons.Icons
11import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
12import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
13+import androidx.compose.material.icons.filled.Search
12import androidx.compose.material3.CircularProgressIndicator
14import androidx.compose.material3.CircularProgressIndicator
13import androidx.compose.material3.ExperimentalMaterial3Api
15import androidx.compose.material3.ExperimentalMaterial3Api
14import androidx.compose.material3.Icon
16import androidx.compose.material3.Icon
15import androidx.compose.material3.MaterialTheme
17import androidx.compose.material3.MaterialTheme
16import androidx.compose.material3.ModalBottomSheet
18import androidx.compose.material3.ModalBottomSheet
19+import androidx.compose.material3.OutlinedTextField
17import androidx.compose.material3.Text
20import androidx.compose.material3.Text
18import androidx.compose.material3.TextButton
21import androidx.compose.material3.TextButton
19import androidx.compose.runtime.Composable
22import androidx.compose.runtime.Composable
23+import androidx.compose.runtime.getValue
24+import androidx.compose.runtime.mutableStateOf
25+import androidx.compose.runtime.remember
26+import androidx.compose.runtime.saveable.rememberSaveable
27+import androidx.compose.runtime.setValue
20import androidx.compose.ui.Alignment
28import androidx.compose.ui.Alignment
21import androidx.compose.ui.Modifier
29import androidx.compose.ui.Modifier
22import androidx.compose.ui.res.stringResource
30import androidx.compose.ui.res.stringResource
23import androidx.compose.ui.semantics.Role
31import androidx.compose.ui.semantics.Role
24import androidx.compose.ui.semantics.contentDescription
32import androidx.compose.ui.semantics.contentDescription
25−import androidx.compose.ui.semantics.role
26import androidx.compose.ui.semantics.semantics
33import androidx.compose.ui.semantics.semantics
34+import androidx.compose.ui.text.input.ImeAction
27import androidx.compose.ui.text.style.TextOverflow
35import androidx.compose.ui.text.style.TextOverflow
28import androidx.compose.ui.unit.dp
36import androidx.compose.ui.unit.dp
29import androidx.compose.foundation.layout.Box
37import androidx.compose.foundation.layout.Box
@@ -75,32 +83,59 @@ fun CommandPickerSheet(
75 modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
83 modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
86+ var query by rememberSaveable { mutableStateOf("") }
89+ onValueChange = { query = it },
90+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp, vertical = 8.dp),
91+ placeholder = { Text(stringResource(R.string.search_commands)) },
92+ leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
94+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
96+ val filtered = remember(commands, query) {
98+ if (q.isEmpty()) commands
99+ else commands.filter {
100+ it.name.contains(q, ignoreCase = true) ||
101+ it.displayDescription.contains(q, ignoreCase = true)
104+ if (filtered.isEmpty()) {
106+ stringResource(R.string.no_commands_match, query.trim()),
107+ style = MaterialTheme.typography.bodyMedium,
108+ color = MaterialTheme.colorScheme.onSurfaceVariant,
109+ modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
78− LazyColumn(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
112+ LazyColumn(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
79− items(commands, key = { it.name }) { cmd ->
113+ items(filtered, key = { it.name }) { cmd ->
81− modifier = Modifier
115+ modifier = Modifier
82− .fillMaxWidth()
116+ .fillMaxWidth()
83− .clickable(role = Role.Button) { onSelect(cmd); onDismiss() }
117+ .clickable(role = Role.Button) { onSelect(cmd); onDismiss() }
84− .padding(horizontal = 24.dp, vertical = 12.dp),
118+ .padding(horizontal = 24.dp, vertical = 12.dp),
86− androidx.compose.foundation.layout.Row(
87− verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
120+ androidx.compose.foundation.layout.Row(
121+ verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
89− Icon(Icons.AutoMirrored.Filled.PlaylistPlay, contentDescription = null)
123+ Icon(Icons.AutoMirrored.Filled.PlaylistPlay, contentDescription = null)
126+ style = MaterialTheme.typography.bodyLarge,
127+ modifier = Modifier.padding(start = 8.dp),
91− "/${cmd.name}",
131+ cmd.displayDescription,
92− style = MaterialTheme.typography.bodyLarge,
132+ style = MaterialTheme.typography.bodySmall,
133+ color = MaterialTheme.colorScheme.onSurfaceVariant,
135+ overflow = TextOverflow.Ellipsis,
93− modifier = Modifier.padding(start = 8.dp),
136+ modifier = Modifier.padding(start = 28.dp, top = 2.dp),
97− cmd.displayDescription,
98− style = MaterialTheme.typography.bodySmall,
99− color = MaterialTheme.colorScheme.onSurfaceVariant,
101− overflow = TextOverflow.Ellipsis,
102− modifier = Modifier.padding(start = 28.dp, top = 2.dp),