From 7c2671683191fda3fb298dae3694aac95f3adde5 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Mon, 28 Sep 2026 12:07:57 +0100 Subject: [PATCH] feat(quick-search): interrupteur Copy / Fill quand une fenetre cible existe Ouverte par Ctrl+Shift+Q ou la barrette, la recherche remplissait toujours ; copier restait impossible alors que le remplissage echoue vers une application lancee en administrateur, ou quand le gestionnaire de Chrome s'en mele. Sans cible (tray, palette), l'interrupteur n'apparait pas : remplir n'aurait rien a viser. Le choix est retenu en pref DPAPI, les verbes des boutons et l'aide suivent. Co-Authored-By: Claude Opus 5.5 --- css/style.css | 10 +++++++++ index.html | 6 ++++++ js/app.overlays.js | 54 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/css/style.css b/css/style.css index c624ef6..89667c3 100644 --- a/css/style.css +++ b/css/style.css @@ -2133,6 +2133,16 @@ input[type="range"]::-webkit-slider-thumb { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.qs-mode { + display: flex; gap: 2px; padding: 2px; flex-shrink: 0; + background: var(--bg-elev-2); border-radius: 999px; +} +.qs-mode button { + border: 0; background: none; cursor: pointer; + color: var(--text-dim); font-size: 12px; + padding: 4px 10px; border-radius: 999px; +} +.qs-mode button.is-on { background: var(--accent); color: #fff; } .quick-search-acts { display: flex; gap: 2px; flex-shrink: 0; } diff --git a/index.html b/index.html index a5655eb..778bb67 100644 --- a/index.html +++ b/index.html @@ -1231,6 +1231,12 @@ + + Esc
diff --git a/js/app.overlays.js b/js/app.overlays.js index 981bc26..eb8638c 100644 --- a/js/app.overlays.js +++ b/js/app.overlays.js @@ -27,6 +27,39 @@ let quickSearchHideAfter = false; // HWND and is waiting for cmd://autofill/execute. On pick we SendInput // the password instead of copying to the clipboard. let quickSearchFillMode = false; +// CE QUE L'UTILISATEUR PREFERE quand une cible existe : remplir, ou copier +// quand meme - le remplissage echoue vers une application lancee en +// administrateur, ou quand le gestionnaire de Chrome s'en mele. Retenu en pref +// DPAPI : localStorage se vide a chaque lancement (port ephemere). +let quickSearchPreferCopy = false; + +/// Remplit-on vraiment ? Il faut une cible ET ne pas avoir choisi Copy. +function qsFills() { return quickSearchFillMode && !quickSearchPreferCopy; } + +function qsApplyMode() { + const box = document.getElementById('qsMode'); + if (box) { + box.hidden = !quickSearchFillMode; + box.querySelectorAll('[data-qsmode]').forEach(b => + b.classList.toggle('is-on', (b.dataset.qsmode === 'fill') === qsFills())); + } + const hintEl = document.querySelector('#quickSearchModal .quick-search-hint'); + if (hintEl) { + hintEl.textContent = qsFills() + ? 'Enter / click = password · Shift+Enter / right-click = user+password · Ctrl+Enter / Ctrl+click = username · Esc = cancel' + : 'Enter / click = copy password · Ctrl+Enter / Ctrl+click = copy username · Esc = close'; + } +} + +document.addEventListener('click', ev => { + const b = ev.target.closest && ev.target.closest('[data-qsmode]'); + if (!b) return; + quickSearchPreferCopy = b.dataset.qsmode === 'copy'; + if (Bridge.active) Bridge.setPref('qsCopy', quickSearchPreferCopy ? '1' : '0'); + qsApplyMode(); + quickSearchRender(); + document.getElementById('quickSearchInput').focus(); +}); function quickSearchScoreEntry(e, q) { if (!q) return 1; // empty query → all entries pass, ordering preserved @@ -89,7 +122,7 @@ function quickSearchRender() { row.appendChild(avatar); row.appendChild(main); // Un bouton par valeur presente : pas besoin de retenir clic / Ctrl+clic. - const verb = quickSearchFillMode ? 'Fill ' : 'Copy '; + const verb = qsFills() ? '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 }); @@ -133,7 +166,7 @@ async function quickSearchPickEntry(entry, mode) { try { if (secret && secret !== '[ERROR]') code = (await generateTOTP(secret)).code; } catch (_) { code = ''; } if (!code) { toast('2FA: cannot read the secret', 'error'); return; } - if (quickSearchFillMode) { + if (qsFills()) { autofillPendingToast = entryDisplayName(entry) + ' · 2FA code sent'; if (Bridge.active) Bridge.executeAutofill('', code, quickSearchHideAfter); quickSearchFillMode = false; @@ -159,7 +192,7 @@ async function quickSearchPickEntry(entry, mode) { // Fill mode (Ctrl+Shift+Q hotkey): SendInput directly into the HWND // Delphi saved when the hotkey fired. No clipboard touch. - if (quickSearchFillMode) { + if (qsFills()) { if (mode === 'user') { const u = entry.username || ''; if (!u) { toast('No username on this entry', 'warning'); return; } @@ -527,12 +560,15 @@ function openQuickSearchModal(hideAfter, forFill) { quickSearchSelected = 0; quickSearchHideAfter = !!hideAfter; quickSearchFillMode = !!forFill; - // Subtle hint to the user about what Enter will do. - const hintEl = modal.querySelector('.quick-search-hint'); - if (hintEl) { - hintEl.textContent = forFill - ? 'Enter / click = password · Shift+Enter / right-click = user+password · Ctrl+Enter / Ctrl+click = username · Esc = cancel' - : 'Enter / click = copy password · Ctrl+Enter / Ctrl+click = copy username · Esc = close'; + qsApplyMode(); + // La preference arrive de Delphi : on affiche tout de suite, et on + // rectifie des qu'elle est la (une fraction de seconde). + if (forFill && Bridge.active) { + Bridge.getPref('qsCopy').then(v => { + quickSearchPreferCopy = v === '1'; + qsApplyMode(); + quickSearchRender(); + }).catch(() => {}); } quickSearchRender(); setTimeout(() => input.focus(), 50);