Add autocomplete="off" to all six digit inputs so Safari only targets the single otp-autofill helper field with autocomplete="one-time-code". Focus the helper field on init and clearOtp() so Safari evaluates it for autofill at page load; add focus-proxy CSS class to keep the first box visually highlighted while the helper has focus. Also close the host edit modal when clicking the backdrop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@@ -1305,6 +1305,7 @@ sub app_html {
|
||
| 1305 | 1305 |
transition: border-color .15s, background .15s; |
| 1306 | 1306 |
} |
| 1307 | 1307 |
.otp-row input:focus { border-color: var(--accent); background: #fff; }
|
| 1308 |
+ .otp-row input.focus-proxy { border-color: var(--accent); background: #fff; }
|
|
| 1308 | 1309 |
.otp-row input.filled { border-color: #b3c6f0; background: #fff; }
|
| 1309 | 1310 |
#login-error {
|
| 1310 | 1311 |
color: var(--bad); font-size: 13px; text-align: center; |
@@ -1494,12 +1495,12 @@ sub app_html {
|
||
| 1494 | 1495 |
<input type="hidden" id="otp-hidden" name="otp"> |
| 1495 | 1496 |
</div> |
| 1496 | 1497 |
<div class="otp-row"> |
| 1497 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 1"> |
|
| 1498 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 2"> |
|
| 1499 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 3"> |
|
| 1500 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 4"> |
|
| 1501 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 5"> |
|
| 1502 |
- <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" aria-label="Digit 6"> |
|
| 1498 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 1"> |
|
| 1499 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 2"> |
|
| 1500 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 3"> |
|
| 1501 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 4"> |
|
| 1502 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 5"> |
|
| 1503 |
+ <input type="text" inputmode="numeric" pattern="[0-9]*" class="otp-digit" autocomplete="off" aria-label="Digit 6"> |
|
| 1503 | 1504 |
</div> |
| 1504 | 1505 |
</form> |
| 1505 | 1506 |
<div id="login-error"></div> |
@@ -2002,7 +2003,7 @@ sub app_html {
|
||
| 2002 | 2003 |
}); |
| 2003 | 2004 |
} |
| 2004 | 2005 |
|
| 2005 |
- otpDigits[0].focus(); |
|
| 2006 |
+ (otpAutofill || otpDigits[0]).focus(); |
|
| 2006 | 2007 |
|
| 2007 | 2008 |
otpDigits.forEach((input, idx) => {
|
| 2008 | 2009 |
input.addEventListener('keydown', (e) => {
|
@@ -2066,13 +2067,15 @@ sub app_html {
|
||
| 2066 | 2067 |
otpDigits.forEach(i => { i.value = ''; i.classList.remove('filled'); });
|
| 2067 | 2068 |
if (otpAutofill) otpAutofill.value = ''; |
| 2068 | 2069 |
if (otpHidden) otpHidden.value = ''; |
| 2069 |
- otpDigits[0].focus(); |
|
| 2070 |
+ (otpAutofill || otpDigits[0]).focus(); |
|
| 2070 | 2071 |
} |
| 2071 | 2072 |
|
| 2072 | 2073 |
if (otpAutofill) {
|
| 2073 | 2074 |
const handleAutofill = () => fillOtp(otpAutofill.value, 0); |
| 2074 | 2075 |
otpAutofill.addEventListener('input', handleAutofill);
|
| 2075 | 2076 |
otpAutofill.addEventListener('change', handleAutofill);
|
| 2077 |
+ otpAutofill.addEventListener('focus', () => otpDigits[0].classList.add('focus-proxy'));
|
|
| 2078 |
+ otpAutofill.addEventListener('blur', () => otpDigits[0].classList.remove('focus-proxy'));
|
|
| 2076 | 2079 |
} |
| 2077 | 2080 |
|
| 2078 | 2081 |
document.querySelectorAll('[data-page-link]').forEach(link => {
|
@@ -2108,6 +2111,9 @@ sub app_html {
|
||
| 2108 | 2111 |
$('filter').addEventListener('input', renderHosts);
|
| 2109 | 2112 |
$('new-host').addEventListener('click', newHost);
|
| 2110 | 2113 |
$('close-host-modal').addEventListener('click', requestCloseHostModal);
|
| 2114 |
+ $('host-modal').addEventListener('click', (event) => {
|
|
| 2115 |
+ if (event.target === $('host-modal') && !$('save-host').disabled) closeHostModal();
|
|
| 2116 |
+ }); |
|
| 2111 | 2117 |
window.addEventListener('keydown', (event) => {
|
| 2112 | 2118 |
if (event.key === 'Escape' && !$('host-modal').hidden) requestCloseHostModal();
|
| 2113 | 2119 |
}); |