9edde75430a8

sync settings: remove ✦ badge from nav, add network note, promote Add a device row, flip Show to when={isPro()}, fix App.tsx v=view() used before declaration (v2.35.8)

time
2026-05-09 01:49 UTC · 5 months ago
machine
koh2
parent
b05386777208
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/App.tsx+1 −1src/popup/styles/popup.css+20 −0src/popup/views/Settings.tsx+153 −156
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.35.7",4+  "version": "2.35.8",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.35.7",4+  "version": "2.35.8",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "2.35.7",3+  "version": "2.35.8",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/popup/App.tsx+1 −1
@@ -577,6 +577,7 @@577577         const wasCaptured = captured;578578         dragging = false;579579         captured = false;580+        const v = view();580581 581582         if (!wasCaptured) {582583           // Tap in the peek strip while in entry view → open the vault list.@@ -587,7 +588,6 @@587588 588589         const restClosed = window.innerHeight - PEEK_PX;589590         const current = sheetTranslate() ?? restClosed;590-        const v = view();591591 592592         // Snap rules:593593         //   - Strong upward velocity → open

view file

src/popup/styles/popup.css+20 −0
@@ -4028,6 +4028,26 @@40284028   letter-spacing: 0.05em;40294029 }40304030 4031+.peach-sync-network-note {4032+  margin: 0 0 6px;4033+  font-size: 0.75rem;4034+  color: var(--peach-t2);4035+  line-height: 1.4;4036+}4037+4038+.peach-settings-row--tappable {4039+  background: none;4040+  border: none;4041+  width: 100%;4042+  text-align: left;4043+  cursor: pointer;4044+  color: var(--peach-t1);4045+  font-size: 0.8125rem;4046+}4047+.peach-settings-row--tappable:hover {4048+  color: var(--peach-t0);4049+}4050+40314051 .peach-sync-pro-hint {40324052   display: flex;40334053   flex-direction: column;

view file

src/popup/views/Settings.tsx+153 −156
@@ -769,7 +769,7 @@769769 770770         <p class="peach-settings-nav-label">VAULT</p>771771         {renderCatRow({ section: "vault", name: "Vault", desc: "Entries, health, import & export", icon: iconLock })}772-        {renderCatRow({ section: "sync", name: "Sync", desc: "Paired devices & trusted networks", icon: iconSync, pro: true })}772+        {renderCatRow({ section: "sync", name: "Sync", desc: "Pair devices · sync is free", icon: iconSync })}773773 774774         <p class="peach-settings-nav-label">PREFERENCES</p>775775         {renderCatRow({ section: "appearance", name: "Appearance", desc: "Theme & accent color", icon: iconSun })}@@ -1300,180 +1300,177 @@13001300     return (13011301       <div class="peach-settings-body">13021302         <div class="peach-settings-group">1303-          {/* Pairing is free — always show Add a device and paired list */}1304-          <div class="peach-settings-row peach-settings-row--meta peach-settings-row--stack">1305-            <span class="peach-sync-section-label">Paired devices ({pairedDevices().length})</span>1306-            <Show when={pairedDevices().length === 0}>1307-              <p class="peach-settings-hint">1308-                No paired devices yet. Add a device to start syncing.1309-              </p>1310-            </Show>1311-            <Show when={pairedDevices().length > 0}>1312-              <div class="peach-history-list">1313-                <For each={pairedDevices()}>1314-                  {(device) => (1315-                    <div class="peach-history-row">1316-                      <div class="peach-history-meta">1317-                        <span class="peach-history-time">{device.displayName}</span>1318-                        <span class="peach-history-sub">1319-                          {device.lastSyncAt1320-                            ? `Last synced: ${formatSnapshotTime(device.lastSyncAt)}`1321-                            : "Never synced"}1322-                        </span>1323-                      </div>1324-                      <button1325-                        class="peach-btn peach-btn--subtle peach-btn--sm peach-history-btn"1326-                        onClick={() => void handleRemoveDevice(device.deviceId)}1327-                      >1328-                        Remove1329-                      </button>1303+          <p class="peach-sync-network-note">1304+            Sync works on your home and trusted networks. Some public or1305+            corporate networks block device connections.1306+          </p>1307+1308+          {/* Add a device — FREE, always shown */}1309+          <button1310+            class="peach-settings-row peach-settings-row--tappable"1311+            onClick={() => props.onOpenSyncPairing?.()}1312+          >1313+            <span>Add a device</span>1314+            <svg width="6" height="10" viewBox="0 0 6 10" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round">1315+              <path d="M1 1l4 4-4 4" />1316+            </svg>1317+          </button>1318+1319+          {/* Paired devices list — shown when any exist */}1320+          <Show when={pairedDevices().length > 0}>1321+            <div class="peach-history-list">1322+              <For each={pairedDevices()}>1323+                {(device) => (1324+                  <div class="peach-history-row">1325+                    <div class="peach-history-meta">1326+                      <span class="peach-history-time">{device.displayName}</span>1327+                      <span class="peach-history-sub">1328+                        {device.lastSyncAt1329+                          ? `Last synced ${formatRelativeTime(device.lastSyncAt)}`1330+                          : "Never synced"}1331+                      </span>13301332                     </div>1331-                  )}1332-                </For>1333-              </div>1334-            </Show>1335-            <div class="peach-settings-actions--row">1336-              <button1337-                class="peach-btn peach-btn--subtle peach-btn--sm"1338-                onClick={() => props.onOpenSyncPairing?.()}1339-              >1340-                Add a device1341-              </button>1333+                    <button1334+                      class="peach-btn peach-btn--subtle peach-btn--sm peach-history-btn"1335+                      onClick={() => void handleRemoveDevice(device.deviceId)}1336+                    >1337+                      Remove1338+                    </button>1339+                  </div>1340+                )}1341+              </For>13421342             </div>1343-          </div>1343+          </Show>13441344 1345-          {/* Automatic sync is Pro-gated */}1345+          {/* Automatic sync — PRO only */}13461346           <Show1347-            when={!isPro()}1347+            when={isPro()}13481348             fallback={1349-              <>1350-                <div class="peach-settings-row">1351-                  <span>Status</span>1352-                  <span class="peach-settings-value peach-settings-value--status">1353-                    <span class="peach-sync-status-dot" style={{ background: getStatusColor(syncStatus()) }}></span>1354-                    {getStatusLabel(syncStatus())}1355-                  </span>1356-                </div>1357-                <div class="peach-settings-row peach-settings-row--meta">1358-                  <span>Last sync</span>1359-                  <span class="peach-settings-value">{lastSyncAt() ? formatRelativeTime(lastSyncAt()) : "Never synced"}</span>1360-                </div>1361-                <div class="peach-settings-row peach-settings-row--meta">1349+              <Show when={pairedDevices().length > 0}>1350+                <div class="peach-sync-pro-hint">1351+                  <p class="peach-settings-hint">1352+                    Upgrade to Pro for automatic background sync across all your devices.1353+                  </p>13621354                   <button13631355                     class="peach-btn peach-btn--subtle peach-btn--sm"1364-                    onClick={() => void handleSyncNow()}1365-                    disabled={syncStatus() === "not_trusted" || syncingNow()}1356+                    onClick={() => setActiveSection("license")}13661357                   >1367-                    <Switch>1368-                      <Match when={syncingNow()}>Syncing…</Match>1369-                      <Match when={syncFeedback()?.kind === "ok"}>✓ {syncFeedback()!.text}</Match>1370-                      <Match when={syncFeedback()?.kind === "err"}>{syncFeedback()!.text}</Match>1371-                      <Match when={true}>Sync now</Match>1372-                    </Switch>1358+                    Upgrade to Pro13731359                   </button>13741360                 </div>1375-                <p class="peach-settings-hint">1376-                  If you edit on multiple devices before syncing, the most recently1377-                  changed device wins. Use Vault History above to recover older1378-                  versions if needed.1379-                </p>1361+              </Show>1362+            }1363+          >1364+            <div class="peach-settings-row">1365+              <span>Status</span>1366+              <span class="peach-settings-value peach-settings-value--status">1367+                <span class="peach-sync-status-dot" style={{ background: getStatusColor(syncStatus()) }}></span>1368+                {getStatusLabel(syncStatus())}1369+              </span>1370+            </div>1371+            <div class="peach-settings-row peach-settings-row--meta">1372+              <span>Last sync</span>1373+              <span class="peach-settings-value">{lastSyncAt() ? formatRelativeTime(lastSyncAt()) : "Never synced"}</span>1374+            </div>1375+            <div class="peach-settings-row peach-settings-row--meta">1376+              <button1377+                class="peach-btn peach-btn--subtle peach-btn--sm"1378+                onClick={() => void handleSyncNow()}1379+                disabled={syncStatus() === "not_trusted" || syncingNow()}1380+              >1381+                <Switch>1382+                  <Match when={syncingNow()}>Syncing…</Match>1383+                  <Match when={syncFeedback()?.kind === "ok"}>✓ {syncFeedback()!.text}</Match>1384+                  <Match when={syncFeedback()?.kind === "err"}>{syncFeedback()!.text}</Match>1385+                  <Match when={true}>Sync now</Match>1386+                </Switch>1387+              </button>1388+            </div>1389+            <p class="peach-settings-hint">1390+              If you edit on multiple devices before syncing, the most recently1391+              changed device wins. Use Vault History to recover older versions.1392+            </p>13801393 1381-                <div class="peach-settings-row peach-settings-row--meta peach-settings-row--stack">1382-                  <span class="peach-sync-section-label">Trusted networks ({trustedNetworks().length})</span>1383-                  <For each={trustedNetworks()}>1384-                    {(network) => (1385-                      <div class="peach-sync-device-row">1386-                        <span class="peach-sync-device-name">1387-                          {network.isTailscale ? "Tailscale" : (network.ssid ?? "Unknown network")}1388-                        </span>1389-                        <Show1390-                          when={!network.isTailscale}1391-                          fallback={<span class="peach-sync-device-meta">Always trusted</span>}1392-                        >1393-                          <button1394-                            class="peach-btn peach-btn--ghost peach-btn--sm peach-sync-remove-btn"1395-                            onClick={() => void handleRemoveNetwork(network.ssid, network.bssid, network.isTailscale)}1396-                          >1397-                            Remove1398-                          </button>1399-                        </Show>1400-                      </div>1401-                    )}1402-                  </For>1403-                  <div class="peach-settings-actions--row">1404-                    <Show when={!isCurrentNetworkTrusted()}>1394+            <div class="peach-settings-row peach-settings-row--meta peach-settings-row--stack">1395+              <span class="peach-sync-section-label">Trusted networks ({trustedNetworks().length})</span>1396+              <For each={trustedNetworks()}>1397+                {(network) => (1398+                  <div class="peach-sync-device-row">1399+                    <span class="peach-sync-device-name">1400+                      {network.isTailscale ? "Tailscale" : (network.ssid ?? "Unknown network")}1401+                    </span>1402+                    <Show1403+                      when={!network.isTailscale}1404+                      fallback={<span class="peach-sync-device-meta">Always trusted</span>}1405+                    >14051406                       <button1406-                        class="peach-btn peach-btn--subtle peach-btn--sm"1407-                        onClick={() => void handleMarkNetworkTrusted()}1408-                        disabled={markingNetwork()}1407+                        class="peach-btn peach-btn--ghost peach-btn--sm peach-sync-remove-btn"1408+                        onClick={() => void handleRemoveNetwork(network.ssid, network.bssid, network.isTailscale)}14091409                       >1410-                        {markingNetwork() ? "Marking…" : "Mark current network as trusted"}1410+                        Remove14111411                       </button>14121412                     </Show>14131413                   </div>1414-                </div>1415-1416-                <div class="peach-settings-row peach-settings-row--meta peach-settings-row--stack">1417-                  <span class="peach-sync-section-label">Advanced</span>1418-                  <Show1419-                    when={resetIdentityConfirm()}1420-                    fallback={1421-                      <div class="peach-sync-advanced-row">1422-                        <div class="peach-sync-advanced-info">1423-                          <span class="peach-sync-advanced-name">Reset sync identity</span>1424-                          <span class="peach-sync-device-meta">1425-                            Generates a new keypair and unpairs every device.1426-                          </span>1427-                        </div>1428-                        <button1429-                          class="peach-btn peach-btn--ghost peach-btn--sm peach-sync-remove-btn"1430-                          onClick={() => setResetIdentityConfirm(true)}1431-                        >1432-                          Reset1433-                        </button>1434-                      </div>1435-                    }1414+                )}1415+              </For>1416+              <div class="peach-settings-actions--row">1417+                <Show when={!isCurrentNetworkTrusted()}>1418+                  <button1419+                    class="peach-btn peach-btn--subtle peach-btn--sm"1420+                    onClick={() => void handleMarkNetworkTrusted()}1421+                    disabled={markingNetwork()}14361422                   >1437-                    <div class="peach-sync-reset-confirm">1438-                      <p class="peach-sync-reset-confirm-headline">Reset sync identity?</p>1439-                      <p class="peach-sync-reset-confirm-body">1440-                        This will unpair all devices. You will need to pair them again from scratch.1441-                      </p>1442-                      <div class="peach-settings-actions--row">1443-                        <button1444-                          class="peach-btn peach-btn--ghost peach-btn--sm"1445-                          onClick={() => setResetIdentityConfirm(false)}1446-                          disabled={resetIdentityRunning()}1447-                        >1448-                          Cancel1449-                        </button>1450-                        <button1451-                          class="peach-btn peach-btn--danger peach-btn--sm"1452-                          onClick={() => void handleResetSyncIdentity()}1453-                          disabled={resetIdentityRunning()}1454-                        >1455-                          {resetIdentityRunning() ? "Resetting..." : "Reset"}1456-                        </button>1457-                      </div>1423+                    {markingNetwork() ? "Marking…" : "Mark current network as trusted"}1424+                  </button>1425+                </Show>1426+              </div>1427+            </div>1428+1429+            <div class="peach-settings-row peach-settings-row--meta peach-settings-row--stack">1430+              <span class="peach-sync-section-label">Advanced</span>1431+              <Show1432+                when={resetIdentityConfirm()}1433+                fallback={1434+                  <div class="peach-sync-advanced-row">1435+                    <div class="peach-sync-advanced-info">1436+                      <span class="peach-sync-advanced-name">Reset sync identity</span>1437+                      <span class="peach-sync-device-meta">1438+                        Generates a new keypair and unpairs every device.1439+                      </span>14581440                     </div>1459-                  </Show>1441+                    <button1442+                      class="peach-btn peach-btn--ghost peach-btn--sm peach-sync-remove-btn"1443+                      onClick={() => setResetIdentityConfirm(true)}1444+                    >1445+                      Reset1446+                    </button>1447+                  </div>1448+                }1449+              >1450+                <div class="peach-sync-reset-confirm">1451+                  <p class="peach-sync-reset-confirm-headline">Reset sync identity?</p>1452+                  <p class="peach-sync-reset-confirm-body">1453+                    This will unpair all devices. You will need to pair them again from scratch.1454+                  </p>1455+                  <div class="peach-settings-actions--row">1456+                    <button1457+                      class="peach-btn peach-btn--ghost peach-btn--sm"1458+                      onClick={() => setResetIdentityConfirm(false)}1459+                      disabled={resetIdentityRunning()}1460+                    >1461+                      Cancel1462+                    </button>1463+                    <button1464+                      class="peach-btn peach-btn--danger peach-btn--sm"1465+                      onClick={() => void handleResetSyncIdentity()}1466+                      disabled={resetIdentityRunning()}1467+                    >1468+                      {resetIdentityRunning() ? "Resetting..." : "Reset"}1469+                    </button>1470+                  </div>14601471                 </div>1461-              </>1462-            }1463-          >1464-            <Show when={pairedDevices().length > 0}>1465-              <div class="peach-sync-pro-hint">1466-                <p class="peach-settings-hint">1467-                  Upgrade to Pro for automatic background sync across all your devices.1468-                </p>1469-                <button1470-                  class="peach-btn peach-btn--subtle peach-btn--sm"1471-                  onClick={() => setActiveSection("license")}1472-                >1473-                  Upgrade to Pro1474-                </button>1475-              </div>1476-            </Show>1472+              </Show>1473+            </div>14771474           </Show>14781475         </div>14791476       </div>

view file