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 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-28 12:07:57 +01:00
parent 5e4fcfad0f
commit 7c26716831
3 changed files with 61 additions and 9 deletions
+45 -9
View File
@@ -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);