Compare commits

..

4 Commits

Author SHA1 Message Date
r-zakarya 092eb2f73a feat(fillbar): bouton Paste qui tape le presse-papiers Windows
Pour les clients a distance qui ne transmettent pas le presse-papiers
(TigerVNC vers un Mac) : le texte est tape par le chemin de l'autofill,
touches VNC et dispositions comprises, insere au curseur. Et l'arabe des
champs a chasse fixe s'affiche a la bonne taille (repli Segoe UI / Tahoma).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:53:09 +01:00
r-zakarya bf7a8f4d38 fix(autofill): texte arabe + latin tape en entier vers VNC
Un caractere sans touche dans la disposition courante est cherche dans les
autres dispositions installees ; la fenetre VNC y bascule le temps de le
taper, puis la disposition d'origine est rendue. Eprouve vers un Mac, clavier
francais comme arabe actif.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:55:09 +01:00
r-zakarya 9999dd113d fix(autofill): AltGr et caracteres sans touche vers VNC
AltGr envoye en Ctrl gauche + Alt droit etendu, avec scancodes : "@" et
"#" en AZERTY arrivaient sans AltGr. Un caractere sans touche dans la
disposition locale est saute et compte dans le journal (copier a la place).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:45:33 +01:00
r-zakarya a3e63ee8f1 fix(autofill): vraies touches vers un visualiseur VNC
VK_PACKET (texte Unicode) n'est pas retransmis par TigerVNC : le Mac
distant recevait un V par caractere. Cible VNC -> touches du clavier via
VkKeyScanEx dans la disposition de la cible, modificateurs compris.
Les autres fenetres gardent l'envoi Unicode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:47 +01:00
4 changed files with 276 additions and 21 deletions
+33
View File
@@ -316,6 +316,16 @@ Détails :
Le cadre est peint dans `WM_ERASEBKGND` plutôt que par `WS_BORDER`, qui se
sert dans les couleurs système.
**Bouton Paste (2026-10-14)** : `LAZY_ID_PASTE` / cle `paste` (desactive par
defaut) → `PasteClipboardAsKeystrokes` lit le presse-papiers **Windows**
(`CF_UNICODETEXT`) et le **tape** dans la fenetre en avant par
`ExecuteAutofill(..., AClearFirst=False)` - donc vraies touches et bascule de
disposition vers un client VNC (TigerVNC vers un Mac ne transmet pas le
presse-papiers). Insere au curseur, saut de ligne final retire (un terminal
lancerait la commande), sauts internes tapes en Entree. Marche coffre ferme :
rien ne passe par la page. Pas de texte (image, fichier) → bulle « No text in
the clipboard ».
### Réglages de la barrette (2026-09-18)
**Settings → onglet Fill bar**, et le **clic droit** sur la barrette pour les
@@ -1443,6 +1453,25 @@ d'accord.
### Contraintes connues autofill
- **VNC (2026-10-14)** : les caracteres partent en `KEYEVENTF_UNICODE`
(`VK_PACKET`), que TigerVNC & co ne savent pas retransmettre - un Mac
recevait un `V` par caractere. Cible dont l'exe contient « vnc »
(`TargetTypesByKeyCodes`) → **vraies touches** (`SendKeyCodeString` :
`VkKeyScanEx` dans la disposition de la cible, + Maj/Ctrl/Alt, AltGr =
Ctrl+Alt). **AltGr = `VK_LCONTROL` + `VK_RMENU` etendu**, comme le vrai
clavier : un Ctrl+Alt generique etait lu par TigerVNC comme deux
modificateurs, et `@` (AltGr+0 en AZERTY) arrivait en `a`. Chaque touche porte
aussi son scancode (`MapVirtualKeyEx`). **Texte melange (arabe + latin)** :
un caractere sans touche dans la disposition courante est cherche dans les
autres dispositions installees (`GetKeyboardLayoutList`) ; la fenetre cible
y est basculee (`WM_INPUTLANGCHANGEREQUEST`, attente que son fil l'ait
prise - TigerVNC traduit chaque touche avec la disposition de SON fil), et
la disposition d'origine est rendue a la fin. Seul un caractere qu'aucune
disposition installee ne sait taper est **saute et compte** - journal
`N character(s) have no key on this keyboard and were NOT typed - copy
instead`. Eprouve vers un Mac : `تاسيب... jhbs... @ #` complet, clavier
francais comme arabe actif.
- **UIPI** : SendInput vers process élevé bloqué. Notepad++ admin,
regedit → autofill silencieusement no-op. Tester avec apps non-élevées.
- **Chrome password manager** : si Chrome a des credentials sauvegardés
@@ -1593,6 +1622,10 @@ après chaque `DELETE /attachments/{id}`. Un petit vault sain ne paie rien.
texte = fin de la commande ; un `#` apres une continuation reste du texte.
Limite : un chemin Windows fini par `\` en fin de ligne passe pour une
continuation.
- **Polices a chasse fixe et arabe** (2026-10-14) : aucune n'a l'arabe, et
Windows empruntait une police plus petite. Chaque pile `…, monospace` de
`css/style.css` finit par `'Segoe UI', Tahoma, monospace` : le latin reste a
chasse fixe, l'arabe prend une taille comparable.
- **Settings search** : `#settingsSearch` filtre les `.setting-row` par
texte ; match sur le label de section garde toute la section. Esc dans
la search avec query = clear ; Esc vide = ferme Settings.
+21 -17
View File
@@ -17,10 +17,14 @@ body {
overflow: hidden;
}
/* Les polices a chasse fixe n'ont pas l'arabe : Windows empruntait une police
au hasard, plus petite. 'Segoe UI' / Tahoma, places avant 'monospace' dans
chaque pile, prennent ces caracteres a une taille comparable ; le latin reste
dans la police a chasse fixe. */
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: inherit; }
kbd {
font-family: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
font-family: 'JetBrains Mono', 'SF Mono', Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
padding: 2px 6px;
border: 1px solid var(--border);
@@ -248,7 +252,7 @@ input[type="range"]::-webkit-slider-thumb {
.licence-machine code {
display: inline-block; padding: 6px 10px;
background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
font-family: ui-monospace, "Cascadia Mono", Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 14px; letter-spacing: 1px; color: var(--text);
user-select: all;
}
@@ -1216,7 +1220,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.entry-pw {
flex: 1;
font-family: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
font-family: 'JetBrains Mono', 'SF Mono', Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 12px;
color: var(--text-dim);
overflow: hidden;
@@ -1268,7 +1272,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.totp-code {
flex: 1;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 22px;
letter-spacing: 2px;
color: var(--accent);
@@ -1330,7 +1334,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.auth-card-code {
flex: 1;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 26px;
letter-spacing: 3px;
color: var(--accent);
@@ -1386,7 +1390,7 @@ input[type="range"]::-webkit-slider-thumb {
.health-score.is-danger { border-color: #ef4444; color: #ef4444; }
.health-score-num {
font-size: 36px; font-weight: 700;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
}
.health-score-lbl {
font-size: 11px; font-weight: 600;
@@ -1417,7 +1421,7 @@ input[type="range"]::-webkit-slider-thumb {
.health-bd-n { text-align: right; font-variant-numeric: tabular-nums; }
.health-bd-pts {
text-align: right;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
color: var(--text-faint);
}
/* Nothing wrong in this category — dim it so the eye goes to what needs work. */
@@ -1572,7 +1576,7 @@ input[type="range"]::-webkit-slider-thumb {
.history-date {
font-size: 11px;
color: var(--text-dim);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
}
.history-preview {
display: flex; align-items: flex-start; gap: 6px;
@@ -1581,7 +1585,7 @@ input[type="range"]::-webkit-slider-thumb {
.history-value {
flex: 1 1 0;
min-width: 0;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 12px;
color: var(--text);
/* Long revealed passwords wrap to multiple lines (no ellipsis) so
@@ -1634,7 +1638,7 @@ input[type="range"]::-webkit-slider-thumb {
flex-wrap: wrap;
}
.cheatsheet-keys kbd {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
padding: 2px 7px;
background: var(--bg);
@@ -1814,7 +1818,7 @@ input[type="range"]::-webkit-slider-thumb {
.audit-row:hover { background: var(--bg); }
.audit-date {
color: var(--text-dim);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
white-space: nowrap;
}
.audit-action {
@@ -1823,7 +1827,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.audit-ip {
color: var(--text-faint);
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
text-align: right;
}
@@ -1969,7 +1973,7 @@ input[type="range"]::-webkit-slider-thumb {
.so-note-body {
min-height: 220px;
resize: vertical;
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 13px;
line-height: 1.55;
white-space: pre-wrap;
@@ -1987,7 +1991,7 @@ input[type="range"]::-webkit-slider-thumb {
.so-cmds-cmd { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.so-cmds-empty { font-size: 12px; color: var(--text-dim); }
#qsBack { flex-shrink: 0; }
.qs-cmd { font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
.qs-cmd { font-family: ui-monospace, Consolas, 'Segoe UI', Tahoma, monospace; font-size: 13px; }
/* ---- + New dropdown ---------------------------------- */
.new-entry-wrap {
@@ -2543,7 +2547,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-family: 'JetBrains Mono', monospace;
font-family: 'JetBrains Mono', 'Segoe UI', Tahoma, monospace;
font-size: 13px;
word-break: break-all;
margin-bottom: 16px;
@@ -2780,7 +2784,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
/* ---- Hotkey capture buttons (Settings → Autofill) ---- */
.hotkey-capture-btn {
font-family: ui-monospace, "Cascadia Code", Menlo, monospace;
font-family: ui-monospace, "Cascadia Code", Menlo, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
min-width: 140px;
text-align: center;
@@ -3240,7 +3244,7 @@ input[type="password"]::-ms-clear {
margin-bottom: 28px;
}
.prc-code {
font-family: 'Courier New', monospace;
font-family: 'Courier New', 'Segoe UI', Tahoma, monospace;
font-size: 32px;
letter-spacing: 6px;
text-align: center;
+221 -4
View File
@@ -183,6 +183,7 @@ type
// les declenche aussi, meme capture de la fenetre cible.
procedure FireNewEntryRequest;
procedure FireQuickSearchRequest;
procedure PasteClipboardAsKeystrokes;
/// Pose l'opacite qui convient a l'etat de survol donne.
procedure ApplyLazyAlpha(AHover: Boolean);
/// Menu du clic droit sur la barrette : le choix de l'opacite au repos.
@@ -450,6 +451,7 @@ const
LAZY_ID_ADD = 106; // nouvelle entree depuis la fenetre en avant (Ctrl+Shift+A)
LAZY_ID_SEARCH = 107; // recherche rapide (Ctrl+Shift+Q)
LAZY_ID_SETTINGS = 108; // reglages, onglet Fill bar
LAZY_ID_PASTE = 109; // tape le presse-papiers Windows dans la fenetre en avant
LAZY_MENU_ICONS = 1; // hors des valeurs d'opacite (25..100) du meme menu
LAZY_MENU_SETTINGS = 2;
LAZY_MENU_CLEAR = 3;
@@ -1116,6 +1118,59 @@ end;
// The target of a fill is whatever window owns the foreground at the moment
// the user asks for it - a shortcut, or a click on the lazy strip, which takes
// no foreground precisely so this stays true.
/// COLLER EN TAPANT. Certains clients a distance (TigerVNC vers un Mac) ne
/// transmettent pas le presse-papiers Windows : Ctrl+V n'y colle rien. Le texte
/// est donc TAPE dans la fenetre en avant, par le meme chemin que l'autofill -
/// vraies touches pour un client VNC, dispositions basculees au besoin. Le
/// presse-papiers est celui de Windows : coffre ouvert ou non, rien ne passe
/// par la page. Pas de Ctrl+A + Suppr avant : on insere au curseur, comme un
/// collage.
procedure TPMBridge.PasteClipboardAsKeystrokes;
var
LTarget: HWND;
LData: THandle;
LPtr: PWideChar;
LText: string;
begin
LTarget := GetForegroundWindow;
if IsOwnProcessWindow(LTarget) then
begin
ShowBalloon('Nothing to paste into',
'Click where the text should go first, then press Paste.', True);
Exit;
end;
LText := '';
if OpenClipboard(0) then
try
LData := GetClipboardData(CF_UNICODETEXT);
if LData <> 0 then
begin
LPtr := GlobalLock(LData);
if LPtr <> nil then
try
LText := LPtr;
finally
GlobalUnlock(LData);
end;
end;
finally
CloseClipboard;
end;
// Un retour a la ligne final, tape dans un terminal, vaudrait Entree.
// Les sauts de ligne internes partent en #13 : Entree, comme un collage.
LText := LText.Replace(#13#10, #10).Replace(#13, #10).TrimRight([#10])
.Replace(#10, #13);
if LText = '' then
begin
// Une image ou un fichier copies ne sont pas du texte : le dire tel quel.
ShowBalloon('No text in the clipboard', 'Copy some text first, then press Paste.', True);
Exit;
end;
if not ExecuteAutofill(LTarget, '', LText, False, False, False) then
ShowBalloon('Paste blocked',
'The window did not accept the keystrokes (it may run as administrator).', True);
end;
procedure TPMBridge.FireAutofillRequest(AKind: TAutofillKind;
AFromBar: Boolean = False);
var
@@ -1302,6 +1357,7 @@ begin
LAZY_ID_SEARCH: LGlyph := #$E721; // Search
LAZY_ID_OPEN: LGlyph := #$E8A7; // OpenInNewWindow
LAZY_ID_SETTINGS: LGlyph := #$E713; // Settings (roue)
LAZY_ID_PASTE: LGlyph := #$E77F; // Paste
else
LGlyph := #$E711; // Cancel (X)
end;
@@ -1505,6 +1561,7 @@ begin
LAZY_ID_OPEN: if Assigned(FOnTrayRestore) then FOnTrayRestore();
LAZY_ID_ADD: FireNewEntryRequest;
LAZY_ID_SEARCH: FireQuickSearchRequest;
LAZY_ID_PASTE: PasteClipboardAsKeystrokes;
LAZY_ID_SETTINGS:
begin
if Assigned(FOnTrayRestore) then FOnTrayRestore();
@@ -1669,6 +1726,7 @@ begin
AddButton('full', LAZY_ID_FULL, 'Full', 'Fill username and password', 56);
AddButton('pwd', LAZY_ID_PWD, 'Password', 'Fill the password only', 76);
AddButton('totp', LAZY_ID_TOTP, '2FA', 'Fill the 2FA code', 52);
AddButton('paste', LAZY_ID_PASTE, 'Paste', 'Type the Windows clipboard into the current window', 52);
AddButton('add', LAZY_ID_ADD, 'Add', 'New entry from the current window', 48);
AddButton('search', LAZY_ID_SEARCH, 'Search', 'Quick search', 60);
// Meme raison que le X : quand la barrette est allumee, elle est souvent la
@@ -2076,6 +2134,157 @@ begin
SendInput(LCount, @LInputs[0], SizeOf(TInput));
end;
function QueryFullProcessImageNameW(hProcess: THandle; dwFlags: DWORD;
lpExeName: PWideChar; var lpdwSize: DWORD): BOOL; stdcall;
external kernel32 name 'QueryFullProcessImageNameW';
/// UN VISUALISEUR VNC NE SAIT PAS TAPER DE L'UNICODE. SendUnicodeString envoie
/// chaque caractere en VK_PACKET ; une fenetre Windows le comprend, mais VNC
/// (TigerVNC, RealVNC, TightVNC, UltraVNC) traduit les VRAIES touches vers la
/// machine distante et ne sait que faire de VK_PACKET : un Mac recevait
/// "VVVVVV", un V par caractere. Pour lui on tape les touches du clavier.
/// ponytail: reconnu au nom de l'exe ("vnc") ; un autre client a distance qui
/// aurait le meme defaut s'ajouterait ici, ou deviendrait un reglage.
function TargetTypesByKeyCodes(AHwnd: HWND): Boolean;
const
PROCESS_QUERY_LIMITED_INFORMATION = $1000;
var
LPid, LLen: DWORD;
LProc: THandle;
LName: array[0..MAX_PATH] of WideChar;
begin
Result := False;
LPid := 0;
GetWindowThreadProcessId(AHwnd, LPid);
if LPid = 0 then Exit;
LProc := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, LPid);
if LProc = 0 then Exit;
try
LLen := Length(LName);
if QueryFullProcessImageNameW(LProc, 0, @LName[0], LLen) then
Result := ExtractFileName(string(LName)).ToLower.Contains('vnc');
finally
CloseHandle(LProc);
end;
end;
/// Le texte en VRAIES touches, dans la disposition clavier de la cible : chaque
/// caractere devient sa touche plus Maj / Ctrl / Alt selon VkKeyScanEx, avec
/// son scancode (certains clients VNC lisent la position physique).
/// ALTGR = Ctrl GAUCHE + Alt DROIT (touche etendue), comme le clavier le
/// produit : un Ctrl+Alt generique etait lu par TigerVNC comme deux
/// modificateurs ordinaires, et "@" (AltGr+0 en AZERTY) arrivait en "a".
/// TEXTE MELANGE (arabe + latin) : un caractere sans touche dans la
/// disposition courante est cherche dans les AUTRES dispositions installees
/// sur le poste ; on y bascule la fenetre cible (TigerVNC traduit chaque touche
/// avec la disposition de SON fil), on tape, et la disposition d'origine est
/// rendue a la fin. Seul un caractere qu'aucune disposition installee ne sait
/// taper est saute et compte - la copie, que TigerVNC synchronise, reste alors
/// le recours. Rend le nombre de sautes.
function SwitchTargetLayout(ATarget: HWND; ALayout: HKL): Boolean;
var
LThread: DWORD;
begin
LThread := GetWindowThreadProcessId(ATarget, nil);
PostMessage(ATarget, WM_INPUTLANGCHANGEREQUEST, 0, LPARAM(ALayout));
// La bascule est asynchrone : on attend qu'elle soit faite avant de taper,
// sinon les premieres touches partiraient encore dans l'ancienne.
for var I := 1 to 30 do
begin
if GetKeyboardLayout(LThread) = ALayout then Exit(True);
Sleep(10);
end;
Result := GetKeyboardLayout(LThread) = ALayout;
end;
function LayoutForChar(ACh: WideChar; ACurrent: HKL): HKL;
var
LList: array[0..31] of HKL;
N, I: Integer;
begin
Result := 0;
N := GetKeyboardLayoutList(Length(LList), LList);
for I := 0 to N - 1 do
if (LList[I] <> ACurrent) and (VkKeyScanEx(ACh, LList[I]) <> -1) then
Exit(LList[I]);
end;
function SendKeyCodeString(const AText: string; ATarget: HWND; ALayout: HKL): Integer;
var
LInputs: TArray<TInput>;
LCount: Integer;
procedure Key(AVk: Word; AUp: Boolean; AExtended: Boolean = False);
begin
if LCount >= Length(LInputs) then SetLength(LInputs, LCount + 16);
FillChar(LInputs[LCount], SizeOf(TInput), 0);
LInputs[LCount].Itype := INPUT_KEYBOARD;
LInputs[LCount].ki.wVk := AVk;
LInputs[LCount].ki.wScan := MapVirtualKeyEx(AVk, 0 {MAPVK_VK_TO_VSC}, ALayout);
if AUp then LInputs[LCount].ki.dwFlags := KEYEVENTF_KEYUP;
if AExtended then
LInputs[LCount].ki.dwFlags := LInputs[LCount].ki.dwFlags or KEYEVENTF_EXTENDEDKEY;
Inc(LCount);
end;
var
I: Integer;
LScan: SHORT;
LVk, LMods: Byte;
LShift, LCtrl, LAlt: Boolean;
begin
Result := 0;
var LOriginal := ALayout;
try
for I := 1 to Length(AText) do
begin
LScan := VkKeyScanEx(AText[I], ALayout);
if LScan = -1 then
begin
var LOther := LayoutForChar(AText[I], ALayout);
if (LOther <> 0) and SwitchTargetLayout(ATarget, LOther) then
begin
ALayout := LOther;
LScan := VkKeyScanEx(AText[I], ALayout);
end;
end;
if LScan = -1 then
begin
Inc(Result);
Continue;
end;
LVk := Lo(Word(LScan));
LMods := Hi(Word(LScan));
LShift := LMods and 1 <> 0;
LCtrl := LMods and 2 <> 0;
LAlt := LMods and 4 <> 0;
LCount := 0;
if LShift then Key(VK_LSHIFT, False);
if LCtrl then Key(VK_LCONTROL, False);
// Alt seul = Alt gauche ; avec Ctrl, c'est AltGr = Alt droit, etendu.
if LAlt and LCtrl then Key(VK_RMENU, False, True)
else if LAlt then Key(VK_LMENU, False);
Key(LVk, False);
Key(LVk, True);
if LAlt and LCtrl then Key(VK_RMENU, True, True)
else if LAlt then Key(VK_LMENU, True);
if LCtrl then Key(VK_LCONTROL, True);
if LShift then Key(VK_LSHIFT, True);
SendInput(LCount, @LInputs[0], SizeOf(TInput));
end;
finally
// Rendre a l'utilisateur la disposition qu'il avait choisie.
if ALayout <> LOriginal then SwitchTargetLayout(ATarget, LOriginal);
end;
end;
/// Rend le nombre de caracteres qui n'ont pas pu etre tapes (VNC seulement).
function SendTextTo(const AText: string; AByKeys: Boolean; ATarget: HWND;
ALayout: HKL): Integer;
begin
Result := 0;
if AByKeys then Result := SendKeyCodeString(AText, ATarget, ALayout)
else SendUnicodeString(AText);
end;
// Send one virtual-key press (down+up).
procedure SendVKey(AVk: Word);
var
@@ -2290,8 +2499,12 @@ begin
LSw := TStopwatch.StartNew;
WaitForModifierRelease(1000);
ForceReleaseModifiers; // timeout hit with keys still down → clean state
var LByKeys := (ATargetHWND <> 0) and TargetTypesByKeyCodes(ATargetHWND);
var LLayout := GetKeyboardLayout(GetWindowThreadProcessId(ATargetHWND, nil));
FLastFillWaits := Format('foreground=%d ms, modifiers=%d ms',
[LFgMs, LSw.ElapsedMilliseconds]);
if LByKeys then FLastFillWaits := FLastFillWaits + ', typed as key codes (VNC)';
var LMissing := 0;
Sleep(FocusSettleDelayMs);
// Never type into our own window: if the target refused the foreground,
@@ -2315,19 +2528,19 @@ begin
if AUsernameOnly then
begin
ClearFieldIfWanted;
SendUnicodeString(AUsername);
Inc(LMissing, SendTextTo(AUsername, LByKeys, ATargetHWND, LLayout));
Exit;
end;
if AUsername = '' then
begin
ClearFieldIfWanted;
SendUnicodeString(APassword);
Inc(LMissing, SendTextTo(APassword, LByKeys, ATargetHWND, LLayout));
Exit;
end;
ClearFieldIfWanted;
SendUnicodeString(AUsername);
Inc(LMissing, SendTextTo(AUsername, LByKeys, ATargetHWND, LLayout));
Sleep(200);
// Real-VK send: a physically held Shift here turns Tab into Shift+Tab —
// focus moves BACKWARD and the password lands in the username field.
@@ -2339,8 +2552,12 @@ begin
// Tab lands somewhere that is not an editable field (a link, a reveal
// button, the document itself, as on the Reddit login form) it selects the
// ENTIRE PAGE, and the password is then typed into nothing.
SendUnicodeString(APassword);
Inc(LMissing, SendTextTo(APassword, LByKeys, ATargetHWND, LLayout));
finally
if LMissing > 0 then
FLastFillWaits := FLastFillWaits + Format(
', %d character(s) have no key on this keyboard and were NOT typed - copy instead',
[LMissing]);
// Re-register with the LIVE combo (user may have rebound it), not the
// hardcoded default — otherwise a fill would reset new-entry to Ctrl+Shift+A.
if FNewEntryHotkeyRegistered then
+1
View File
@@ -768,6 +768,7 @@
<label><input type="checkbox" class="fb-btn" data-key="full"> Full</label>
<label><input type="checkbox" class="fb-btn" data-key="pwd"> Password</label>
<label><input type="checkbox" class="fb-btn" data-key="totp"> 2FA</label>
<label><input type="checkbox" class="fb-btn" data-key="paste"> Paste (types the clipboard)</label>
<label><input type="checkbox" class="fb-btn" data-key="add"> Add current window</label>
<label><input type="checkbox" class="fb-btn" data-key="search"> Search</label>
<label><input type="checkbox" class="fb-btn" data-key="open"> Open</label>