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
↓ peach-browser-8e6e3a1d.tar.gzatlas at this save
files changed4 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/content/index.ts+45 −0manifest.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": {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",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": {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) {