f38052979d35

polish Sync layout and migration notices (v5.2.50)

time
2026-09-12 00:12 UTC · 1 month ago
machine
koh2
agent
camel-stream/auto
session
sync screen polish
parent
8e6d7effbebe
4 files src/popup/views/Settings.tsx+30 −34src/popup/views/contracts.test.tsx+5 −0src/shared/release-notes.test.ts+12 −9src/shared/release-notes.ts+2 −2
src/popup/views/Settings.tsx+30 −34
@@ -255,7 +255,7 @@255255       id: "sync",256256       num: "IV · ECHO",257257       title: "Sync",258-      lede: isPro() ? "your devices, one vault" : "Pro · encrypted device sync",258+      lede: isPro() ? "End to End Encryption" : "Pro · encrypted device sync",259259       bgVar: "--stack-sync-bg",260260       fgVar: "--stack-sync-fg",261261       icon: "sync",@@ -1738,22 +1738,18 @@17381738             <span class="peach-settings-value" style="color:var(--peach-t2);line-height:1.5;flex:1;min-width:0;overflow-wrap:anywhere">17391739               {syncFlash() ?? lastSyncLine()}17401740             </span>1741-          </div>1742-          <Show when={pairedDevices().length > 0}>1743-            <div class="peach-settings-row peach-settings-row--meta">1744-              <span class="peach-settings-value" style="color:var(--peach-t2)">1745-                {(syncSummary().entriesSent ?? 0) + (syncSummary().entriesReceived ?? 0) > 01746-                  ? `${syncSummary().entriesSent ?? 0} sent · ${syncSummary().entriesReceived ?? 0} received`1747-                  : ""}1748-              </span>1741+            {/* The transfer counts already ride in the status line, so this row1742+                was an empty flex sibling in every other case: it read as a dead1743+                gap and held the button away from the status it belongs to. */}1744+            <Show when={pairedDevices().length > 0}>17491745               <button17501746                 class="peach-btn peach-btn--subtle peach-btn--sm"17511747                 aria-label="Sync this vault now"17521748                 disabled={syncStatus() === "syncing"}17531749                 onClick={() => void syncNow()}17541750               >{syncStatus() === "syncing" ? "syncing…" : "sync now"}</button>1755-            </div>1756-          </Show>1751+            </Show>1752+          </div>17571753 17581754           {/* §11: this panel is a migration call to action, not a permanent17591755               label. On the settled new generation there is nothing left to do@@ -1822,29 +1818,6 @@18221818             </Show>18231819           </Show>18241820 1825-          <p class="peach-settings-sub-label">ADD A DEVICE</p>1826-          <button1827-            class="peach-sync-pair-option"1828-            onClick={() => props.onOpenSyncPairing?.("code")}1829-          >1830-            <span class="peach-sync-pair-option-icon" aria-hidden="true">1831-              <svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">1832-                <rect x="6" y="2.5" width="8" height="15" rx="1.6" />1833-                <path d="M9 15 L11 15" />1834-              </svg>1835-            </span>1836-            <span class="peach-sync-pair-option-main">1837-              <span class="peach-sync-pair-option-title">Show a code</span>1838-              <span class="peach-sync-pair-option-sub">Scan it with Peach on your phone, then confirm one 8-digit code</span>1839-            </span>1840-            <span class="peach-sync-pair-option-chevron" aria-hidden="true">›</span>1841-          </button>1842-          <Show when={!isPro()}>1843-            <p class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2);margin:8px 2px 0">1844-              At least one device needs Peach Pro — pair with a Pro device and it carries over to here.1845-            </p>1846-          </Show>1847-18481821           <Show when={pairedDevices().length > 0}>18491822             <p class="peach-settings-sub-label">PAIRED DEVICES</p>18501823             <For each={pairedDevices()}>@@ -1965,6 +1938,29 @@19651938                 </div>19661939               )}19671940             </For>1941+          </Show>1942+1943+          <p class="peach-settings-sub-label">ADD A DEVICE</p>1944+          <button1945+            class="peach-sync-pair-option"1946+            onClick={() => props.onOpenSyncPairing?.("code")}1947+          >1948+            <span class="peach-sync-pair-option-icon" aria-hidden="true">1949+              <svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">1950+                <rect x="6" y="2.5" width="8" height="15" rx="1.6" />1951+                <path d="M9 15 L11 15" />1952+              </svg>1953+            </span>1954+            <span class="peach-sync-pair-option-main">1955+              <span class="peach-sync-pair-option-title">Show a code</span>1956+              <span class="peach-sync-pair-option-sub">Scan it with Peach on your phone, then confirm one 8-digit code</span>1957+            </span>1958+            <span class="peach-sync-pair-option-chevron" aria-hidden="true">›</span>1959+          </button>1960+          <Show when={!isPro()}>1961+            <p class="peach-settings-value" style="font-size:11px;line-height:1.5;color:var(--peach-t2);margin:8px 2px 0">1962+              At least one device needs Peach Pro — pair with a Pro device and it carries over to here.1963+            </p>19681964           </Show>19691965 19701966           <button

view file

src/popup/views/contracts.test.tsx+5 −0
@@ -811,6 +811,11 @@811811     const syncNowButton = container.querySelector<HTMLButtonElement>(812812       'button[aria-label="Sync this vault now"]',813813     )!;814+    const syncStatusRow = syncNowButton.closest(".peach-settings-row--meta");815+    const syncCard = syncNowButton.closest('[data-settings-card-id="sync"]');816+    expect(syncStatusRow).not.toBeNull();817+    expect(syncStatusRow?.contains(syncNowButton)).toBe(true);818+    expect(syncCard?.querySelectorAll(".peach-settings-row--meta")).toHaveLength(1);814819     const labels = Array.from(container.querySelectorAll(".peach-settings-sub-label"));815820     const addDeviceLabel = labels.find((label) => label.textContent?.includes("ADD A DEVICE"))!;816821     const pairedDevicesLabel = labels.find((label) => label.textContent?.includes("PAIRED DEVICES"))!;

view file

src/shared/release-notes.test.ts+12 −9
@@ -43,27 +43,30 @@4343     expect(PEACH_VERSION.length).toBeGreaterThan(0);4444   });4545 46-  it("keeps sync recovery as its own breaking release", () => {47-    const syncRecovery = RELEASES.find((release) =>48-      release.notes.some((note) => {49-        const body = note.body.toLowerCase();50-        return body.includes("revoke") && body.includes("pair");51-      })52-    );46+  it("keeps the major Sync security repair notice explicit", () => {47+    const syncRecovery = RELEASES.find((release) => release.version === "5.2.46");48+    const body = syncRecovery?.notes[0]?.body.toLowerCase() ?? "";5349     expect(syncRecovery?.importance).toBe("breaking");54-    expect(syncRecovery?.version).toBe("5.2.17");50+    expect(body).toContain("update peach on every device");51+    expect(body).toContain("open sync settings");52+    expect(body).toContain("master password");53+    expect(body).toContain("reset sync");54+    expect(body).toContain("pair or re-pair every device");55+    expect(body).toContain("revocation");56+    expect(body).toContain("vault and recovery words are unchanged");57+    expect(body).toContain("sorry");5558 5659     const vaultV4 = RELEASES.find((release) => release.version === "5.1.3");5760     expect(vaultV4?.importance).toBe("breaking");5861     expect(vaultV4?.notes.some((note) => note.body.toLowerCase().includes("revoke"))).toBe(false);5962   });60-6163   it("shows the sync recovery releases for upgrades from 5.2.15", () => {6264     expect(importantReleasesSince("5.2.15").map((release) => release.version)).toEqual(["5.2.46", "5.2.17"]);6365   });6466   it("combines every important release newer than the installed version", () => {6567     expect(importantReleasesSince("5.0.0").map((release) => release.version)).toEqual(["5.2.46", "5.2.17", "5.1.3"]);6668   });69+6770 6871   it("hasReleaseNotes returns true when notes exist", () => {6972     expect(hasReleaseNotes()).toBe(true);

view file

src/shared/release-notes.ts+2 −2
@@ -41,8 +41,8 @@4141     releasedAt: "2026-09-11",4242     importance: "breaking",4343     notes: [{44-      heading: "Sync needs one deliberate repair",45-      body: "This security update starts a new Sync generation. Update Peach on every other device, reset Sync here, then pair each device again. Your vault and recovery words are unchanged; old device trust is archived, not silently reused.",44+      heading: "A major Sync security update",45+      body: "We're sorry for the extra step. This major security update makes Sync and device Revocation meaningfully more secure, but it requires a deliberate repair: 1) update Peach on every device; 2) open Sync settings on the device showing the reset notice; 3) enter your master password and complete Reset Sync; 4) pair or re-pair every device individually. Old device trust will not continue automatically. Your vault and recovery words are unchanged.",4646     }],4747   },4848   { version: "5.2.17", releasedAt: "2026-09-07", importance: "breaking", notes: [{ heading: "Sync may need device repair", body: "If sync starts failing after this security update, revoke the affected devices and pair them again. A recent security change may require repairing device trust before sync can resume." }] },

view file