fix(autofill): kill residual hotkey chord; dedicated fail-balloon setting

Residual new-entry trigger (1 in 6): password chars go out as
KEYEVENTF_UNICODE (VK_PACKET, can't match a hotkey) — the real chord risk is
the Ctrl+A clear-field, which sends a real VK_A. If the user re-presses
Ctrl+Shift mid-sequence, that VK_A becomes physical Ctrl+Shift+A = our own
new-entry hotkey. ForceReleaseModifiers now runs inside
SendSelectAllAndDelete, at the risky instant, not just once up front.

Balloon: was gated by "Show tray notifications" (OFF for this user) — now
gated by its own synced setting "Tray alert when autofill is blocked"
(autofillFailBalloon, Settings > Autofill, default ON), carried as notify=0
on cmd://autofill/execute. ShowBalloon no longer gates internally; each
caller applies its own setting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-12 05:23:02 +01:00
parent 9a72fc0424
commit 72dcc3dd82
4 changed files with 48 additions and 10 deletions
+14 -1
View File
@@ -142,7 +142,8 @@ const Bridge = (() => {
'&password=' + encodeURIComponent(password) +
(hideAfter ? '&hide_after=1' : '') +
(field === 'user' ? '&field=user' : '') +
(state.autofillClearField ? '' : '&clear=0'));
(state.autofillClearField ? '' : '&clear=0') +
(state.autofillFailBalloon ? '' : '&notify=0'));
},
// Ask Delphi to bring the main window to front (used when the
@@ -645,6 +646,7 @@ const state = {
recoveryConfigured: false, // refreshed by refreshRecoveryStatus on Settings open
autofillEnabled: localStorage.getItem('autofillEnabled') !== '0', // default ON
autofillClearField: localStorage.getItem('autofillClearField') !== '0', // default ON
autofillFailBalloon: localStorage.getItem('autofillFailBalloon') !== '0', // default ON
// Hotkey combos. Each combo = { ctrl, shift, alt, win, key }.
// key is the uppercase character or VK label ('A'..'Z', '0'..'9',
// 'F1'..'F12'). Default: Ctrl+Shift+L / Ctrl+Shift+P. Combos are
@@ -6344,6 +6346,7 @@ function openSettings() {
$('#settingFaviconActionsRow').style.display = Bridge.active ? 'flex' : 'none';
$('#settingAutofill').checked = state.autofillEnabled;
$('#settingAutofillClear').checked = state.autofillClearField;
$('#settingAutofillFailBalloon').checked = state.autofillFailBalloon;
$('#settingAutofillRow').style.display = Bridge.active ? '' : 'none';
// Hotkey capture buttons — labels reflect current combos.
$('#settingAutofillFullCombo').textContent = autofillComboLabel(state.autofillHotkeyFull);
@@ -6756,6 +6759,8 @@ const SYNCED_SETTING_KEYS = [
// Send Ctrl+A + Del before typing each autofill field (default ON).
// OFF for targets where Ctrl+A isn't select-all (terminals, RDP).
'autofillClearField',
// Tray balloon when a fill fails while the window is hidden (default ON).
'autofillFailBalloon',
];
// Sets `data-editor-position` on <body> so CSS can swap the slideover
@@ -6839,6 +6844,9 @@ async function loadServerSettings() {
case 'autofillClearField':
localStorage.setItem('autofillClearField', v ? '1' : '0');
break;
case 'autofillFailBalloon':
localStorage.setItem('autofillFailBalloon', v ? '1' : '0');
break;
}
});
// Apply visual settings immediately.
@@ -7674,6 +7682,11 @@ async function init() {
localStorage.setItem('autofillClearField', e.target.checked ? '1' : '0');
saveServerSettings();
});
$('#settingAutofillFailBalloon').addEventListener('change', e => {
state.autofillFailBalloon = e.target.checked;
localStorage.setItem('autofillFailBalloon', e.target.checked ? '1' : '0');
saveServerSettings();
});
// ---- Hotkey capture buttons ----
// Click → button label becomes "Press combo…" → next keydown captures.