93a2840e97c4
fix: identity helper + suggest list fade is scroll-state-aware (only when overflowing, clears at bottom) instead of an always-on mask that cut short lists (v3.6.150)
- time
- 2026-06-03 14:12 UTC · 4 months ago
- machine
- koh2
- agent
- claude-opus-4-8
- session
- inline popup fixes
- parent
- a45e13531957
↓ peach-browser-93a2840e.tar.gzatlas at this save
files changed4 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/content/inject.ts+32 −3manifest.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 Password Manager",5- "version": "3.6.149",5+ "version": "3.6.150",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 Password Manager",4- "version": "3.6.149",4+ "version": "3.6.150",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "3.6.149",3+ "version": "3.6.150",44 "description": "Zero-knowledge password manager",55 "type": "module",66 "scripts": {src/content/inject.ts+32 −3
@@ -1197,9 +1197,7 @@11971197 transition: grid-template-rows 220ms cubic-bezier(0.16,1,0.3,1); }11981198 .identity-rows.is-collapsed { grid-template-rows: 0fr; }11991199 .identity-rows-inner { overflow-y: auto; min-height: 0; max-height: 148px;1200- scrollbar-width: thin; scrollbar-color: var(--peach-text-muted) transparent;1201- mask-image: linear-gradient(to bottom, black calc(100% - 18px), transparent 100%);1202- -webkit-mask-image: linear-gradient(to bottom, black calc(100% - 18px), transparent 100%); }1200+ scrollbar-width: thin; scrollbar-color: var(--peach-text-muted) transparent; }12031201 .identity-rows-inner::-webkit-scrollbar { width: 4px; }12041202 .identity-rows-inner::-webkit-scrollbar-track { background: transparent; }12051203 .identity-rows-inner::-webkit-scrollbar-thumb { background: var(--peach-text-muted); border-radius: 2px; }@@ -1542,6 +1540,20 @@15421540 // Identity rows15431541 const list = container.appendChild(document.createElement("div"));15441542 list.className = "identity-list";1543+ // Bottom fade only when the list overflows its max-height and isn't scrolled1544+ // to the end. A short list (1-2 identities) shows no fade, so it never looks1545+ // prematurely cut off; a long list gets the affordance, which clears on1546+ // reaching the bottom.1547+ const updateListFade = () => {1548+ const overflowing = list.scrollHeight > list.clientHeight + 1;1549+ const atBottom = list.scrollTop + list.clientHeight >= list.scrollHeight - 2;1550+ const mask = overflowing && !atBottom1551+ ? "linear-gradient(to bottom, black calc(100% - 24px), transparent 100%)"1552+ : "none";1553+ list.style.setProperty("mask-image", mask);1554+ list.style.setProperty("-webkit-mask-image", mask);1555+ };1556+ list.addEventListener("scroll", updateListFade);15451557 if (typedQuery && identities.length === 0) {15461558 const empty = list.appendChild(document.createElement("div"));15471559 empty.className = "identity-empty";@@ -1573,6 +1585,8 @@15731585 });15741586 }15751587 1588+ updateListFade();1589+15761590 // Divider before "Use a different email…"15771591 if (identities.length > 0) {15781592 container.appendChild(Object.assign(document.createElement("div"), { className: "divider" }));@@ -1937,6 +1951,20 @@19371951 const rows = rowsWrapper.appendChild(document.createElement("div"));19381952 rows.className = "identity-rows-inner";19391953 1954+ // Bottom fade only when the list actually overflows and isn't scrolled to1955+ // the end — a static mask-image fades the last row even on a 1-2 item list1956+ // that fits entirely, making it look cut off. Driven off live scroll state.1957+ const updateRowsFade = () => {1958+ const overflowing = rows.scrollHeight > rows.clientHeight + 1;1959+ const atBottom = rows.scrollTop + rows.clientHeight >= rows.scrollHeight - 2;1960+ const mask = overflowing && !atBottom1961+ ? "linear-gradient(to bottom, black calc(100% - 18px), transparent 100%)"1962+ : "none";1963+ rows.style.setProperty("mask-image", mask);1964+ rows.style.setProperty("-webkit-mask-image", mask);1965+ };1966+ rows.addEventListener("scroll", updateRowsFade);1967+19401968 // Hides the list and removes the document-level click watcher.19411969 function hideList() {19421970 list.style.display = "none";@@ -2006,6 +2034,7 @@20062034 });20072035 }20082036 list.style.display = "";2037+ updateRowsFade();20092038 // Register the close-on-outside-click watcher each time the list shows.20102039 // addEventListener deduplicates identical (fn, options) pairs, so20112040 // re-rendering on input events does not stack up listeners.