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
4 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/content/inject.ts+32 −3
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 Password Manager",5-  "version": "3.6.149",5+  "version": "3.6.150",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 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",

view file

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": {

view file

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.

view file