Przeglądaj źródła

Avoid Safari Keychain/password-manager prompts on code entry fields

Switch code inputs from type=password to type=text with
inputmode=numeric + digit pattern, drop autocomplete=one-time-code
(which opts into iOS SMS-code autofill) in favor of autocomplete=off,
and rename every id/label/placeholder away from "PIN"/"password" so
browsers and password managers stop treating these as login fields.
ZiB 3 miesięcy temu
rodzic
commit
b4945b180c

+ 25 - 25
custom_components/zlock_slots/frontend/zlock-slots-card.js

@@ -97,7 +97,7 @@ class ZLockSlotsCard extends HTMLElement {
         .form-row-inline { display: flex; gap: 10px; margin-bottom: 10px; }
         .form-row-inline .field { display: flex; flex-direction: column; gap: 4px; }
         .form-row-inline .field.slot { width: calc(3ch + 20px); }
-        .form-row-inline .field.pin { width: 130px; }
+        .form-row-inline .field.code { width: 130px; }
         .form-row-inline button.action { align-self: flex-end; }
         label { font-size: 0.8rem; opacity: 0.7; }
         input { padding: 6px 8px; border: 1px solid var(--divider-color, #ccc); border-radius: 4px; font-size: 0.9rem; background: var(--card-background-color, #fff); color: var(--primary-text-color); width: 100%; box-sizing: border-box; }
@@ -107,9 +107,9 @@ class ZLockSlotsCard extends HTMLElement {
         .empty-note { opacity: 0.5; font-size: 0.85rem; padding: 8px 0; }
         .inline-edit { display: flex; gap: 6px; align-items: center; }
         .inline-edit input { flex: 1; }
-        .pin-edit-row td { padding: 0 8px 6px; }
-        .pin-edit-row .pin-edit { display: flex; gap: 8px; align-items: center; }
-        .pin-edit-row label { font-size: 0.8rem; opacity: 0.7; white-space: nowrap; }
+        .code-edit-row td { padding: 0 8px 6px; }
+        .code-edit-row .code-edit { display: flex; gap: 8px; align-items: center; }
+        .code-edit-row label { font-size: 0.8rem; opacity: 0.7; white-space: nowrap; }
         .name-display { cursor: text; }
       </style>
       <ha-card${this._config.title ? ` header="${escapeHtml(this._config.title)}"` : ""}>
@@ -136,7 +136,7 @@ class ZLockSlotsCard extends HTMLElement {
                           <button class="icon-btn status-icon ${s.status}" data-action="toggle-enabled" data-slot="${s.slot_num}" data-enabled="${s.status === "enabled"}" title="${s.status === "enabled" ? "Disable slot" : "Enable slot"}">
                             <ha-icon icon="${s.status === "enabled" ? "mdi:account" : "mdi:account-off"}"></ha-icon>
                           </button>
-                          <button class="icon-btn" data-action="edit-pin" data-slot="${s.slot_num}" title="Change PIN">
+                          <button class="icon-btn" data-action="edit-code" data-slot="${s.slot_num}" title="Change code">
                             <ha-icon icon="mdi:account-edit"></ha-icon>
                           </button>
                           <button class="icon-btn clear-btn" data-action="clear" data-slot="${s.slot_num}" title="Clear slot ${s.slot_num}">
@@ -145,11 +145,11 @@ class ZLockSlotsCard extends HTMLElement {
                         </span>
                       </td>
                     </tr>
-                    <tr class="pin-edit-row" id="pin-edit-row-${s.slot_num}" style="display:none">
+                    <tr class="code-edit-row" id="code-edit-row-${s.slot_num}" style="display:none">
                       <td colspan="3">
-                        <div class="pin-edit">
-                          <label>New PIN</label>
-                          <input id="pin-input-${s.slot_num}" type="password" inputmode="numeric" pattern="[0-9]*" minlength="4" maxlength="8" autocomplete="one-time-code" placeholder="4–8 digits" data-slot="${s.slot_num}" data-type="pin" />
+                        <div class="code-edit">
+                          <label>New code</label>
+                          <input id="code-input-${s.slot_num}" type="text" inputmode="numeric" pattern="[0-9]*" minlength="4" maxlength="8" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" data-lpignore="true" data-1p-ignore="true" placeholder="4–8 digits" data-slot="${s.slot_num}" data-type="code" />
                         </div>
                       </td>
                     </tr>
@@ -165,9 +165,9 @@ class ZLockSlotsCard extends HTMLElement {
               <label>Slot #</label>
               <input id="add-slot" type="number" min="1" max="999" placeholder="auto" />
             </div>
-            <div class="field pin">
-              <label>PIN</label>
-              <input id="add-code" type="password" inputmode="numeric" pattern="[0-9]*" minlength="4" maxlength="8" autocomplete="one-time-code" placeholder="4–8 digits" />
+            <div class="field code">
+              <label>Code</label>
+              <input id="add-code" type="text" inputmode="numeric" pattern="[0-9]*" minlength="4" maxlength="8" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" data-lpignore="true" data-1p-ignore="true" placeholder="4–8 digits" />
             </div>
             <button class="action" id="add-btn">Set</button>
           </div>
@@ -210,9 +210,9 @@ class ZLockSlotsCard extends HTMLElement {
           input.focus();
           input.select();
 
-        } else if (action === "edit-pin") {
-          const row = root.getElementById(`pin-edit-row-${slot}`);
-          const input = root.getElementById(`pin-input-${slot}`);
+        } else if (action === "edit-code") {
+          const row = root.getElementById(`code-edit-row-${slot}`);
+          const input = root.getElementById(`code-input-${slot}`);
           row.style.display = "";
           input.value = "";
           input.focus();
@@ -225,8 +225,8 @@ class ZLockSlotsCard extends HTMLElement {
       root.getElementById(`name-edit-${slot}`).style.display = "none";
     };
 
-    const closePin = (slot) => {
-      root.getElementById(`pin-edit-row-${slot}`).style.display = "none";
+    const closeCode = (slot) => {
+      root.getElementById(`code-edit-row-${slot}`).style.display = "none";
     };
 
     const digitsOnly = (input) => {
@@ -238,17 +238,17 @@ class ZLockSlotsCard extends HTMLElement {
 
     root.querySelectorAll("input[data-slot]").forEach(input => {
       const slot = parseInt(input.dataset.slot, 10);
-      const isPin = input.dataset.type === "pin";
-      if (isPin) digitsOnly(input);
+      const isCode = input.dataset.type === "code";
+      if (isCode) digitsOnly(input);
 
       input.addEventListener("keydown", async (e) => {
         if (e.key === "Enter") {
           e.preventDefault();
           input.removeEventListener("blur", input._blurHandler);
-          if (isPin) {
+          if (isCode) {
             const code = input.value;
             if (code.length < 4 || code.length > 8) return;
-            closePin(slot);
+            closeCode(slot);
             await this._callService("set_code", { ...target, slot, code });
           } else {
             const name = input.value.trim();
@@ -257,14 +257,14 @@ class ZLockSlotsCard extends HTMLElement {
           }
         } else if (e.key === "Escape") {
           input.removeEventListener("blur", input._blurHandler);
-          isPin ? closePin(slot) : closeName(slot);
+          isCode ? closeCode(slot) : closeName(slot);
         }
       });
 
       input._blurHandler = async () => {
-        if (isPin) {
+        if (isCode) {
           const code = input.value;
-          closePin(slot);
+          closeCode(slot);
           if (code.length >= 4 && code.length <= 8) {
             await this._callService("set_code", { ...target, slot, code });
           }
@@ -290,7 +290,7 @@ class ZLockSlotsCard extends HTMLElement {
       const code = codeInput.value.trim();
 
       if (slotVal !== undefined && slotVal < 1) { status.textContent = "Slot must be 1 or higher."; return; }
-      if (code.length < 4 || code.length > 8) { status.textContent = "PIN must be 4–8 digits."; return; }
+      if (code.length < 4 || code.length > 8) { status.textContent = "Code must be 4–8 digits."; return; }
 
       status.textContent = "";
       try {