Last active
October 18, 2026 12:47
-
-
Save rishubil/663e3716533a27247c26ac9e1651bf8e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Password manager autofill fixes | |
| // @namespace https://gist.github.com/rishubil | |
| // @version 0.3.1 | |
| // @description Fixes form fields that password managers such as 1Password cannot fill. | |
| // @match https://arca.live/u/login* | |
| // @downloadURL https://gist.github.com/rishubil/663e3716533a27247c26ac9e1651bf8e/raw/autofill-fixes.user.js | |
| // @updateURL https://gist.github.com/rishubil/663e3716533a27247c26ac9e1651bf8e/raw/autofill-fixes.user.js | |
| // @run-at document-idle | |
| // @grant none | |
| // ==/UserScript== | |
| // To support a new site, add an entry to FIXES and add its pages to @match above. | |
| (function () { | |
| 'use strict'; | |
| const FIXES = [ | |
| { | |
| name: 'arca.live OTP', | |
| matches: (url) => url.hostname === 'arca.live' && url.pathname.startsWith('/u/login'), | |
| apply: fixArcaLiveOtp, | |
| }, | |
| ]; | |
| // The site splits the OTP code into six boxes (#numberInput1-6). Password | |
| // managers do not fill them. This fix adds one plain OTP field and passes a | |
| // complete code to the boxes, where the site's own input handler submits it. | |
| function fixArcaLiveOtp() { | |
| const stage = document.getElementById('stage-6'); | |
| const boxes = stage && [...stage.querySelectorAll('.otp-inputs input')]; | |
| if (!boxes || boxes.length !== 6) return; | |
| const [firstBox] = boxes; | |
| const field = document.createElement('input'); | |
| field.type = 'text'; | |
| field.id = 'otp'; | |
| field.name = 'otp'; | |
| field.autocomplete = 'one-time-code'; | |
| field.inputMode = 'numeric'; | |
| field.maxLength = 7; | |
| field.placeholder = 'OTP'; | |
| field.setAttribute('aria-label', 'OTP code'); | |
| field.style.cssText = 'display:block;width:100%;box-sizing:border-box;margin:0 0 12px;' | |
| + 'padding:8px;font-size:1.2em;text-align:center;letter-spacing:.3em;'; | |
| stage.querySelector('.otp-inputs').before(field); | |
| // Only the new field may claim the one-time code. | |
| firstBox.setAttribute('autocomplete', 'off'); | |
| // In the OTP stage, <body> takes digit keys and paste away from every input. | |
| for (const type of ['keydown', 'paste']) { | |
| field.addEventListener(type, (event) => event.stopPropagation()); | |
| } | |
| const submit = (code) => { | |
| for (const box of boxes) box.value = ''; | |
| firstBox.value = code; | |
| firstBox.dispatchEvent(new InputEvent('input')); | |
| }; | |
| // A password manager can fill the code together with the username and the | |
| // password, before the OTP stage opens. The site would submit that code | |
| // too early, so hold it until the OTP stage opens. | |
| let pendingCode = ''; | |
| // Pass only a complete code. On a partial code, the site moves focus to the next box. | |
| field.addEventListener('input', () => { | |
| const code = field.value.replace(/\D/g, ''); | |
| if (code.length !== 6) return; | |
| if (stage.classList.contains('active')) { | |
| submit(code); | |
| } else { | |
| pendingCode = code; | |
| } | |
| }); | |
| // The site calls focus() on box 1 when it opens or resets the OTP stage. | |
| // A focus listener is not enough: the browser fires no focus event while | |
| // the window is in the background. | |
| firstBox.focus = () => { | |
| if (pendingCode) { | |
| submit(pendingCode); | |
| pendingCode = ''; | |
| return; | |
| } | |
| field.value = ''; | |
| field.focus(); | |
| }; | |
| } | |
| const url = new URL(location.href); | |
| for (const fix of FIXES) { | |
| if (fix.matches(url)) fix.apply(); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment