git.iko.soy/opencode-android
on master help sign in
Add search-field clear buttons and hidden-line counts on collapsed diffs/tool output

Every search field (session list, file browser, model/agent/command pickers) now shows a trailing clear (✕) icon when non-empty. Collapsed diffs and tool output now report how many lines are hidden ("Show N more lines") instead of a bare "Show more", falling back to the generic label for a single over-long line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commitfe5705b1835f69f8625edc77ad9182658b2098a5 authoriko <mail@iko.soy> dateThu 2 Jul 2026, 04:10 (3 months ago) parent178e422
8 files changed+61−2
app/src/main/java/soy/iko/opencode/ui/chat/AgentPickerSheet.kt+9 app/src/main/java/soy/iko/opencode/ui/chat/CommandPickerSheet.kt+9 app/src/main/java/soy/iko/opencode/ui/chat/ModelPickerSheet.kt+9 app/src/main/java/soy/iko/opencode/ui/chat/PartRenderer.kt+13−1 app/src/main/java/soy/iko/opencode/ui/components/DiffView.kt+1−1 app/src/main/java/soy/iko/opencode/ui/file/FileBrowserScreen.kt+8 app/src/main/java/soy/iko/opencode/ui/session/SessionListScreen.kt+8 app/src/main/res/values/strings.xml+4
app/src/main/java/soy/iko/opencode/ui/chat/AgentPickerSheet.kt+9
@@ -13,10 +13,12 @@ import androidx.compose.foundation.text.KeyboardActions
1313import androidx.compose.foundation.text.KeyboardOptions
1414import androidx.compose.material.icons.Icons
1515import androidx.compose.material.icons.filled.Check
16+import androidx.compose.material.icons.filled.Close
1617import androidx.compose.material.icons.filled.Search
1718import androidx.compose.material3.CircularProgressIndicator
1819import androidx.compose.material3.ExperimentalMaterial3Api
1920import androidx.compose.material3.Icon
21+import androidx.compose.material3.IconButton
2022import androidx.compose.material3.MaterialTheme
2123import androidx.compose.material3.ModalBottomSheet
2224import androidx.compose.material3.OutlinedTextField
@@ -97,6 +99,13 @@ fun AgentPickerSheet(
9799 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp, vertical = 8.dp),
98100 placeholder = { Text(stringResource(R.string.search_agents)) },
99101 leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
102+ trailingIcon = if (query.isNotEmpty()) {
103+ {
104+ IconButton(onClick = { query = "" }) {
105+ Icon(Icons.Filled.Close, contentDescription = stringResource(R.string.clear_search))
106+ }
107+ }
108+ } else null,
100109 singleLine = true,
101110 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
102111 keyboardActions = KeyboardActions(onSearch = { keyboardController?.hide() }),
app/src/main/java/soy/iko/opencode/ui/chat/CommandPickerSheet.kt+9
@@ -11,10 +11,12 @@ import androidx.compose.foundation.text.KeyboardActions
1111import androidx.compose.foundation.text.KeyboardOptions
1212import androidx.compose.material.icons.Icons
1313import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
14+import androidx.compose.material.icons.filled.Close
1415import androidx.compose.material.icons.filled.Search
1516import androidx.compose.material3.CircularProgressIndicator
1617import androidx.compose.material3.ExperimentalMaterial3Api
1718import androidx.compose.material3.Icon
19+import androidx.compose.material3.IconButton
1820import androidx.compose.material3.MaterialTheme
1921import androidx.compose.material3.ModalBottomSheet
2022import androidx.compose.material3.OutlinedTextField
@@ -100,6 +102,13 @@ fun CommandPickerSheet(
100102 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp, vertical = 8.dp),
101103 placeholder = { Text(stringResource(R.string.search_commands)) },
102104 leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
105+ trailingIcon = if (query.isNotEmpty()) {
106+ {
107+ IconButton(onClick = { query = "" }) {
108+ Icon(Icons.Filled.Close, contentDescription = stringResource(R.string.clear_search))
109+ }
110+ }
111+ } else null,
103112 singleLine = true,
104113 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
105114 keyboardActions = KeyboardActions(onSearch = { keyboardController?.hide() }),
app/src/main/java/soy/iko/opencode/ui/chat/ModelPickerSheet.kt+9
@@ -13,10 +13,12 @@ import androidx.compose.foundation.text.KeyboardActions
1313import androidx.compose.foundation.text.KeyboardOptions
1414import androidx.compose.material.icons.Icons
1515import androidx.compose.material.icons.filled.Check
16+import androidx.compose.material.icons.filled.Close
1617import androidx.compose.material.icons.filled.Search
1718import androidx.compose.material3.CircularProgressIndicator
1819import androidx.compose.material3.ExperimentalMaterial3Api
1920import androidx.compose.material3.Icon
21+import androidx.compose.material3.IconButton
2022import androidx.compose.material3.MaterialTheme
2123import androidx.compose.material3.ModalBottomSheet
2224import androidx.compose.material3.OutlinedTextField
@@ -97,6 +99,13 @@ fun ModelPickerSheet(
9799 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp, vertical = 8.dp),
98100 placeholder = { Text(stringResource(R.string.search_models)) },
99101 leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
102+ trailingIcon = if (query.isNotEmpty()) {
103+ {
104+ IconButton(onClick = { query = "" }) {
105+ Icon(Icons.Filled.Close, contentDescription = stringResource(R.string.clear_search))
106+ }
107+ }
108+ } else null,
100109 singleLine = true,
101110 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
102111 keyboardActions = KeyboardActions(onSearch = { keyboardController?.hide() }),
app/src/main/java/soy/iko/opencode/ui/chat/PartRenderer.kt+13−1
@@ -293,6 +293,13 @@ private fun ToolCallView(part: ToolPart, modifier: Modifier) {
293293 onToggleExpand = if (detail.length > COLLAPSED_LIMIT) {
294294 { expanded = !expanded }
295295 } else null,
296+ // How many lines are hidden while collapsed, so the expand affordance can
297+ // tell the user the scale of what's truncated instead of a bare "Show more".
298+ // `collapsed` is a prefix of `detail`, so its line count never exceeds it;
299+ // 0 (a single long line cut mid-way) falls back to the generic label.
300+ moreLines = if (detail.length > COLLAPSED_LIMIT) {
301+ detail.lines().size - collapsed.lines().size
302+ } else 0,
296303 expandedState = expandedState,
297304 collapsedState = collapsedState,
298305 onCopy = { copyToClipboard(context, "output", detail) },
@@ -315,6 +322,7 @@ private fun CollapsibleDetail(
315322 keySuffix: String,
316323 expanded: Boolean = false,
317324 onToggleExpand: (() -> Unit)? = null,
325+ moreLines: Int = 0,
318326 expandedState: String = "",
319327 collapsedState: String = "",
320328 onCopy: (() -> Unit)? = null,
@@ -370,7 +378,11 @@ private fun CollapsibleDetail(
370378 modifier = Modifier.size(16.dp),
371379 )
372380 Text(
373− if (expanded) stringResource(R.string.show_less) else stringResource(R.string.show_more),
381+ when {
382+ expanded -> stringResource(R.string.show_less)
383+ moreLines > 0 -> stringResource(R.string.show_more_lines, moreLines)
384+ else -> stringResource(R.string.show_more)
385+ },
374386 style = MaterialTheme.typography.labelSmall,
375387 modifier = Modifier.padding(start = 4.dp),
376388 )
app/src/main/java/soy/iko/opencode/ui/components/DiffView.kt+1−1
@@ -189,7 +189,7 @@ fun DiffView(diff: String, modifier: Modifier = Modifier) {
189189 onClick = { expanded = true },
190190 contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 10.dp),
191191 ) {
192− Text(stringResource(R.string.show_more))
192+ Text(stringResource(R.string.show_more_lines, lines.size - COLLAPSED_DIFF_LINES))
193193 }
194194 }
195195 Spacer(Modifier.height(10.dp))
app/src/main/java/soy/iko/opencode/ui/file/FileBrowserScreen.kt+8
@@ -19,6 +19,7 @@ import androidx.compose.foundation.text.KeyboardOptions
1919import androidx.compose.material.icons.Icons
2020import androidx.compose.material.icons.automirrored.filled.ArrowBack
2121import androidx.compose.material.icons.filled.ChevronRight
22+import androidx.compose.material.icons.filled.Close
2223import androidx.compose.material.icons.filled.Description
2324import androidx.compose.material.icons.filled.Folder
2425import androidx.compose.material.icons.filled.Home
@@ -105,6 +106,13 @@ fun FileBrowserScreen(
105106 modifier = Modifier.fillMaxWidth().padding(12.dp).testTag("file_search"),
106107 label = { Text(stringResource(R.string.search_files)) },
107108 leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
109+ trailingIcon = if (state.query.isNotEmpty()) {
110+ {
111+ IconButton(onClick = { vm.setQuery("") }) {
112+ Icon(Icons.Filled.Close, contentDescription = stringResource(R.string.clear_search))
113+ }
114+ }
115+ } else null,
108116 singleLine = true,
109117 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
110118 keyboardActions = KeyboardActions(onSearch = { keyboardController?.hide() }),
app/src/main/java/soy/iko/opencode/ui/session/SessionListScreen.kt+8
@@ -24,6 +24,7 @@ import androidx.compose.material.icons.filled.Add
2424import androidx.compose.material.icons.filled.ArrowDropDown
2525import androidx.compose.material.icons.filled.ChatBubbleOutline
2626import androidx.compose.material.icons.filled.CheckCircle
27+import androidx.compose.material.icons.filled.Close
2728import androidx.compose.material.icons.filled.Delete
2829import androidx.compose.material.icons.filled.Folder
2930import androidx.compose.material.icons.filled.MoreVert
@@ -374,6 +375,13 @@ private fun androidx.compose.foundation.layout.BoxScope.SessionListBody(
374375 modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp).testTag("session_search"),
375376 label = { Text(stringResource(R.string.search_sessions)) },
376377 leadingIcon = { Icon(Icons.Filled.Search, contentDescription = null) },
378+ trailingIcon = if (state.query.isNotEmpty()) {
379+ {
380+ IconButton(onClick = { onQueryChange("") }) {
381+ Icon(Icons.Filled.Close, contentDescription = stringResource(R.string.clear_search))
382+ }
383+ }
384+ } else null,
377385 singleLine = true,
378386 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
379387 keyboardActions = KeyboardActions(onSearch = { keyboardController?.hide() }),
app/src/main/res/values/strings.xml+4
@@ -138,6 +138,7 @@
138138 <string name="thoughts">Thoughts</string>
139139 <string name="show_more">Show more</string>
140140 <string name="show_less">Show less</string>
141+ <string name="show_more_lines">Show %1$d more lines</string>
141142 <string name="tool_completed">Completed</string>
142143 <string name="tool_error">Error</string>
143144 <string name="tool_running">Running</string>
@@ -270,4 +271,7 @@
270271 <string name="session_deleted_chat">Session deleted</string>
271272 <string name="today">Today</string>
272273 <string name="yesterday">Yesterday</string>
274+
275+ <!-- Search -->
276+ <string name="clear_search">Clear search</string>
273277</resources>