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
↓ peach-browser-efe6b2ae.tar.gzatlas at this save
files changed7 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 −62manifest.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": {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",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>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 {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 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 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