Global hotkeys come from Win32, outside the window, so the busy overlay intercepts nothing: Ctrl+Shift+A opened the editor behind it, unreachable. Guarded in openSlideOver and openQuickSearchModal rather than at each trigger - every caller routes through them, and no save path raises the overlay.
Two bugs in the same flow, both on the tray-originated path.
1. Esc did nothing until the search box was clicked: after the tray restore
the WebView2 child does not hold the OS keyboard focus, so no keystroke
reached the page at all — a DOM .focus() cannot fix that. Both quick-search
entry points now call WebBrowser.SetFocus after restoring.
2. Esc closed the modal but left the app on screen. In fill mode
closeQuickSearchModal fires cancelAutofill and then minimizeToTray, and two
back-to-back window.location.href assignments COALESCE — the minimise was
dropped. The copy path was unaffected because it never calls cancelAutofill,
which is why this only showed up here. Deferred by a setTimeout(0), the same
remedy CLAUDE.md already documents for quickunlock/store + pin/clear.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Same workflow as the password-only hotkey: click the 2FA field, press the
combo, the current 6-digit code is typed in.
Reuses the whole autofill pipeline — no new native typing path. The code is
sent in the password slot with an empty username, so Delphi's existing
password-only branch types just the code.
- Bridge: akTotp kind, hotkey id 47, registered/torn down alongside the other
two autofill combos; an unconfigured combo (vk=0) doesn't count as a
registration failure.
- Matching filters on totp_secret BEFORE scoring, so the multi-match picker
never offers an entry that has no code.
- Configurable + synced like the others (autofillHotkeyTotp), collision-checked
against the other four slots, covered by Reset to defaults and the cheatsheet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "clears in 30s" suffix was hardcoded in ~14 toasts. Single helper
clipClearSuffix() reads clipboardClearSeconds (''=Never); prefixes concatenated
to it. Cheatsheet text made generic "(auto-clears)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug 1: a maximized window trayed via the quick-search fill flow came back
"normal" on the next restore. ExecuteAutofill minimizes the window BEFORE
MinimizeToTray snapshots the placement, so the snapshot said SHOWMINIMIZED
and the never-restore-minimized guard forced SHOWNORMAL. Now honours
WPF_RESTORETOMAXIMIZED (Windows keeps the pre-minimize state in flags).
Bug 2: filling into an elevated app (admin Notepad) showed "password sent"
while UIPI silently discarded the keystrokes (SendInput even reports
success). ExecuteAutofill is now a function: it checks the target process
elevation up front (can't-open counts as elevated) and returns False without
typing. UMainForm feeds the result to JS via Bridge.onAutofillResult; the
quick-search success toast is deferred until Delphi confirms, and a failure
shows "Autofill blocked - the target window runs as administrator".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eighth slice. Quick search wasn't contiguous — its functions sat on both
sides of the cheatsheet and password-history modals (lines 725-1104). Rather
than a fiddly non-contiguous cut, the whole overlay cluster is extracted as
one byte-identical block: js/app.overlays.js (quick search + cheatsheet +
password history). Pure declarations, no top-level side effects → loads
before app.js; all state/api/Bridge/render/decryptPwd refs resolve via shared
global scope at call time.
- Byte-for-byte identical; syntax OK on all nine app parts; 62/62 tests green.
- index.html + BuildAssets whitelist + harness APP_PARTS updated.
app.js: 11936 → 9170 lines (8 modules extracted, ~2770 lines). Load order:
argon2 → crypto → totp → favicon → import → backup → health → overlays →
app → sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>