efe6b2aee64e

vault history shows 3 latest inline + older dropdown; center version corner; fix Pro card date clipping; health loading screen for all users + title renamed to Advanced Vault Health; health view stays mounted during entry edit to prevent freeze on cancel (v3.6.61)

time
2026-05-20 11:00 UTC · 5 months ago
machine
koh2
parent
cbceefec3283
7 files manifest.firefox.json+1 −1manifest.json+1 −1src/popup/App.tsx+4 −1src/popup/styles/popup.css+1 −1src/popup/styles/settings-stack.css+3 −1src/popup/views/Health.tsx+11 −4src/popup/views/Settings.tsx+65 −62
manifest.firefox.json+1 −1
@@ -2,7 +2,7 @@22   "_comment_audio_capture": "Firefox does not honor the Chrome 'audioCapture' optional_permission — getUserMedia in WebExtensions is gated solely by the runtime permission prompt. Microphone access for chirp pairing works from the popup under the existing '<all_urls>' host permission; no manifest entry is required or accepted here.",33   "manifest_version": 3,44   "name": "Peach",5-  "version": "3.6.60",5+  "version": "3.6.61",66   "description": "Zero-knowledge password manager",77   "browser_specific_settings": {88     "gecko": {

view file

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

view file

src/popup/App.tsx+4 −1
@@ -1488,7 +1488,9 @@14881488                 />14891489               </Suspense>14901490             </Show>1491-            <Show when={view() === "health"}>1491+            {/* Health stays mounted while editing an entry from it so1492+                computeReused() doesn't rerun on every cancel/back. */}1493+            <Show when={view() === "health" || (view() === "add" && editBackView() === "health")}>14921494               <Suspense fallback={<div class="peach-loading"><PeachWordmark /></div>}>14931495                 <HealthView14941496                   entries={entries()}@@ -1496,6 +1498,7 @@14961498                   onFixEntry={handleFixEntry}14971499                   onBack={() => setView("settings")}14981500                   onRefresh={loadEntries}1501+                  hidden={view() !== "health"}14991502                 />15001503               </Suspense>15011504             </Show>

view file

src/popup/styles/popup.css+1 −1
@@ -6244,7 +6244,7 @@62446244   flex-direction: column;62456245   align-items: center;62466246   gap: 6px;6247-  padding: 20px 0 22px;6247+  padding: 20px 0 8px;62486248 }62496249 62506250 .peach-pro-active-mark-wrap {

view file

src/popup/styles/settings-stack.css+3 −1
@@ -60,12 +60,14 @@6060 .stk-version-corner {6161   position: absolute;6262   bottom: 7px;63-  right: 14px;63+  left: 50%;64+  transform: translateX(-50%);6465   font-family: var(--font-mono);6566   font-size: 7px;6667   letter-spacing: 0.06em;6768   color: color-mix(in srgb, var(--peach-t2) 50%, transparent);6869   pointer-events: none;70+  white-space: nowrap;6971   z-index: 1;7072 }7173 

view file

src/popup/views/Health.tsx+11 −4
@@ -26,6 +26,9 @@2626   onFixEntry: (entry: VaultEntry) => void;2727   onBack: () => void;2828   onRefresh: () => void;29+  /** Set by App while an entry edit is in progress so the view stays mounted30+   *  but invisible — avoids re-running computeReused() on every back-nav. */31+  hidden?: boolean;2932 }3033 3134 function formatExposure(count: number): string {@@ -182,9 +185,11 @@182185       stalePasskeys().length183186   );184187 188+  const isLoading = () => reusedLoading() || (!!props.isPro && advancedLoading());189+185190   return (186191     <>187-      <div class="peach-overlay-view ov-enter-right" style="display:flex">192+      <div class="peach-overlay-view ov-enter-right" style={{ display: props.hidden ? "none" : "flex" }}>188193         <div class="peach-overlay-header">189194           <button190195             class="peach-btn peach-btn--ghost peach-btn--sm peach-icon-btn"@@ -195,12 +200,12 @@195200               <path d="M6 1L1 6L6 11" />196201             </svg>197202           </button>198-          <span class="peach-overlay-title">vault health</span>203+          <span class="peach-overlay-title">Advanced Vault Health</span>199204           <div class="peach-overlay-spacer"></div>200205         </div>201206 202207         <div class="peach-health-body">203-          <Show when={props.isPro && advancedLoading()}>208+          <Show when={isLoading()}>204209             <div class="peach-health-loading-screen" aria-busy="true" aria-live="polite">205210               <div class="peach-health-loading-glyph" aria-hidden="true">206211                 <svg width="32" height="32" viewBox="0 0 32 32" fill="none">@@ -220,7 +225,8 @@220225               </div>221226             </div>222227           </Show>223-          <Show when={props.isPro && !advancedLoading() && advancedReport() && advancedReport()!.remediation.length > 0}>228+          <Show when={!isLoading()}>229+          <Show when={props.isPro && advancedReport() && advancedReport()!.remediation.length > 0}>224230             <div class="peach-health-pro-queue">225231               <div class="peach-health-pro-queue-head">226232                 <span class="peach-health-pro-queue-title">Remediation queue</span>@@ -451,6 +457,7 @@451457               </For>452458             </HealthSection>453459           </div>460+          </Show>{/* end !isLoading() */}454461         </div>455462       </div>456463 

view file

src/popup/views/Settings.tsx+65 −62
@@ -205,7 +205,7 @@205205   const [historySnapshots, setHistorySnapshots] = createSignal<VaultSnapshot[]>([]);206206   const [historyLoading, setHistoryLoading] = createSignal(false);207207   const [historyRestoring, setHistoryRestoring] = createSignal<string | null>(null);208-  const [historyOpen, setHistoryOpen] = createSignal(false);208+  const [historyShowAll, setHistoryShowAll] = createSignal(false);209209 210210   async function loadVaultHistory() {211211     setHistoryLoading(true);@@ -1062,69 +1062,72 @@10621062             </button>10631063           </div>10641064 1065-          <button1066-            class="peach-settings-advanced-toggle"1067-            onClick={() => setHistoryOpen((v) => !v)}1068-            aria-expanded={historyOpen()}1069-          >1070-            <span class="peach-settings-sub-label" style="margin:0">VAULT HISTORY</span>1071-            <span class="peach-settings-advanced-chevron">1072-              {historyOpen() ? "▲" : "▼"}1073-            </span>1074-          </button>1075-          <Show when={historyOpen()}>1076-            <Show when={historyLoading()}>1077-              <p class="peach-settings-hint">Loading…</p>1078-            </Show>1079-            <Show when={!historyLoading() && historySnapshots().length === 0}>1080-              <p class="peach-settings-hint">1081-                No snapshots yet. Peach saves automatically before every change —1082-                saves, syncs, and imports.1083-              </p>1084-            </Show>1085-            <Show when={!historyLoading() && historySnapshots().length > 0}>1086-              <p class="peach-settings-hint">1087-                Restoring locks your vault. Unlock with the password that1088-                encrypted that snapshot to confirm.1089-              </p>1090-              <div class="peach-history-list">1091-                <For each={historySnapshots()}>1092-                  {(snap) => (1093-                    <div class="peach-history-row">1094-                      <div class="peach-history-meta">1095-                        <span class="peach-history-time">1096-                          {formatSnapshotTime(snap.savedAt)}1097-                        </span>1098-                        <span class="peach-history-sub">1099-                          {[1100-                            snap.entryCount !== null1101-                              ? `${snap.entryCount} ${snap.entryCount === 1 ? "entry" : "entries"}`1102-                              : null,1103-                            reasonLabel(snap.reason),1104-                          ]1105-                            .filter(Boolean)1106-                            .join(" · ")}1107-                        </span>1108-                      </div>1109-                      <button1110-                        class={`peach-btn peach-btn--subtle peach-btn--sm peach-history-btn${1111-                          historyRestoring() === snap.id ? " is-busy" : ""1112-                        }`}1113-                        onClick={() => void handleRestoreVaultSnapshot(snap.id)}1114-                        disabled={historyRestoring() !== null}1115-                      >1116-                        <Show when={historyRestoring() === snap.id} fallback="Restore">1117-                          …1118-                        </Show>1119-                      </button>1065+          <span class="peach-settings-sub-label">VAULT HISTORY</span>1066+          <Show when={historyLoading()}>1067+            <p class="peach-settings-hint">Loading…</p>1068+          </Show>1069+          <Show when={!historyLoading() && historySnapshots().length === 0}>1070+            <p class="peach-settings-hint">1071+              No snapshots yet. Peach saves automatically before every change —1072+              saves, syncs, and imports.1073+            </p>1074+          </Show>1075+          <Show when={!historyLoading() && historySnapshots().length > 0}>1076+            <p class="peach-settings-hint">1077+              Restoring locks your vault. Unlock with the password that1078+              encrypted that snapshot to confirm.1079+            </p>1080+            <div class="peach-history-list">1081+              <For each={historySnapshots().slice(0, historyShowAll() ? undefined : 3)}>1082+                {(snap) => (1083+                  <div class="peach-history-row">1084+                    <div class="peach-history-meta">1085+                      <span class="peach-history-time">1086+                        {formatSnapshotTime(snap.savedAt)}1087+                      </span>1088+                      <span class="peach-history-sub">1089+                        {[1090+                          snap.entryCount !== null1091+                            ? `${snap.entryCount} ${snap.entryCount === 1 ? "entry" : "entries"}`1092+                            : null,1093+                          reasonLabel(snap.reason),1094+                        ]1095+                          .filter(Boolean)1096+                          .join(" · ")}1097+                      </span>11201098                     </div>1121-                  )}1122-                </For>1123-              </div>1124-              <p class="peach-history-footer">1125-                {historySnapshots().length} of 10 snapshots1126-              </p>1099+                    <button1100+                      class={`peach-btn peach-btn--subtle peach-btn--sm peach-history-btn${1101+                        historyRestoring() === snap.id ? " is-busy" : ""1102+                      }`}1103+                      onClick={() => void handleRestoreVaultSnapshot(snap.id)}1104+                      disabled={historyRestoring() !== null}1105+                    >1106+                      <Show when={historyRestoring() === snap.id} fallback="Restore">1107+                        …1108+                      </Show>1109+                    </button>1110+                  </div>1111+                )}1112+              </For>1113+            </div>1114+            <Show when={historySnapshots().length > 3}>1115+              <button1116+                class="peach-settings-advanced-toggle"1117+                onClick={() => setHistoryShowAll((v) => !v)}1118+                aria-expanded={historyShowAll()}1119+              >1120+                <span class="peach-settings-hint" style="margin:0">1121+                  {historyShowAll() ? "show less" : `${historySnapshots().length - 3} older`}1122+                </span>1123+                <span class="peach-settings-advanced-chevron">1124+                  {historyShowAll() ? "▲" : "▼"}1125+                </span>1126+              </button>11271127             </Show>1128+            <p class="peach-history-footer">1129+              {historySnapshots().length} of 10 snapshots1130+            </p>11281131           </Show>11291132 11301133           <button

view file