cbfaea58dd97

condense recovery-words screen to fit 520px with no scroll and fix unlock-after-lockout stranding on the lock screen when recovery confirmation is pending, bump 4.10.17

time
2026-08-20 17:33 UTC · 2 months ago
machine
koh2
agent
qwen3.8-max
session
peach ui fixes
parent
7009878b61cc
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/popup.css+15 −14src/popup/views/Lock.tsx+9 −1tests/lock-screen.spec.ts+31 −0
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "4.10.16",4+  "version": "4.10.17",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -2,7 +2,7 @@22   "manifest_version": 3,33   "minimum_chrome_version": "116",44   "name": "Peach Password Manager",5-  "version": "4.10.16",5+  "version": "4.10.17",66   "description": "Zero-knowledge password manager",77   "action": {88     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "4.10.16",3+  "version": "4.10.17",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

src/popup/styles/popup.css+15 −14
@@ -8420,8 +8420,8 @@84208420 84218421 .peach-recovery-header {84228422   text-align: center;8423-  margin-bottom: 16px;8424-  margin-top: 12px;8423+  margin-bottom: 0;8424+  margin-top: 2px;84258425 }84268426 .peach-recovery-header .peach-onboarding-headline {84278427   font-size: 1.25rem;@@ -8503,24 +8503,25 @@85038503   gap: 4px;85048504 }85058505 8506-/* ── Recovery words display (post-create) ────────────────────────── */8506+/* ── Recovery words display (post-create) ──────────────────────────8507+   Condensed so word list + confirmation fit the 520px popup with no8508+   scroll — the confirm button must never be clipped. */85078509 .lock-screen--recovery-display {85088510   display: flex;85098511   flex-direction: column;85108512   align-items: stretch;8511-  /* Same top/left padding as the onboarding screens so the wordmark sits8512-     on the same pixel instead of crowding the popup edge. */8513-  padding: 16px 16px 16px;8513+  padding: 10px 16px 12px;8514+  gap: 8px;85148515   min-height: 0;85158516   overflow-y: auto;85168517 }85178518 85188519 .peach-recovery-words-display {85198520   width: 100%;8520-  margin-bottom: 16px;8521+  margin-bottom: 0;85218522   border: 1px solid var(--peach-t3);85228523   border-radius: 8px;8523-  padding: 12px 14px;8524+  padding: 10px 14px;85248525   background: rgba(245, 240, 234, 0.04);85258526 }85268527 @@ -8530,7 +8531,7 @@85308531   letter-spacing: 0.08em;85318532   color: var(--peach-t2);85328533   text-transform: uppercase;8533-  margin-bottom: 10px;8534+  margin-bottom: 8px;85348535 }85358536 85368537 .peach-recovery-word-list {@@ -8539,7 +8540,7 @@85398540   padding: 0;85408541   display: grid;85418542   grid-template-columns: repeat(2, 1fr);8542-  gap: 6px 16px;8543+  gap: 5px 16px;85438544 }85448545 85458546 .peach-recovery-word-item {@@ -8561,7 +8562,7 @@85618562 85628563 .peach-recovery-download-btn {85638564   display: block;8564-  margin: 10px auto 0;8565+  margin: 8px auto 0;85658566   padding: 4px 10px;85668567   background: transparent;85678568   border: 1px solid var(--peach-border);@@ -8612,13 +8613,13 @@86128613   letter-spacing: 0.04em;86138614   color: var(--peach-t2);86148615   text-align: center;8615-  margin: 0 0 12px;8616+  margin: 0 0 8px;86168617 }86178618 .peach-recovery-confirm-fields {86188619   display: flex;86198620   flex-direction: column;8620-  gap: 14px;8621-  margin-bottom: 10px;8621+  gap: 10px;8622+  margin-bottom: 8px;86228623 }86238624 .peach-recovery-confirm-field {86248625   display: flex;

view file

src/popup/views/Lock.tsx+9 −1
@@ -316,7 +316,15 @@316316       </Match>317317 318318       {/* ── Recovery words display after fresh vault creation ────────── */}319-      <Match when={showRecoveryWords() && (!props.hasVault || props.forceRecoveryWords)}>319+      {/* Read props.forceRecoveryWords directly, not just the boot-time320+          signal copy: when the vault is locked with a pending confirmation,321+          App flips the flag after UNLOCK without remounting LockView. */}322+      <Match323+        when={324+          (showRecoveryWords() || props.forceRecoveryWords) &&325+          (!props.hasVault || props.forceRecoveryWords)326+        }327+      >320328         <RecoveryWordDisplayView321329           onDone={props.onUnlock}322330         />

view file

tests/lock-screen.spec.ts+31 −0
@@ -312,6 +312,37 @@312312     await expect(popup.locator("#master-password")).toHaveCount(0);313313     await ctx.close();314314   });315+316+  test("LS24 - locked out before confirming recovery words: unlock routes back to the confirmation", async () => {317+    const { popup } = await setupVault(ctx, VAULT_PASSWORD, { confirmRecovery: false });318+    await popup.waitForSelector(".lock-screen--recovery-display", { timeout: 15000 });319+    // Lock, then click out before confirming — the vault must stay recoverable.320+    await popup.evaluate(() => chrome.runtime.sendMessage({ type: "LOCK" }));321+    await popup.waitForTimeout(300);322+    await popup.close();323+324+    const { popup: reopened } = await loadPopup(ctx);325+    await reopened.waitForSelector("#unlock-password", { timeout: 15000 });326+    await reopened.locator("#unlock-password").fill(VAULT_PASSWORD);327+    await reopened.locator("#unlock-password").press("Enter");328+329+    // Unlock must re-present the pending confirmation, not strand on the lock screen.330+    await expect(reopened.locator(".lock-screen--recovery-display")).toBeVisible({ timeout: 15000 });331+    await expect(reopened.locator("#unlock-password")).toHaveCount(0);332+333+    await reopened.waitForSelector(".peach-recovery-confirm-input:not([disabled])", { timeout: 20000 });334+    const words = await reopened.locator(".peach-recovery-word-text").allTextContents();335+    const fields = reopened.locator(".peach-recovery-confirm-field");336+    const count = await fields.count();337+    for (let i = 0; i < count; i++) {338+      const field = fields.nth(i);339+      const pos = parseInt((await field.locator(".peach-recovery-confirm-num").innerText()).replace(/\D/g, ""), 10);340+      await field.locator(".peach-recovery-confirm-input").fill(words[pos - 1]!.trim());341+    }342+    await reopened.getByRole("button", { name: /i wrote them down/i }).click();343+    await expect(reopened.locator(UNLOCKED)).toBeVisible({ timeout: 15000 });344+    await ctx.close();345+  });315346 });316347 317348 test.describe("Lock Screen - Auto-lock Configuration", () => {

view file