fix(desktop): search pill visibility, sidebar + Add Feed button

- SearchPill: fix collapsed state not rendering — removed nested
  Box/Surface/matchParentSize, replaced with simple if/else on
  expanded state within single Surface+Row
- Sidebar: add "+ Add Feed" item below custom feeds in FEEDS section
- Dropdown menu moves outside Surface to render properly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-05-29 07:02:50 +03:00
co-authored by Claude Opus 4.6
parent 55e7fcb903
commit c69c4462b9
2 changed files with 152 additions and 202 deletions
@@ -231,6 +231,15 @@ fun MainSidebar(
},
)
}
// "+ Add Feed" button
SidebarNavItem(
icon = MaterialSymbols.Add,
label = "Add Feed",
isActive = false,
expanded = expanded,
onClick = onAddColumn,
)
}
}
@@ -47,7 +47,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
@@ -64,11 +63,6 @@ import com.vitorpamplona.amethyst.desktop.SearchHistoryStore
import com.vitorpamplona.amethyst.desktop.platform.PlatformInfo
import com.vitorpamplona.amethyst.desktop.ui.theme.hoverHighlight
/**
* Search pill that expands inline into a text input with history dropdown.
* Collapsed: clickable pill with search icon + shortcut hint.
* Expanded: BasicTextField in same pill shape + dropdown below with recent/saved searches.
*/
@Composable
fun SearchPill(
onClick: () -> Unit,
@@ -84,214 +78,161 @@ fun SearchPill(
val savedSearches by SearchHistoryStore.savedSearches.collectAsState()
Surface(
onClick = { expanded = true },
shape = RoundedCornerShape(999.dp),
color = MaterialTheme.colorScheme.surfaceContainerHigh,
modifier = modifier.height(36.dp).hoverHighlight(),
) {
Box {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 12.dp),
) {
Icon(
MaterialSymbols.Search,
contentDescription = "Search",
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(Modifier.width(8.dp))
if (expanded) {
// Expanded: inline text input
BasicTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
modifier =
Modifier
.weight(1f)
.focusRequester(focusRequester)
.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyDown && event.key == Key.Escape) {
expanded = false
textFieldValue = TextFieldValue("")
focusManager.clearFocus()
true
} else {
false
}
},
textStyle =
MaterialTheme.typography.labelMedium.copy(
color = MaterialTheme.colorScheme.onSurface,
),
singleLine = true,
decorationBox = { innerTextField ->
Box(contentAlignment = Alignment.CenterStart) {
if (textFieldValue.text.isEmpty()) {
Text(
"Search notes, profiles...",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
innerTextField()
}
},
)
} else {
Text(
"Search...",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.weight(1f),
)
}
Spacer(Modifier.width(8.dp))
Text(
shortcutHint,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
}
// Dropdown below the pill when expanded
DropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
textFieldValue = TextFieldValue("")
},
) {
if (history.isNotEmpty()) {
Text(
"Recent",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
)
history.take(5).forEach { query ->
val text = QuerySerializer.serialize(query)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 12.dp),
modifier =
Modifier
.fillMaxWidth()
.clickable {
textFieldValue = TextFieldValue(text, TextRange(text.length))
}.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(
MaterialSymbols.Search,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Icon(MaterialSymbols.History, null, Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant)
Spacer(Modifier.width(8.dp))
BasicTextField(
value = textFieldValue,
onValueChange = { textFieldValue = it },
modifier =
Modifier
.weight(1f)
.focusRequester(focusRequester)
.onFocusChanged { state ->
if (!state.isFocused && expanded) {
expanded = false
textFieldValue = TextFieldValue("")
}
}.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyDown && event.key == Key.Escape) {
expanded = false
textFieldValue = TextFieldValue("")
focusManager.clearFocus()
true
} else {
false
}
},
textStyle =
MaterialTheme.typography.labelMedium.copy(
color = MaterialTheme.colorScheme.onSurface,
),
singleLine = true,
decorationBox = { innerTextField ->
Box(contentAlignment = Alignment.CenterStart) {
if (textFieldValue.text.isEmpty()) {
Text(
"Search notes, profiles...",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
innerTextField()
}
},
)
Spacer(Modifier.width(8.dp))
Text(
shortcutHint,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
// Dropdown with history
DropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
textFieldValue = TextFieldValue("")
},
) {
if (history.isNotEmpty()) {
Text(
"Recent",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
)
history.take(5).forEach { query ->
val text = QuerySerializer.serialize(query)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable {
textFieldValue = TextFieldValue(text, TextRange(text.length))
onClick()
}.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(
MaterialSymbols.History,
null,
Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(Modifier.width(8.dp))
Text(text, style = MaterialTheme.typography.bodySmall, maxLines = 1)
}
}
}
if (savedSearches.isNotEmpty()) {
if (history.isNotEmpty()) {
HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
}
Text(
"Saved",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
)
savedSearches.take(5).forEach { saved ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable { onClick() }
.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(
MaterialSymbols.Bookmark,
null,
Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.width(8.dp))
Text(saved.label, style = MaterialTheme.typography.bodySmall, maxLines = 1)
}
}
}
if (history.isEmpty() && savedSearches.isEmpty()) {
Text(
"Type to search",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(12.dp),
)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable { onClick() }
.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(
MaterialSymbols.AutoMirrored.OpenInNew,
null,
Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.width(8.dp))
Text(
"Open full search",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
)
}
}
} else {
// Collapsed: clickable pill
Surface(
onClick = {
expanded = true
},
shape = RoundedCornerShape(999.dp),
color = MaterialTheme.colorScheme.surfaceContainerHigh,
modifier = Modifier.matchParentSize(),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 12.dp),
) {
Icon(
MaterialSymbols.Search,
contentDescription = "Search",
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(Modifier.width(8.dp))
Text(
"Search...",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(Modifier.weight(1f))
Spacer(Modifier.width(8.dp))
Text(
shortcutHint,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
Text(text, style = MaterialTheme.typography.bodySmall, maxLines = 1)
}
}
}
if (savedSearches.isNotEmpty()) {
if (history.isNotEmpty()) {
HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
}
Text(
"Saved",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
)
savedSearches.take(5).forEach { saved ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable { onClick() }
.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(MaterialSymbols.Bookmark, null, Modifier.size(16.dp), tint = MaterialTheme.colorScheme.primary)
Spacer(Modifier.width(8.dp))
Text(saved.label, style = MaterialTheme.typography.bodySmall, maxLines = 1)
}
}
}
if (history.isEmpty() && savedSearches.isEmpty()) {
Text(
"Type to search",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(12.dp),
)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier
.fillMaxWidth()
.clickable { onClick() }
.hoverHighlight()
.padding(horizontal = 12.dp, vertical = 8.dp),
) {
Icon(MaterialSymbols.AutoMirrored.OpenInNew, null, Modifier.size(16.dp), tint = MaterialTheme.colorScheme.primary)
Spacer(Modifier.width(8.dp))
Text("Open full search", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.primary)
}
}
if (expanded) {