diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/DeckSidebar.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/DeckSidebar.kt index 0b1d11dfce..8ba2841162 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/DeckSidebar.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/deck/DeckSidebar.kt @@ -231,6 +231,15 @@ fun MainSidebar( }, ) } + + // "+ Add Feed" button + SidebarNavItem( + icon = MaterialSymbols.Add, + label = "Add Feed", + isActive = false, + expanded = expanded, + onClick = onAddColumn, + ) } } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchPill.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchPill.kt index fab04eb156..7692cfa1c9 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchPill.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/search/SearchPill.kt @@ -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) {