23df9cfbbe84
fix the real vault list bug: PeachSegmentedControl rendered every option as an equal-weight full-width accent-or-neutral pill (SMART and ALL both solid orange bars). Added PeachFilterPills (small content-sized accent pill for the active option, bare quiet text for the rest) matching the browser's actual restraint, and switched vault list's sort row to it. Consolidated the header from 4 stacked rows to 3 by folding the ALL/NOTES segmented control into a single toggle icon in the action cluster. Added explicit '9 entries' count matching browser's integrated count display. Tightened entry row vertical padding
- time
- 2026-07-24 06:31 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- critical-corrections-c1-c7
- parent
- ac11454ad814
↓ peach-android-23df9cfb.tar.gzatlas at this save
files changed2 files
app/src/main/java/com/peachpasswords/ui/components/PeachSegmentedControl.kt+54 −0app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+35 −27app/src/main/java/com/peachpasswords/ui/components/PeachSegmentedControl.kt+54 −0
@@ -12,6 +12,7 @@1212 import androidx.compose.runtime.Composable1313 import androidx.compose.ui.Alignment1414 import androidx.compose.ui.Modifier15+import androidx.compose.ui.draw.clip1516 import androidx.compose.ui.unit.dp1617 import com.peachpasswords.ui.theme.LocalPeachAccent1718 import com.peachpasswords.ui.theme.PeachSpacing@@ -61,6 +62,59 @@6162 Text(segment.label.uppercase(), style = PeachType.sortPill)6263 }6364 }65+ }66+ }67+ }68+}69+70+/**71+ * Browser-matched quiet filter row: the active option gets one small,72+ * content-sized accent pill; every other option is bare text (no box,73+ * no fill) sitting quietly beside it. Distinct from [PeachSegmentedControl],74+ * which renders every option as an equal-weight full-width button — right for75+ * a real multi-way choice (entry type), wrong for a filter/sort row, where76+ * giving every option the same loud pill treatment reads as several accent77+ * moments stacked instead of one (SMART/A-Z/Z-A/RECENT in the browser).78+ */79+@Composable80+fun PeachFilterPills(81+ segments: List<PeachSegment>,82+ selectedKey: String,83+ onSelected: (String) -> Unit,84+ modifier: Modifier = Modifier,85+) {86+ val accent = LocalPeachAccent.current87+ Row(88+ modifier = modifier,89+ verticalAlignment = Alignment.CenterVertically,90+ horizontalArrangement = Arrangement.spacedBy(PeachSpacing.md),91+ ) {92+ segments.forEach { segment ->93+ val selected = segment.key == selectedKey94+ if (selected) {95+ Surface(96+ color = accent,97+ contentColor = peachColors.Bg,98+ shape = RoundedCornerShape(999.dp),99+ ) {100+ Text(101+ segment.label.uppercase(),102+ style = PeachType.sortPill,103+ modifier = Modifier104+ .clip(RoundedCornerShape(999.dp))105+ .clickable { onSelected(segment.key) }106+ .padding(horizontal = PeachSpacing.sm, vertical = PeachSpacing.xs),107+ )108+ }109+ } else {110+ Text(111+ segment.label.uppercase(),112+ color = peachColors.T2,113+ style = PeachType.sortPill,114+ modifier = Modifier115+ .clickable { onSelected(segment.key) }116+ .padding(vertical = PeachSpacing.xs),117+ )64118 }65119 }66120 }app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+35 −27
@@ -36,6 +36,7 @@3636 import androidx.compose.foundation.shape.RoundedCornerShape3737 import androidx.compose.foundation.text.BasicTextField3838 import androidx.compose.material.icons.Icons39+import androidx.compose.material.icons.automirrored.outlined.Notes3940 import androidx.compose.material.icons.outlined.DeleteOutline4041 import androidx.compose.material.icons.outlined.FavoriteBorder4142 import androidx.compose.material.icons.outlined.Lock@@ -94,8 +95,8 @@9495 import com.peachpasswords.ui.sync.SyncViewModel9596 import com.peachpasswords.ui.PeachNav9697 import com.peachpasswords.ui.components.PeachEmptyOrEndState98+import com.peachpasswords.ui.components.PeachFilterPills9799 import com.peachpasswords.ui.components.PeachSegment98-import com.peachpasswords.ui.components.PeachSegmentedControl99100 import com.peachpasswords.ui.components.PeachTopBar100101 import com.peachpasswords.ui.components.FaviconInitials101102 import com.peachpasswords.ui.theme.LocalPeachAccent@@ -176,7 +177,7 @@176177 SearchBar(177178 value = search,178179 onChange = vm::setSearch,179- count = if (search.isNotBlank()) "${entries.size}/$total" else "$total",180+ count = if (search.isNotBlank()) "${entries.size}/$total" else pluralEntries(total),180181 )181182 if (allTags.isNotEmpty()) {182183 TagFilterStrip(@@ -412,6 +413,8 @@412413 }413414 }414415 416+private fun pluralEntries(total: Int): String = if (total == 1) "1 entry" else "$total entries"417+415418 @Composable416419 private fun SearchBar(value: String, onChange: (String) -> Unit, count: String) {417420 val accent = LocalPeachAccent.current@@ -457,7 +460,11 @@457460 onTrash: () -> Unit,458461 ) {459462 val hairline = peachColors.T3460- Column(463+ // One row, matching the browser's single combined sort+actions bar — the464+ // notes-only filter used to get a whole second segmented control the same465+ // visual weight as the 4-way sort control; folded into a single toggle466+ // icon in the action cluster instead, same as trash/select/health.467+ Row(461468 modifier = Modifier462469 .fillMaxWidth()463470 .drawBehind {@@ -468,45 +475,36 @@468475 )469476 }470477 .padding(horizontal = PeachSpacing.md, vertical = PeachSpacing.sm),471- verticalArrangement = Arrangement.spacedBy(PeachSpacing.sm),478+ verticalAlignment = Alignment.CenterVertically,479+ horizontalArrangement = Arrangement.spacedBy(PeachSpacing.sm),472480 ) {473- PeachSegmentedControl(481+ PeachFilterPills(474482 segments = SortOrder.entries.map { PeachSegment(it.name, it.label) },475483 selectedKey = active.name,476484 onSelected = { key ->477485 SortOrder.entries.firstOrNull { it.name == key }?.let(onSort)478486 },487+ modifier = Modifier.weight(1f),479488 )480- Row(481- verticalAlignment = Alignment.CenterVertically,482- horizontalArrangement = Arrangement.spacedBy(PeachSpacing.sm),483- ) {484- PeachSegmentedControl(485- segments = listOf(486- PeachSegment("all", "All"),487- PeachSegment("notes", "Notes"),488- ),489- selectedKey = if (noteOnly) "notes" else "all",490- onSelected = { key ->491- if ((key == "notes") != noteOnly) onToggleNote()492- },493- modifier = Modifier.weight(1f),494- )495- ActionCluster(496- onHealth = onHealth,497- onSelectMode = onSelectMode,498- onTrash = onTrash,499- )500- }489+ ActionCluster(490+ noteOnly = noteOnly,491+ onToggleNote = onToggleNote,492+ onHealth = onHealth,493+ onSelectMode = onSelectMode,494+ onTrash = onTrash,495+ )501496 }502497 }503498 504499 @Composable505500 private fun ActionCluster(501+ noteOnly: Boolean,502+ onToggleNote: () -> Unit,506503 onHealth: () -> Unit,507504 onSelectMode: () -> Unit,508505 onTrash: () -> Unit,509506 ) {507+ val accent = LocalPeachAccent.current510508 Surface(511509 color = peachColors.S1,512510 contentColor = peachColors.T1,@@ -519,6 +517,16 @@519517 horizontalArrangement = Arrangement.spacedBy(PeachSpacing.xs),520518 ) {521519 ClusterIcon(520+ icon = {521+ Icon(522+ Icons.AutoMirrored.Outlined.Notes,523+ contentDescription = "Notes only",524+ tint = if (noteOnly) accent else peachColors.T1,525+ )526+ },527+ onClick = onToggleNote,528+ )529+ ClusterIcon(522530 icon = { Icon(Icons.Outlined.FavoriteBorder, contentDescription = "health") },523531 onClick = onHealth,524532 )@@ -607,7 +615,7 @@607615 )608616 }609617 .clickable(interactionSource = interaction, indication = null, onClick = onTap)610- .padding(horizontal = 16.dp, vertical = 9.dp),618+ .padding(horizontal = 16.dp, vertical = 7.dp),611619 horizontalArrangement = Arrangement.spacedBy(10.dp),612620 verticalAlignment = Alignment.CenterVertically,613621 ) {