diff --git a/TEST_SCENARIOS.md b/TEST_SCENARIOS.md index 9835d8c..7cb021c 100644 --- a/TEST_SCENARIOS.md +++ b/TEST_SCENARIOS.md @@ -169,6 +169,12 @@ plus bas. - **Non-régression** : le clic extérieur ne ferme déjà pas l'éditeur pendant l'attente — `#busyOverlay` est dans la liste blanche, et il couvre l'écran de toute façon. - **Tester les DEUX déclencheurs** : *Sync now* **et** *Export*. Il y a **deux** gestionnaires `Esc` distincts — celui en phase de capture dédié à l'éditeur, et la chaîne globale « une surface par frappe » (confirm → palette → change-master → cheatsheet → … → éditeur). Le premier correctif n'en couvrait qu'un, et l'export passait par l'autre : le bug semblait persister alors qu'il avait été corrigé sur le chemin du sync. +### B16 · hotkeys globaux pendant une opération bloquante +- **ACTION** : lancer un *Export* (ou *Sync now*) → pendant l'overlay, presser `Ctrl+Shift+A`, puis `Ctrl+Shift+Q`. +- **ATTENDU** : ni l'éditeur ni la quick search ne s'ouvrent ; toast « Busy — try again in a moment ». Tout redevient normal une fois l'overlay parti. +- **Pourquoi les hotkeys échappent aux gardes UI** : ils arrivent de Win32, **hors de la fenêtre**. L'overlay qui couvre l'application n'intercepte rien : le panneau s'ouvrait derrière lui, inaccessible. +- **Où est posé le garde** : dans `openSlideOver` et `openQuickSearchModal`, pas sur chaque déclencheur — c'est le point de passage de *tous* les appelants (hotkey, palette de commandes, bouton *Fix* du vault health). Sûr parce qu'**aucun chemin de sauvegarde n'affiche l'overlay** : rien de légitime n'ouvre l'éditeur pendant une opération bloquante. + ### B14 · dialogues natifs ouverts par un clic programmatique - **ACTION** : `A` ouverte + **dirty** → (a) Settings → *Import vault* ; (b) télécharger une pièce jointe (fallback web) ; (c) *Upload icon* ; (d) joindre un fichier. - **ATTENDU** : **seul** le dialogue natif s'ouvre. Pas de « Discard unsaved changes? » par-dessus, l'éditeur reste ouvert et modifié. diff --git a/js/app.js b/js/app.js index a088d17..dcaa4eb 100644 --- a/js/app.js +++ b/js/app.js @@ -3839,6 +3839,15 @@ const ENTRY_TEMPLATES = { // pre-fill the corresponding fields for the new-entry path. async function openSlideOver(id, opts) { opts = opts || {}; + // A blocking operation owns the screen (export, sync, rotation). The busy + // overlay covers the panel, so the editor would open unreachable behind it + // — and the Ctrl+Shift+A hotkey reaches this from outside the window, where + // the overlay stops nothing. No save path shows the overlay, so nothing + // legitimate opens the editor while it is up. + if (isBusy()) { + toast('Busy — try again in a moment', 'warning'); + return false; + } const isNew = (id == null); const e = isNew ? null : state.entries.find(x => x.id === id); // Switching to another entry while the current edit has unsaved diff --git a/js/app.overlays.js b/js/app.overlays.js index 396e08a..e9d1c88 100644 --- a/js/app.overlays.js +++ b/js/app.overlays.js @@ -465,6 +465,13 @@ function endTour() { } function openQuickSearchModal(hideAfter, forFill) { + // Same reason as the editor: the hotkey fires from outside the window, so + // the busy overlay covering the app stops nothing — the modal would open + // behind it, unreachable. + if (typeof isBusy === 'function' && isBusy()) { + toast('Busy — try again in a moment', 'warning'); + return; + } const modal = document.getElementById('quickSearchModal'); const input = document.getElementById('quickSearchInput'); modal.classList.remove('is-hidden');