Showing 1 changed files with 24 additions and 12 deletions
+24 -12
scripts/host_manager.pl
@@ -1277,6 +1277,9 @@ sub app_html {
1277 1277
       padding-bottom: 0;
1278 1278
     }
1279 1279
     .login-card form.busy { opacity: .72; pointer-events: none; }
1280
+    /* Off-screen helper fields keep the visible UI to the 6 OTP boxes while still
1281
+       giving the password manager a username anchor and an aggregated OTP target
1282
+       (see development-log: "Password-Manager-Friendly Form Shape"). */
1280 1283
     .pm-helper-fields {
1281 1284
       position: absolute;
1282 1285
       left: -10000px;
@@ -1292,8 +1295,9 @@ sub app_html {
1292 1295
       padding: 0;
1293 1296
       border: 0;
1294 1297
     }
1295
-    /* 6 separate OTP digit boxes — Safari detects the group and anchors
1296
-       its OTP autofill to the first box (same pattern as pbx-mgmt). */
1298
+    /* 6 separate OTP digit boxes. No autocomplete="one-time-code" on them: that
1299
+       hint was what made Safari mark the whole group and re-present its OTP
1300
+       autofill on every focused box. Without it, the banner stays on the first. */
1297 1301
     .otp-row {
1298 1302
       display: flex;
1299 1303
       gap: var(--otp-gap);
@@ -1305,6 +1309,7 @@ sub app_html {
1305 1309
       background: #f8fafc; caret-color: transparent; outline: none;
1306 1310
       transition: border-color .15s, background .15s;
1307 1311
     }
1312
+    .otp-row input:first-child { border-color: var(--accent); }
1308 1313
     .otp-row input:focus { border-color: var(--accent); background: #fff; }
1309 1314
     .otp-row input.filled { border-color: #b3c6f0; background: #fff; }
1310 1315
     #login-error {
@@ -1496,12 +1501,12 @@ sub app_html {
1496 1501
           <input type="hidden" id="otp-hidden" name="otp">
1497 1502
         </div>
1498 1503
         <div class="otp-row">
1499
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" aria-label="Digit 1">
1500
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" aria-label="Digit 2">
1501
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" aria-label="Digit 3">
1502
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" aria-label="Digit 4">
1503
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" aria-label="Digit 5">
1504
-          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" aria-label="Digit 6">
1504
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 1">
1505
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 2">
1506
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 3">
1507
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 4">
1508
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 5">
1509
+          <input type="text" class="otp-digit" maxlength="1" inputmode="numeric" autocomplete="off" required aria-label="Digit 6">
1505 1510
         </div>
1506 1511
       </form>
1507 1512
       <div id="login-error"></div>
@@ -2044,7 +2049,10 @@ sub app_html {
2044 2049
     function clearOtp() {
2045 2050
       otpDigits.forEach(i => { i.value = ''; i.classList.remove('filled'); });
2046 2051
       if (otpHidden) otpHidden.value = '';
2047
-      otpDigits[0].focus();
2052
+      // Same conditional focus as on load: don't steal focus to the OTP boxes for
2053
+      // an unknown operator, so Safari's autofill anchor on the username stays.
2054
+      if (loginAccount && !loginAccount.value) loginAccount.focus();
2055
+      else otpDigits[0].focus();
2048 2056
     }
2049 2057
 
2050 2058
     otpDigits.forEach((input, idx) => {
@@ -2082,9 +2090,13 @@ sub app_html {
2082 2090
       });
2083 2091
     });
2084 2092
 
2085
-    // Focus the first box: it carries autocomplete="one-time-code", so Safari
2086
-    // anchors its OTP autofill there instead of latching onto each box.
2087
-    otpDigits[0].focus();
2093
+    // Focus the first OTP box only for a returning operator (username known).
2094
+    // For an unknown operator, leave focus on the username field so Safari can
2095
+    // present its OTP autofill anchored there without being dismissed by a focus
2096
+    // change (pbx-admin pattern).
2097
+    if (loginAccount && loginAccount.value) otpDigits[0].focus();
2098
+    else if (loginAccount) loginAccount.focus();
2099
+    else otpDigits[0].focus();
2088 2100
 
2089 2101
     document.querySelectorAll('[data-page-link]').forEach(link => {
2090 2102
       link.addEventListener('click', (event) => {