c2712fceaa45

fix: Fit receiver pairing states in popup (v4.4.4)

- Replace the QR with the SAS instead of stacking both states - Keep invitation controls inside the 520px viewport - Hold a wider, stronger bird celebration for five seconds

time
2026-07-29 01:18 UTC · 3 months ago
machine
koh2
parent
da96a6703572
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/popup.css+61 −32src/popup/views/AdoptVaultView.test.tsx+6 −1src/popup/views/AdoptVaultView.tsx+25 −17
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "4.4.3",4+  "version": "4.4.4",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.4.3",5+  "version": "4.4.4",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.4.3",3+  "version": "4.4.4",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

src/popup/styles/popup.css+61 −32
@@ -7128,7 +7128,7 @@71287128 }71297129 71307130 .peach-adopt-vault--pair {7131-  padding: 16px;7131+  padding: 10px;71327132   overflow-y: auto;71337133   scrollbar-width: none;71347134 }@@ -7149,9 +7149,12 @@71497149 }71507150 71517151 .peach-adopt-card--pair {7152-  width: min(100%, 348px);7152+  width: 100%;7153+  max-height: 500px;71537154   margin: auto;7154-  padding: 22px 20px 18px;7155+  padding: 14px 16px 12px;7156+  overflow-y: auto;7157+  scrollbar-width: none;71557158   border-color: color-mix(in srgb, var(--peach-accent) 24%, var(--peach-t0) 10%);71567159   border-radius: 16px;71577160   background:@@ -7163,6 +7166,10 @@71637166     color-mix(in srgb, var(--peach-surface) 94%, transparent);71647167 }71657168 7169+.peach-adopt-card--pair::-webkit-scrollbar {7170+  display: none;7171+}7172+71667173 .peach-adopt-card h1 {71677174   margin: 0 0 10px;71687175   font-size: 1.28rem;@@ -7256,11 +7263,11 @@72567263 }72577264 72587265 .peach-adopt-pairing h1 {7259-  max-width: 280px;7260-  margin: 0 auto 8px;7266+  max-width: 260px;7267+  margin: 0 auto 6px;72617268   color: var(--peach-t0);72627269   font-family: var(--font-display, Georgia, serif);7263-  font-size: 1.55rem;7270+  font-size: 1.42rem;72647271   font-style: italic;72657272   font-weight: 500;72667273   line-height: 1.12;@@ -7268,49 +7275,70 @@72687275 }72697276 72707277 .peach-adopt-pairing .peach-adopt-pair-lede {7271-  max-width: 285px;7278+  max-width: 258px;72727279   margin: 0 auto;72737280   color: color-mix(in srgb, var(--peach-t0) 82%, var(--peach-bg));7274-  font-size: 0.78rem;7275-  line-height: 1.5;7281+  font-size: 0.73rem;7282+  line-height: 1.42;72767283   text-wrap: pretty;72777284 }72787285 72797286 .peach-adopt-bird-stage {7280-  width: min(100%, 288px);7281-  height: 126px;7282-  margin: -8px auto 4px;7287+  width: min(100%, 260px);7288+  height: 102px;7289+  margin: -6px auto 2px;72837290 }72847291 72857292 .peach-adopt-bird-stage .peach-pair-bird-slot,72867293 .peach-adopt-bird-stage .peach-pair-bird {7287-  width: 106px;7288-  height: 106px;7294+  width: 90px;7295+  height: 90px;72897296 }72907297 72917298 .peach-adopt-bird-stage .peach-pair-bird-slot-desktop {7292-  left: 16px;7299+  left: 12px;72937300 }72947301 72957302 .peach-adopt-bird-stage .peach-pair-bird-slot-mobile {7296-  right: 16px;7303+  right: 12px;72977304 }72987305 72997306 .peach-pair-stage.peach-adopt-bird-stage.act-paired .peach-pair-bird-slot-desktop {7300-  --bb-x: 28px;7301-  transform: translateX(28px);7307+  --bb-x: 12px;7308+  transform: translateX(12px);73027309 }73037310 73047311 .peach-pair-stage.peach-adopt-bird-stage.act-paired .peach-pair-bird-slot-mobile {7305-  --bb-x: -28px;7306-  transform: translateX(-28px);7312+  --bb-x: -12px;7313+  transform: translateX(-12px);73077314 }73087315 7316+.peach-pair-stage.peach-adopt-bird-stage.act-paired .peach-pair-bird-slot {7317+  animation: peach-adopt-happy-bob 0.8s ease-in-out infinite;7318+}7319+7320+.peach-pair-stage.peach-adopt-bird-stage.act-paired .peach-pair-bird-slot-mobile {7321+  animation-delay: 0.18s;7322+  animation-direction: reverse;7323+}7324+7325+@keyframes peach-adopt-happy-bob {7326+  0%, 100% {7327+    transform: translateX(var(--bb-x, 0)) translateY(0) rotate(0deg);7328+  }7329+  35% {7330+    transform: translateX(var(--bb-x, 0)) translateY(-8px) rotate(-3deg);7331+  }7332+  70% {7333+    transform: translateX(var(--bb-x, 0)) translateY(-3px) rotate(2deg);7334+  }7335+}7336+73097337 .peach-adopt-connection {73107338   position: absolute;73117339   left: 50%;7312-  bottom: 45px;7313-  width: 62px;7340+  bottom: 38px;7341+  width: 78px;73147342   height: 1px;73157343   translate: -50% 0;73167344   background: color-mix(in srgb, var(--peach-accent) 62%, transparent);@@ -7339,7 +7367,7 @@73397367 }73407368 73417369 .peach-adopt-qr-wrap {7342-  min-height: 184px;7370+  min-height: 160px;73437371   display: grid;73447372   place-items: center;73457373 }@@ -7355,7 +7383,8 @@73557383 }73567384 73577385 .peach-adopt-confirmation {7358-  margin-top: 12px;7386+  margin-top: 18px;7387+  padding: 8px 0 4px;73597388 }73607389 73617390 .peach-adopt-confirmation .peach-adopt-confirmation-label {@@ -7369,14 +7398,14 @@73697398 73707399 .peach-adopt-confirmation .peach-adoption-sas {73717400   margin: 0 auto;7372-  padding: 11px 14px;7401+  padding: 12px 14px;73737402   border-color: color-mix(in srgb, var(--peach-accent) 36%, transparent);73747403   background: color-mix(in srgb, var(--peach-accent) 13%, transparent);73757404 }73767405 73777406 .peach-adopt-confirmation .peach-adoption-sas strong {73787407   color: color-mix(in srgb, var(--peach-accent) 84%, var(--peach-t0));7379-  font-size: 1.5rem;7408+  font-size: 1.58rem;73807409   text-shadow: 0 0 16px color-mix(in srgb, var(--peach-accent) 30%, transparent);73817410 }73827411 @@ -7392,8 +7421,8 @@73927421   display: flex;73937422   align-items: flex-start;73947423   gap: 9px;7395-  margin-top: 14px;7396-  padding: 10px 11px;7424+  margin-top: 10px;7425+  padding: 8px 9px;73977426   border: 1px solid color-mix(in srgb, var(--peach-t0) 13%, transparent);73987427   border-radius: 10px;73997428   background: color-mix(in srgb, var(--peach-t0) 6%, transparent);@@ -7415,8 +7444,8 @@74157444 .peach-adopt-pairing .peach-adopt-privacy p {74167445   margin: 0;74177446   color: color-mix(in srgb, var(--peach-t0) 76%, var(--peach-bg));7418-  font-size: 0.655rem;7419-  line-height: 1.45;7447+  font-size: 0.62rem;7448+  line-height: 1.4;74207449 }74217450 74227451 .peach-adopt-privacy strong {@@ -7425,7 +7454,7 @@74257454 }74267455 74277456 .peach-adopt-pair-actions {7428-  margin-top: 12px;7457+  margin-top: 9px;74297458 }74307459 74317460 .peach-adopt-pair-actions > button {@@ -7436,7 +7465,7 @@74367465   display: inline-flex;74377466   align-items: center;74387467   gap: 8px;7439-  margin: 28px auto 22px;7468+  margin: 22px auto 18px;74407469   padding: 9px 13px;74417470   border: 1px solid color-mix(in srgb, var(--peach-accent) 30%, transparent);74427471   border-radius: 999px;

view file

src/popup/views/AdoptVaultView.test.tsx+6 −1
@@ -139,6 +139,8 @@139139 140140     expect(view.container.textContent).toContain("48219371");141141     expect(view.container.textContent).toContain("Confirm this code on your phone");142+    expect(view.container.querySelector(".peach-adoption-sas")).toBeTruthy();143+    expect(view.container.querySelector(".peach-pair-qr")).toBeNull();142144     expect(view.container.textContent).not.toContain("They match");143145     expect(view.container.textContent).not.toContain("They don't match");144146 @@ -191,6 +193,7 @@191193     await enterStrongPassword(view.container);192194     button(view.container, "Continue").click();193195     await flushView();196+    vi.useFakeTimers();194197 195198     for (const listener of listeners) {196199       listener({@@ -208,7 +211,9 @@208211     expect(view.container.textContent).not.toContain("Joined your vault");209212     expect(view.container.textContent).not.toContain("Open Peach");210213 211-    await new Promise((resolve) => window.setTimeout(resolve, 1_050));214+    vi.advanceTimersByTime(4_999);215+    expect(onDone).not.toHaveBeenCalled();216+    vi.advanceTimersByTime(1);212217     expect(onDone).toHaveBeenCalledOnce();213218   });214219 });

view file

src/popup/views/AdoptVaultView.tsx+25 −17
@@ -23,7 +23,7 @@2323 2424 const MIN_LENGTH = 12;2525 const MIN_ZXCVBN_SCORE = 2;26-const PAIRING_CELEBRATION_MS = 1_000;26+const PAIRING_CELEBRATION_MS = 5_000;2727 2828 let zxcvbnLoader: ((password: string) => { score: number }) | null = null;2929 async function getZxcvbn() {@@ -202,7 +202,10 @@202202           <Match when={phase() === "pair"}>203203             <div204204               class="peach-adopt-pairing"205-              classList={{ "is-complete": receiverComplete() }}205+              classList={{206+                "has-sas": Boolean(receiverSas()),207+                "is-complete": receiverComplete(),208+              }}206209             >207210               <div208211                 class={`peach-pair-stage peach-adopt-bird-stage ${@@ -229,7 +232,9 @@229232               <p class="peach-adopt-pair-lede" aria-live="polite">230233                 {receiverComplete()231234                   ? "Your devices are connected. Getting your vault ready…"232-                  : "Scan this QR in Peach on the device that already has your vault."}235+                  : receiverSas()236+                    ? "Make sure this code matches the one on your phone."237+                    : "Scan this QR in Peach on the device that already has your vault."}233238               </p>234239 235240               <Show@@ -241,23 +246,26 @@241246                   </div>242247                 }243248               >244-                <div class="peach-adopt-qr-wrap" aria-live="polite">245-                  <Show246-                    when={receiverInvitation()}247-                    fallback={249+                <Show250+                  when={receiverSas()}251+                  fallback={252+                    <div class="peach-adopt-qr-wrap" aria-live="polite">248253                       <Show249-                        when={receiverError()}250-                        fallback={<p class="peach-adopt-pair-status">Preparing your secure pairing code…</p>}254+                        when={receiverInvitation()}255+                        fallback={256+                          <Show257+                            when={receiverError()}258+                            fallback={<p class="peach-adopt-pair-status">Preparing your secure pairing code…</p>}259+                          >260+                            <p class="peach-hint peach-hint-error">{receiverError()}</p>261+                          </Show>262+                        }251263                       >252-                        <p class="peach-hint peach-hint-error">{receiverError()}</p>264+                        <PairingQrCode value={receiverInvitation()} size={144} />253265                       </Show>254-                    }255-                  >256-                    <PairingQrCode value={receiverInvitation()} size={168} />257-                  </Show>258-                </div>259-260-                <Show when={receiverSas()}>266+                    </div>267+                  }268+                >261269                   <div class="peach-adopt-confirmation">262270                     <p class="peach-adopt-confirmation-label">8-DIGIT SAFETY CODE</p>263271                     <div class="peach-adoption-sas" aria-label="Pairing confirmation code">

view file