feat(quick-search): boutons par entree - utilisateur, mot de passe ou note, code 2FA (s'ils existent)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,21 @@ function quickSearchRender() {
|
||||
main.appendChild(el('div', { class: 'quick-search-sub' }, e.username));
|
||||
row.appendChild(avatar);
|
||||
row.appendChild(main);
|
||||
// Un bouton par valeur presente : pas besoin de retenir clic / Ctrl+clic.
|
||||
const verb = quickSearchFillMode ? 'Fill ' : 'Copy ';
|
||||
const acts = el('div', { class: 'quick-search-acts' });
|
||||
const act = (ic, title, mode) => {
|
||||
const b = el('button', { class: 'icon-btn icon-btn-sm', type: 'button', title });
|
||||
b.appendChild(icon(ic));
|
||||
b.addEventListener('click', ev => { ev.stopPropagation(); quickSearchPickEntry(e, mode); });
|
||||
b.addEventListener('contextmenu', ev => ev.stopPropagation());
|
||||
acts.appendChild(b);
|
||||
};
|
||||
if (e.username) act('i-user', verb + 'username', 'user');
|
||||
if (e.kind === 'note') act('i-note', 'Copy note', 'note');
|
||||
else act('i-key', verb + 'password', 'pwd');
|
||||
if (e.totp_secret && e.totp_iv) act('i-clock', verb + '2FA code', 'totp');
|
||||
row.appendChild(acts);
|
||||
// Left click → password only; Ctrl+click → username only
|
||||
// (step-2 forms / unlock screens).
|
||||
row.addEventListener('click', ev =>
|
||||
@@ -106,9 +121,42 @@ function quickSearchRender() {
|
||||
// mode: 'full' (user + Tab + password), 'user' (username only) or 'pwd'
|
||||
// (password only). In fill mode each maps to a SendInput variant; in copy
|
||||
// mode 'full' has no meaning so it falls back to copying the password.
|
||||
// 'totp' = code 2FA courant (tape comme un mot de passe, ou copie) ; 'note' =
|
||||
// corps d'une note, toujours COPIE - le taper enverrait ses retours a la ligne
|
||||
// comme des Entree dans le formulaire cible.
|
||||
async function quickSearchPickEntry(entry, mode) {
|
||||
mode = mode || 'full';
|
||||
|
||||
if (mode === 'totp') {
|
||||
const secret = await decryptTotpSecret(entry.totp_secret, entry.totp_iv);
|
||||
let code = '';
|
||||
try { if (secret && secret !== '[ERROR]') code = (await generateTOTP(secret)).code; }
|
||||
catch (_) { code = ''; }
|
||||
if (!code) { toast('2FA: cannot read the secret', 'error'); return; }
|
||||
if (quickSearchFillMode) {
|
||||
autofillPendingToast = entryDisplayName(entry) + ' · 2FA code sent';
|
||||
if (Bridge.active) Bridge.executeAutofill('', code, quickSearchHideAfter);
|
||||
quickSearchFillMode = false;
|
||||
quickSearchHideAfter = false;
|
||||
closeQuickSearchModal();
|
||||
return;
|
||||
}
|
||||
if (Bridge.active) Bridge.copySecure(code, 30000);
|
||||
else { try { await navigator.clipboard.writeText(code); } catch (_) {} }
|
||||
toast(entryDisplayName(entry) + ' · 2FA code copied');
|
||||
closeQuickSearchModal(true);
|
||||
return;
|
||||
}
|
||||
if (mode === 'note') {
|
||||
const body = await decryptPwd(entry.encrypted_password, entry.iv);
|
||||
if (body === '[ERROR]') { toast('Decryption error', 'error'); return; }
|
||||
if (Bridge.active) Bridge.copySecure(body, 30000);
|
||||
else { try { await navigator.clipboard.writeText(body); } catch (_) {} }
|
||||
toast(entryDisplayName(entry) + ' · note copied');
|
||||
closeQuickSearchModal(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill mode (Ctrl+Shift+Q hotkey): SendInput directly into the HWND
|
||||
// Delphi saved when the hotkey fired. No clipboard touch.
|
||||
if (quickSearchFillMode) {
|
||||
|
||||
Reference in New Issue
Block a user