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
↓ peach-browser-cbfaea58.tar.gzatlas at this save
files changed6 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 −0manifest.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": {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",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",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;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 />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", () => {