c99b10bae9bd
refactor: align entry edit option surfaces (v2.14.56)
- time
- 2026-08-15 00:43 UTC · 2 months ago
- machine
- koh2
- parent
- 10b0aee191b8
↓ peach-android-c99b10ba.tar.gzatlas at this save
files changed4 files
VERSION+1 −1app/src/main/java/com/peachpasswords/ui/vault/AdditionalUrlsEditor.kt+36 −63app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+54 −38app/src/main/java/com/peachpasswords/ui/vault/PasswordHistorySection.kt+24 −17app/src/main/java/com/peachpasswords/ui/vault/AdditionalUrlsEditor.kt+36 −63
@@ -1,9 +1,7 @@11 package com.peachpasswords.ui.vault22 3-import androidx.compose.foundation.background4-import androidx.compose.foundation.border53 import androidx.compose.foundation.clickable6-import androidx.compose.foundation.layout.Arrangement4+import androidx.compose.foundation.background75 import androidx.compose.foundation.layout.Box86 import androidx.compose.foundation.layout.Column97 import androidx.compose.foundation.layout.Row@@ -11,65 +9,49 @@119 import androidx.compose.foundation.layout.fillMaxWidth1210 import androidx.compose.foundation.layout.height1311 import androidx.compose.foundation.layout.padding14-import androidx.compose.foundation.layout.width15-import androidx.compose.foundation.shape.RoundedCornerShape1612 import androidx.compose.foundation.text.BasicTextField13+import androidx.compose.foundation.shape.RoundedCornerShape1714 import androidx.compose.material3.Text1815 import androidx.compose.runtime.Composable1916 import androidx.compose.ui.Alignment2017 import androidx.compose.ui.Modifier2118 import androidx.compose.ui.draw.clip22-import androidx.compose.ui.graphics.Color2319 import androidx.compose.ui.graphics.SolidColor2420 import androidx.compose.ui.text.TextStyle2521 import androidx.compose.ui.unit.dp2622 import androidx.compose.ui.unit.sp2723 import com.peachpasswords.ui.theme.LocalPeachAccent28-import com.peachpasswords.ui.theme.peachColors29-import com.peachpasswords.ui.theme.PeachType3024 import com.peachpasswords.ui.theme.PeachSpacing25+import com.peachpasswords.ui.theme.PeachType26+import com.peachpasswords.ui.theme.peachColors3127 32-/** E3 — hard cap on additional URLs, matching the spec. */28+/** Hard cap on additional URLs, matching the browser editor. */3329 const val MAX_ADDITIONAL_URLS = 53430 35-/**36- * E3 — editor block for the additional-URLs list. Self-contained (mirrors the37- * underline `Field` style in EntryEditScreen) so it drops into the login branch38- * below the main `url` field without depending on the screen's private `Field`.39- *40- * Each row is an underlined input labeled "URL N" with an "×" remove control.41- * A ghost "+ add URL" affordance appends an empty entry; it disappears at the42- * cap and is replaced by the limit hint.43- */31+/** Flush continuation rows for URLs beyond the primary URL field. */4432 @Composable4533 fun AdditionalUrlsEditor(4634 urls: List<String>,4735 onChange: (Int, String) -> Unit,4836 onAdd: () -> Unit,4937 onRemove: (Int) -> Unit,50- embedded: Boolean = false,5138 ) {5239 val accent = LocalPeachAccent.current53- Column(54- Modifier.fillMaxWidth().padding(horizontal = if (embedded) PeachSpacing.lg else 0.dp),55- ) {40+ Column(Modifier.fillMaxWidth()) {5641 urls.forEachIndexed { index, value ->57- // Label numbering starts at 2 so it reads as a continuation of the58- // primary URL field (which is conceptually "URL 1").59- Column(Modifier.fillMaxWidth().padding(bottom = 14.dp)) {60- Text(61- "URL ${index + 2}",62- color = peachColors.T1,63- fontFamily = PeachType.Mono,64- fontSize = 8.sp,65- )66- Spacer(Modifier.height(6.dp))67- Row(verticalAlignment = Alignment.CenterVertically) {68- Box(69- Modifier.weight(1f)70- .border(1.dp, peachColors.T3, RoundedCornerShape(10.dp))71- .padding(horizontal = 12.dp, vertical = 12.dp),72- ) {42+ Column(Modifier.fillMaxWidth()) {43+ Row(44+ Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.sm),45+ verticalAlignment = Alignment.CenterVertically,46+ ) {47+ Column(Modifier.weight(1f)) {48+ Text(49+ "URL ${index + 2}",50+ color = peachColors.T1,51+ fontFamily = PeachType.Mono,52+ fontSize = 8.sp,53+ )54+ Spacer(Modifier.height(4.dp))7355 BasicTextField(7456 value = value,7557 onValueChange = { onChange(index, it) },@@ -80,10 +62,9 @@8062 fontSize = 13.sp,8163 ),8264 cursorBrush = SolidColor(accent),83- modifier = Modifier.fillMaxWidth(),65+ modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp),8466 )8567 }86- Spacer(Modifier.width(8.dp))8768 Text(8869 "×",8970 color = peachColors.T2,@@ -92,32 +73,24 @@9273 modifier = Modifier9374 .clip(RoundedCornerShape(6.dp))9475 .clickable { onRemove(index) }95- .padding(horizontal = 8.dp, vertical = 4.dp),76+ .padding(start = PeachSpacing.sm, end = 2.dp, top = 6.dp, bottom = 6.dp),9677 )9778 }79+ if (index < urls.lastIndex) {80+ Box(Modifier.fillMaxWidth().height(1.dp).background(peachColors.T3))81+ }9882 }9983 }100-101- if (urls.size < MAX_ADDITIONAL_URLS) {102- Text(103- "+ add URL",104- color = accent,105- fontFamily = PeachType.Mono,106- fontSize = 11.sp,107- modifier = Modifier108- .clip(RoundedCornerShape(6.dp))109- .clickable(onClick = onAdd)110- .padding(vertical = 4.dp),111- )112- Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))113- } else {114- Text(115- "Max $MAX_ADDITIONAL_URLS additional URLs",116- color = peachColors.T2,117- fontFamily = PeachType.Mono,118- fontSize = 9.sp,119- )120- Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))121- }84+ Box(Modifier.fillMaxWidth().height(1.dp).background(peachColors.T3))85+ Text(86+ if (urls.size < MAX_ADDITIONAL_URLS) "+ add URL" else "Max $MAX_ADDITIONAL_URLS additional URLs",87+ color = if (urls.size < MAX_ADDITIONAL_URLS) accent else peachColors.T2,88+ fontFamily = PeachType.Mono,89+ fontSize = 11.sp,90+ modifier = Modifier91+ .clip(RoundedCornerShape(6.dp))92+ .clickable(enabled = urls.size < MAX_ADDITIONAL_URLS, onClick = onAdd)93+ .padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.sm),94+ )12295 }12396 }app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+54 −38
@@ -118,14 +118,24 @@118118 Field("title", f.title, vm::setTitle)119119 if (f.type == "login") {120120 PeachFieldStack {121- Field("url", f.url, vm::setUrl, joined = true, divider = true)122- AdditionalUrlsEditor(123- urls = f.additionalUrls,124- onChange = vm::setAdditionalUrl,125- onAdd = vm::addAdditionalUrl,126- onRemove = vm::removeAdditionalUrl,127- embedded = true,121+ Field(122+ "url", f.url, vm::setUrl,123+ joined = true,124+ divider = true,125+ trailing = if (f.additionalUrls.isEmpty()) {126+ { AddFieldLink("+ add", vm::addAdditionalUrl) }127+ } else {128+ null129+ },128130 )131+ if (f.additionalUrls.isNotEmpty()) {132+ AdditionalUrlsEditor(133+ urls = f.additionalUrls,134+ onChange = vm::setAdditionalUrl,135+ onAdd = vm::addAdditionalUrl,136+ onRemove = vm::removeAdditionalUrl,137+ )138+ }129139 IdentitySuggestField(130140 "username", f.username, usernameSuggestions, vm::setUsername,131141 joined = true,@@ -144,31 +154,28 @@144154 onToggleShow = vm::toggleShowPassword,145155 onToggleGenerator = vm::toggleGenerator,146156 joined = true,147- divider = true,148157 )149- GeneratorDrawer(150- open = f.generatorOpen,151- length = f.genLength,152- upper = f.genUpper, lower = f.genLower, numbers = f.genNumbers, symbols = f.genSymbols,153- password = f.password,154- copied = f.genCopied,155- onLength = vm::setGenLength,156- onUpper = vm::setGenUpper, onLower = vm::setGenLower,157- onNumbers = vm::setGenNumbers, onSymbols = vm::setGenSymbols,158- onGenerate = vm::generate,159- onCopy = vm::copyGenerated,160- onUse = vm::useGeneratedPassword,161- embedded = true,162- )163- PasswordHistorySection(164- history = f.history,165- expanded = f.showHistory,166- onToggle = vm::toggleHistory,167- onRestore = vm::restorePassword,168- onCopy = vm::copyHistoryPassword,169- embedded = true,170- )171158 }159+ GeneratorDrawer(160+ open = f.generatorOpen,161+ length = f.genLength,162+ upper = f.genUpper, lower = f.genLower, numbers = f.genNumbers, symbols = f.genSymbols,163+ password = f.password,164+ copied = f.genCopied,165+ onLength = vm::setGenLength,166+ onUpper = vm::setGenUpper, onLower = vm::setGenLower,167+ onNumbers = vm::setGenNumbers, onSymbols = vm::setGenSymbols,168+ onGenerate = vm::generate,169+ onCopy = vm::copyGenerated,170+ onUse = vm::useGeneratedPassword,171+ )172+ PasswordHistorySection(173+ history = f.history,174+ expanded = f.showHistory,175+ onToggle = vm::toggleHistory,176+ onRestore = vm::restorePassword,177+ onCopy = vm::copyHistoryPassword,178+ )172179 OptionalLoginAddLinks(173180 showTotpLink = shouldShowAddTotpLink(f.type, f.showTotp, f.totp),174181 showNotesLink = shouldShowLoginNotesLink(@@ -321,12 +328,22 @@321328 onAddNotes: () -> Unit,322329 ) {323330 if (!showTotpLink && !showNotesLink) return324- Row(325- Modifier.fillMaxWidth().padding(top = 4.dp, bottom = 8.dp),326- horizontalArrangement = Arrangement.spacedBy(14.dp),327- ) {328- if (showTotpLink) AddFieldLink("+ add 2FA", onAddTotp)329- if (showNotesLink) AddFieldLink("+ add notes", onAddNotes)331+ PeachFieldStack {332+ Text(333+ "OPTIONAL FIELDS",334+ color = peachColors.T2,335+ fontFamily = PeachType.Mono,336+ fontSize = 8.sp,337+ letterSpacing = 0.08.em,338+ modifier = Modifier.padding(start = PeachSpacing.lg, top = PeachSpacing.md),339+ )340+ Row(341+ Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.sm),342+ horizontalArrangement = Arrangement.spacedBy(14.dp),343+ ) {344+ if (showTotpLink) AddFieldLink("+ add 2FA", onAddTotp)345+ if (showNotesLink) AddFieldLink("+ add notes", onAddNotes)346+ }330347 }331348 }332349 @@ -423,7 +440,6 @@423440 onGenerate: () -> Unit,424441 onCopy: () -> Unit,425442 onUse: () -> Unit,426- embedded: Boolean = false,427443 ) {428444 val height by animateDpAsState(429445 targetValue = if (open) 326.dp else 0.dp,@@ -447,7 +463,7 @@447463 onUse = onUse,448464 )449465 }450- if (open) Spacer(Modifier.height(if (embedded) 8.dp else 14.dp))466+ if (open) Spacer(Modifier.height(14.dp))451467 }452468 453469 /**app/src/main/java/com/peachpasswords/ui/vault/PasswordHistorySection.kt+24 −17
@@ -26,6 +26,7 @@2626 import androidx.compose.ui.unit.dp2727 import androidx.compose.ui.unit.sp2828 import com.peachpasswords.ui.theme.LocalPeachAccent29+import com.peachpasswords.ui.theme.PeachSpacing2930 import com.peachpasswords.ui.theme.peachColors3031 import com.peachpasswords.ui.theme.PeachType3132 import com.peachpasswords.vault.PasswordHistoryItem@@ -50,13 +51,21 @@5051 onToggle: () -> Unit,5152 onRestore: (String) -> Unit,5253 onCopy: (String) -> Unit,53- embedded: Boolean = false,5454 ) {55- Column(Modifier.fillMaxWidth().padding(bottom = if (embedded) 8.dp else 14.dp)) {56- PasswordHistoryToggle(count = history.size, expanded = expanded, onToggle = onToggle)57- if (expanded) {58- Spacer(Modifier.height(8.dp))59- PasswordHistoryList(history = history, onRestore = onRestore, onCopy = onCopy)55+ Column(Modifier.fillMaxWidth().padding(bottom = PeachSpacing.sm)) {56+ PeachFieldStack {57+ PasswordHistoryToggle(58+ count = history.size,59+ expanded = expanded,60+ onToggle = onToggle,61+ modifier = Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.sm),62+ )63+ if (expanded) {64+ Box(Modifier.fillMaxWidth().height(1.dp).background(peachColors.T3))65+ Column(Modifier.fillMaxWidth().padding(horizontal = PeachSpacing.lg, vertical = PeachSpacing.md)) {66+ PasswordHistoryList(history = history, onRestore = onRestore, onCopy = onCopy)67+ }68+ }6069 }6170 }6271 }@@ -70,6 +79,7 @@7079 count: Int,7180 expanded: Boolean,7281 onToggle: () -> Unit,82+ modifier: Modifier = Modifier,7383 ) {7484 val accent = LocalPeachAccent.current7585 val label = if (count == 0) "↺ history" else "↺ history ($count)"@@ -79,7 +89,7 @@7989 fontFamily = PeachType.Display,8090 fontStyle = FontStyle.Italic,8191 fontSize = 15.sp,82- modifier = Modifier.clip(RoundedCornerShape(6.dp)).clickable(onClick = onToggle).padding(vertical = 4.dp),92+ modifier = modifier.clip(RoundedCornerShape(6.dp)).clickable(onClick = onToggle).padding(vertical = 4.dp),8393 )8494 }8595 @@ -95,16 +105,13 @@95105 ) {96106 val accent = LocalPeachAccent.current97107 if (history.isEmpty()) {98- Box(99- Modifier.fillMaxWidth().clip(RoundedCornerShape(8.dp))100- .border(1.dp, accent.copy(alpha = 0.12f), RoundedCornerShape(8.dp))101- .background(accent.copy(alpha = 0.05f)).padding(10.dp),102- ) {103- Text(104- "No earlier passwords yet. When you change this password, the old one is saved here so you can copy or restore it.",105- color = peachColors.T2, fontFamily = PeachType.Mono, fontSize = 10.sp,106- )107- }108+ Text(109+ "No earlier passwords yet. When you change this password, the old one is saved here so you can copy or restore it.",110+ color = peachColors.T2,111+ fontFamily = PeachType.Mono,112+ fontSize = 10.sp,113+ modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),114+ )108115 } else {109116 Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {110117 history.forEach { item -> HistoryRow(item, onRestore, onCopy) }