8e6e3a1d2371

suppress inline selector reopens for 1.5s after submit-button click, Enter-in-form, or submit event (v2.1.8)

time
2026-04-26 17:21 UTC · 6 months ago
machine
koh2
parent
6523d1775037
4 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/content/index.ts+45 −0
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.1.7",4+  "version": "2.1.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.1.7",4+  "version": "2.1.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.1.7",3+  "version": "2.1.8",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/content/index.ts+45 −0
@@ -52,6 +52,42 @@5252   }5353 }5454 55+// Tracks the timestamp of the most recent submit-like interaction on the page56+// (clicking a submit button, pressing Enter in a form, or a `submit` event).57+// Many sites refocus the password/username field on submit — without this58+// guard, that refocus opens the selector exactly when the user is trying to59+// log in. Cleared implicitly by aging out (SUBMIT_SUPPRESS_MS).60+let recentSubmitAt = 0;61+const SUBMIT_SUPPRESS_MS = 1500;62+63+function looksLikeSubmitTrigger(el: Element): boolean {64+  if (el.matches("input[type='submit'], input[type='image']")) return true;65+  if (el.matches("button")) {66+    const type = (el as HTMLButtonElement).type;67+    if (type === "button") return false;68+    if (type === "submit" || el.closest("form")) return true;69+  }70+  const text = (el.textContent || "").toLowerCase().trim();71+  if (!text) return false;72+  return /\b(log\s*in|sign\s*in|sign\s*up|submit|continue|next|register|create\s*account)\b/.test(text);73+}74+75+document.addEventListener("mousedown", (e) => {76+  const target = e.target as Element | null;77+  if (!target?.closest) return;78+  const btn = target.closest("button, input[type='submit'], input[type='image'], input[type='button'], [role='button']") as Element | null;79+  if (btn && looksLikeSubmitTrigger(btn)) recentSubmitAt = Date.now();80+}, true);81+document.addEventListener("submit", () => { recentSubmitAt = Date.now(); }, true);82+document.addEventListener("keydown", (e) => {83+  if (e.key !== "Enter") return;84+  const target = e.target as Element | null;85+  if (!target) return;86+  if (target.matches?.("input, textarea") && (target as HTMLInputElement).form) {87+    recentSubmitAt = Date.now();88+  }89+}, true);90+5591 function debounce<T extends (...args: unknown[]) => void>(5692   fn: T,5793   ms: number@@ -431,6 +467,12 @@431467   // trying to log in. Bail unless the user explicitly summoned us.432468   if (recentlyAutofilledFields.has(field)) return;433469 470+  // A submit-like interaction just happened (button mousedown, Enter in a471+  // form, or a `submit` event). Many sites refocus the password/username472+  // field on submit — suppress the selector long enough for the navigation473+  // or AJAX request to take over.474+  if (Date.now() - recentSubmitAt < SUBMIT_SUPPRESS_MS) return;475+434476   // Prevent two concurrent calls for the same field (e.g. focusin + icon click435477   // firing simultaneously — the second call would dismiss the first's selector).436478   if (pendingInteractions.has(field)) return;@@ -528,6 +570,7 @@528570     currentDismiss = null;529571     lastDismissedField = null; // icon click is always an explicit user open — clear the dismiss lock530572     recentlyAutofilledFields.delete(el); // explicit summon also clears autofill suppression573+    recentSubmitAt = 0;531574     el.focus({ preventScroll: true });532575     void handleFieldInteraction(el);533576   });@@ -584,6 +627,7 @@584627           currentDismiss = null;585628           lastDismissedField = null;586629           recentlyAutofilledFields.delete(el);630+          recentSubmitAt = 0;587631           el.focus({ preventScroll: true });588632           void handleFieldInteraction(el);589633         });@@ -645,6 +689,7 @@645689             ?? fields.find(f => f.type === "username" || f.type === "email")?.el);646690       if (target) {647691         recentlyAutofilledFields.delete(target); // explicit summon clears suppression692+        recentSubmitAt = 0;648693         void handleFieldInteraction(target);649694       }650695     } else if (isShortcut) {

view file