aa9cb4d3508c
mirror deck-root EntryDetailScreen top bar to the browser's persistent peach-chrome header: leading PeachMark + peach wordmark title (was a lock icon + entry/domain title), actions reordered to Notes, Settings, New entry, Delete, Lock trailing-last (was Lock leading, Add/Settings/Delete trailing); add PeachNav.VaultNotes route (VaultListScreen with startNoteOnly) as the nearest Android equivalent to the browser's dedicated secure-notes overlay for the new Notes action
- time
- 2026-07-24 17:13 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- mirror entry-deck top bar to browser chrome
- parent
- 501a671f57b5
↓ peach-android-aa9cb4d3.tar.gzatlas at this save
files changed4 files
app/src/main/java/com/peachpasswords/ui/PeachAppNavHost.kt+8 −0app/src/main/java/com/peachpasswords/ui/PeachNav.kt+5 −0app/src/main/java/com/peachpasswords/ui/vault/EntryDetailScreen.kt+42 −22app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+7 −0app/src/main/java/com/peachpasswords/ui/PeachAppNavHost.kt+8 −0
@@ -155,6 +155,14 @@155155 onBack = if (hasOrigin) ({ navController.popBackStack() }) else null,156156 )157157 }158+ composable(PeachNav.VaultNotes.route) {159+ val hasOrigin = navController.previousBackStackEntry != null160+ VaultListScreen(161+ onNavigate = nav,162+ onBack = if (hasOrigin) ({ navController.popBackStack() }) else null,163+ startNoteOnly = true,164+ )165+ }158166 159167 composable(160168 route = PeachNav.EntryDetail.ROUTE,app/src/main/java/com/peachpasswords/ui/PeachNav.kt+5 −0
@@ -10,6 +10,11 @@1010 data object Lock : PeachNav("lock")1111 data object VaultList : PeachNav("vault")1212 13+ /** Same list, pre-filtered to notes — the closest Android equivalent to the14+ * browser's dedicated secure-notes overlay, reached from the entry-deck15+ * chrome's "Notes" action. */16+ data object VaultNotes : PeachNav("vault/notes")17+1318 data class EntryDetail(val id: String) : PeachNav("entry/$id") {1419 companion object {1520 const val ROUTE = "entry/{id}"app/src/main/java/com/peachpasswords/ui/vault/EntryDetailScreen.kt+42 −22
@@ -41,6 +41,7 @@4141 import androidx.compose.material.icons.Icons4242 import androidx.compose.material.icons.automirrored.rounded.ArrowBack4343 import androidx.compose.material.icons.automirrored.rounded.ArrowForward44+import androidx.compose.material.icons.automirrored.outlined.Notes4445 import androidx.compose.material.icons.outlined.DeleteOutline4546 import androidx.compose.material.icons.outlined.Lock4647 import androidx.compose.material3.Icon@@ -78,6 +79,7 @@7879 import com.peachpasswords.ui.components.CharEnterText7980 import com.peachpasswords.ui.components.CopyButton8081 import com.peachpasswords.ui.components.PeachEmptyOrEndState82+import com.peachpasswords.ui.components.PeachMark8183 import com.peachpasswords.ui.components.PeachTopBar8284 import com.peachpasswords.ui.components.SecureFlagEffect8385 import com.peachpasswords.ui.components.TOTPRing@@ -167,6 +169,7 @@167169 },168170 onAddEntry = { onNavigate(PeachNav.EntryEdit(null)) },169171 onSettings = { onNavigate(PeachNav.Settings) },172+ onOpenNotes = { onNavigate(PeachNav.VaultNotes) },170173 )171174 Spacer(Modifier.height(PeachSpacing.lg))172175 @@ -433,12 +436,18 @@433436 onDelete: () -> Unit,434437 onAddEntry: () -> Unit,435438 onSettings: () -> Unit,439+ onOpenNotes: () -> Unit,436440 ) {437441 val domain = remember(entry?.url, entry?.title) { entry?.displayDomain().orEmpty() }438442 PeachTopBar(439443 title = when {444+ confirmingDelete && entry != null -> "delete \"${entry.title}\"?"445+ // Deck root (no back target) mirrors the browser's persistent446+ // header, which always shows the "peach" wordmark — never the447+ // current entry's identity — leaving PeachMark in `leading` below448+ // to complete the wordmark pairing (see LockScreen's same pattern).449+ onBack == null -> "peach"440450 entry == null -> "entry"441- confirmingDelete -> "delete \"${entry.title}\"?"442451 domain.isNotEmpty() -> domain443452 else -> entry.title444453 },@@ -455,21 +464,7 @@455464 .padding(PeachSpacing.sm),456465 )457466 } else {458- // This entry is the post-unlock landing surface, not pushed459- // from the vault list — there's nothing to go "back" to, so460- // the chrome offers the one action that actually makes sense461- // here instead (lock vault list's own affordance). Same 40dp462- // tap target and padding as every other icon in this bar.463- Icon(464- Icons.Outlined.Lock,465- contentDescription = "Lock vault",466- tint = peachColors.T1,467- modifier = Modifier468- .size(40.dp)469- .clip(CircleShape)470- .clickable(onClick = onLock)471- .padding(PeachSpacing.sm),472- )467+ PeachMark(modifier = Modifier.padding(start = PeachSpacing.sm, end = PeachSpacing.xs))473468 }474469 // The favicon chip used to sit here, but it's the one loud,475470 // colorful element in an otherwise quiet/muted bar (matching the@@ -480,18 +475,21 @@480475 actions = {481476 // This entry is the deck root (no back target) — it's the closest482477 // thing this app has to a "home" screen, so it's where the483- // persistent add/settings actions live now. They used to sit in a484- // bar on the vault list, but that's a sheet reached by sliding up485- // from here (C1), not a screen of its own anymore.478+ // persistent chrome actions live now, mirroring the browser's479+ // peach-chrome header exactly: Notes, Settings, New entry, then480+ // Lock last/trailing (peach-browser/src/popup/App.tsx ~1347-1408).481+ // Delete has no browser-chrome equivalent (it lives in the entry's482+ // own view there) — kept here, grouped with the other483+ // content-mutating actions just before the terminal Lock action.486484 if (onBack == null) {487485 Icon(488- Icons.Rounded.Add,489- contentDescription = "new entry",486+ Icons.AutoMirrored.Outlined.Notes,487+ contentDescription = "Notes",490488 tint = peachColors.T1,491489 modifier = Modifier492490 .size(40.dp)493491 .clip(CircleShape)494- .clickable(onClick = onAddEntry)492+ .clickable(onClick = onOpenNotes)495493 .padding(PeachSpacing.sm),496494 )497495 Icon(@@ -504,6 +502,16 @@504502 .clickable(onClick = onSettings)505503 .padding(PeachSpacing.sm),506504 )505+ Icon(506+ Icons.Rounded.Add,507+ contentDescription = "new entry",508+ tint = peachColors.T1,509+ modifier = Modifier510+ .size(40.dp)511+ .clip(CircleShape)512+ .clickable(onClick = onAddEntry)513+ .padding(PeachSpacing.sm),514+ )507515 }508516 if (entry != null) {509517 Icon(@@ -514,6 +522,18 @@514522 .size(40.dp)515523 .clip(CircleShape)516524 .clickable(onClick = onDelete)525+ .padding(PeachSpacing.sm),526+ )527+ }528+ if (onBack == null) {529+ Icon(530+ Icons.Outlined.Lock,531+ contentDescription = "Lock vault",532+ tint = peachColors.T1,533+ modifier = Modifier534+ .size(40.dp)535+ .clip(CircleShape)536+ .clickable(onClick = onLock)517537 .padding(PeachSpacing.sm),518538 )519539 }app/src/main/java/com/peachpasswords/ui/vault/VaultListScreen.kt+7 −0
@@ -114,8 +114,15 @@114114 // search strip — slide back down to collapse to that entry. Null when115115 // the list itself is the deck root (e.g. empty vault, nothing to open).116116 onBack: (() -> Unit)? = null,117+ // True when reached via the entry-deck chrome's "Notes" action — pre-applies118+ // the notes filter pill so the list opens already scoped to notes, the119+ // closest equivalent to the browser's dedicated secure-notes overlay.120+ startNoteOnly: Boolean = false,117121 vm: VaultListViewModel = hiltViewModel(),118122 ) {123+ LaunchedEffect(Unit) {124+ if (startNoteOnly && !vm.noteOnly.value) vm.toggleNoteFilter()125+ }119126 val entries by vm.entries.collectAsState()120127 val sort by vm.sort.collectAsState()121128 val search by vm.search.collectAsState()