diff --git a/TEST_SCENARIOS.md b/TEST_SCENARIOS.md index cdc9b7f..b5f8a46 100644 --- a/TEST_SCENARIOS.md +++ b/TEST_SCENARIOS.md @@ -193,7 +193,10 @@ plus bas. ### B18 · clavier pendant une opération bloquante ✅ - **ACTION** : lancer un *Sync now* (ou un export) → pendant l'overlay, presser `Tab` plusieurs fois puis `Espace` / `Entrée`. - **ATTENDU** : rien ne se passe. Aucun bouton derrière l'overlay ne prend le focus ni ne s'active. Tout redevient normal ensuite, focus compris. +- **ACTION 2 — les raccourcis** : pendant l'overlay, `Ctrl+A` (tout sélectionner), `Ctrl+K` (palette), `j`/`k` (curseur), **`Suppr`** (suppression par lot). +- **ATTENDU** : aucun effet. Le plus important est `Suppr` : supprimer des entrées pendant qu'un sync ou une rotation écrit sur les mêmes lignes est exactement ce que l'overlay doit empêcher. - **Historique** : l'overlay ne bloque que la **souris** — il couvre la page. Le clavier passait au travers : `Tab` entrait dans les contrôles derrière et `Espace` les activait. Corrigé avec l'attribut natif `inert` (retire un sous-arbre du focus, du hit-testing et de l'arbre d'accessibilité), posé sur chaque enfant de `` **sauf** l'overlay. +- **`inert` ne couvre pas les raccourcis** : ils sont écoutés sur le **document**, donc ils ne traversent aucun élément et rien dans l'arbre ne peut les arrêter. Il a fallu un garde `isBusy()` explicite sur chacun — même famille que les hotkeys Win32, qui arrivent eux de l'extérieur de la fenêtre. - **Point de vigilance** : `hideBusy()` retire l'inertie **inconditionnellement**. Une opération qui échouerait sans le rétablir laisserait une application qui semble gelée. - **`inert` ne suffit pas seul** : il retire les contrôles du focus, mais **pas** le défilement. `Espace`, `Page haut/bas`, `Début/Fin` et les flèches font défiler le document **sans aucun élément focalisé** — la page bougeait encore sous l'overlay. Ces touches sont donc avalées en phase de capture tant que l'opération tourne. - **Vérifier aussi le défilement** : pendant l'overlay, `Espace` et `Page bas` ne doivent rien bouger ; après, le défilement clavier doit refonctionner. diff --git a/js/app.js b/js/app.js index da844de..2b2e255 100644 --- a/js/app.js +++ b/js/app.js @@ -2741,6 +2741,9 @@ function isPasswordAged(e) { // Stays inert when any input/textarea has focus, when a modal is open, // when an open slideover would steal Enter, or the user is mid-edit. function handleCardCursorKey(e) { + // Document-level like the other shortcuts, so `inert` does not stop it: + // j/k would still move the cursor and scroll the grid under the overlay. + if (isBusy()) return; if (e.ctrlKey || e.altKey || e.metaKey) return; const tag = (e.target && e.target.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea' || tag === 'select') return; @@ -7316,6 +7319,10 @@ async function init() { // batch bar does, just via keyboard. document.addEventListener('keydown', e => { if (e.key !== 'Delete' && e.key !== 'Backspace') return; + // Destructive, and listened for on the document — `inert` does not + // reach it. Deleting entries while a sync or a rotation is mid-flight + // is the worst thing this overlay is supposed to prevent. + if (isBusy()) return; if (e.ctrlKey || e.altKey || e.metaKey) return; const tag = (e.target && e.target.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea' || tag === 'select') return; @@ -8267,6 +8274,11 @@ async function init() { // Command palette + bulk shortcuts document.addEventListener('keydown', e => { + // Shortcuts are listened for on the DOCUMENT, so `inert` on the page + // does not stop them — it only removes elements from focus and hit + // testing. Ctrl+A still selected every entry under the busy overlay, + // and Ctrl+K would have opened the palette behind it. + if (isBusy()) return; if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); openPalette();