c99b10bae9bd

refactor: align entry edit option surfaces (v2.14.56)

time
2026-08-15 00:43 UTC · 2 months ago
machine
koh2
parent
10b0aee191b8
4 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 −17
VERSION+1 −1
@@ -1 +1 @@1-2.14.551+2.14.56

view file

app/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 }

view file

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 /**

view file

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) }

view file