Commit Graph

184 Commits

Author SHA1 Message Date
r-zakarya 90a696b1a8 docs(audit): UI is fully English; i18n note was stale
Swept every user-facing surface (index.html, all js strings/toasts/dialogs,
Delphi tray/balloon/dialog text) — no French in the UI. The remaining French
is project docs (CLAUDE.md/CODE_AUDIT.md), which is the doc language, not a
mix to fix. i18n stays a future feature, not a bug.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:50:46 +01:00
r-zakarya 64a843d23a feat(palette): add the sidebar Tools + Settings as commands
Generator, Authenticator, TOTP generator, Vault health, Audit log, Import,
Export and Settings are now reachable from Ctrl+K. View-based tools reuse
the sidebar buttons' click handlers so their cache invalidation rides along.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:22:19 +01:00
r-zakarya a090c64081 feat(security): "Lock vault when Windows locks or sleeps" toggle (default ON)
Explicit opt-out for users without Quick Unlock who don't want to retype the
master password after every sleep. Default ON = exact historical behavior
(lock on WTS lock/suspend, with the documented Quick Unlock exemption —
DPAPI already gates access via the Windows account). Synced setting,
Settings > Security.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:17:16 +01:00
r-zakarya 8555661823 fix(esc): palette closes before the info modals it opens over
Cheatsheet open -> Ctrl+K -> Esc closed the cheatsheet UNDER the palette:
the priority chain tested cheatsheet/history before cmdPalette. Palette now
sits right after confirmModal (still the absolute top) in the one-surface-
per-keystroke chain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:03:31 +01:00
r-zakarya 1751f0534f fix(slideover): opening the editor closes Settings (was hidden underneath)
Ctrl+Shift+A with Settings open created the entry panel BELOW the Settings
panel — invisible. openSlideOver now closes Settings first (root fix: covers
every editor-open path, not just the hotkey), placed after the discard guard
so cancelling keeps Settings. The inverse — opening Settings over the editor
— stays as-is by design (Esc: Settings first, editor second).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:57:20 +01:00
r-zakarya a911f2588f fix(sync): preserve created_at/updated_at when sync adds a remote entry
Same class as the reported import bug, other door: POST /entries always
stamped now, so an entry arriving on a device via sync lost its original
creation date (and advertised a fake edit via fresh updated_at).
HandleCreateEntry now honours body timestamps like bulk-import (absent ->
now); applyRemoteSnapshot already ships them through encryptImportEntry,
normalized to the DB format. Regular saves/duplicates send none - unchanged.
password_changed_at shares the created_at param, which is faithful: the
password is at least as old as the entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 00:18:13 +01:00
r-zakarya 829056f1fa fix(import): normalize timestamps to the DB format at the import door
vault_entries uses SQLite's space-separated UTC format everywhere, and both
sorting and sync last-write-wins compare the strings lexically — so a foreign
JSON import carrying strict-ISO 'T'/millis/Z/offset timestamps would slot in
with a different format and subtly break ordering and merge arbitration.
normalizeImportTimestamp converts any ISO-ish variant to 'YYYY-MM-DD
HH:MM:SS' UTC (bare strings treated as UTC, garbage -> '' = server stamps
now). +1 unit test (69 total).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 00:13:41 +01:00
r-zakarya 8cc1599434 fix(slideover): double-click on Save no longer creates a duplicate entry
soSave had no re-entrancy guard: a second click while the first run awaited
encryption/POST ran the whole save again -> two POSTs, two entries. Wrapped
in a soSaving latch (same class as the sync-button guard); body moved to
soSaveInner so every early validation return releases the latch via finally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 23:57:43 +01:00
r-zakarya 51f72e560a fix(ctxmenu): Esc with the context menu open closes only the menu
Same Esc fall-through class: the menu's Esc handler was bubble-phase and
didn't stop the keystroke, so the slideover capture handler fired first and
popped the discard prompt while the menu also hid. Capture + stopPropagation,
gated on the menu being visible; registered before the slideover handler
(installCustomContextMenu runs at the top of init) so ordering is guaranteed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:48:32 +01:00
r-zakarya bf606493bd fix: three more bugs of the same classes (Esc fall-through, chord, placement)
Hunted the classes behind the recent quick-search fixes across the codebase:

- Esc priority: the command palette is .cmd-palette (not .modal), so the
  slideover's capture-phase Esc handler didn't see it and popped the
  discard-confirm UNDER the open palette. Capture handler now also yields
  to the palette.
- Esc fall-through: the fallback Esc branch closed palette + slideover +
  entry modal + generator ALL on one keystroke. Now closes exactly one
  surface per keystroke, topmost first.
- Modifier chord: the real-VK Tab between username and password becomes
  Shift+Tab if the user holds Shift mid-fill -> focus moves backward and
  the password lands in the username field. ForceReleaseModifiers before
  SendVKey(VK_TAB).
- Stale placement: RestoreFromTray replayed the MinimizeToTray snapshot for
  a merely taskbar-minimised window, teleporting it to the last tray-hide
  position. Snapshot now replays only on a genuine tray return (captured
  before Show flips Visible); iconic windows use Windows' own placement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:46:42 +01:00
r-zakarya 3d217e82d4 fix(quick-search): Esc closes only the modal; cancel returns focus to target
- The qsInput Esc handler didn't stopPropagation, so the SAME keystroke fell
  through to the document-level Esc handlers which, seeing the modal now
  closed, also closed (or discard-prompted) the dirty slideover behind it.
- autofill/cancel now hands the foreground back to the saved target HWND:
  Esc after a fill-mode hotkey returns the user to the window they came
  from instead of leaving our app focused. Locked-vault path still ends
  focused on us (cancelAutofill runs before focusApp).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:20:24 +01:00
r-zakarya 770504ea03 fix(quick-search): only tray-hidden windows go back to the tray on close
wasHidden counted IsIconic too, so a window merely minimised to the taskbar
("visible" to the user) was treated as tray-origin: Esc/pick sent it to the
TRAY, vanishing from the taskbar it came from. wasHidden is now strictly
"came from the tray" (Visible=false); an iconic window gets restored by the
hotkey and simply stays open when the modal closes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:12:41 +01:00
r-zakarya 3b756648c5 fix(slideover): discard-confirm on new->new switch (Ctrl+Shift+A)
Opening a fresh new entry while a dirty unsaved one was open skipped the
discard prompt: both soState.id and id are null, so soState.id !== id was
false and "switching" never triggered. Existing->anything worked (ids
differ). OR in the null/null case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:52:56 +01:00
r-zakarya 2c7f6188e3 fix(clipboard): copy toasts reflect the configured clear delay
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>
2026-07-12 19:42:26 +01:00
r-zakarya 8cfa1b0d6a feat(clipboard): user-configurable auto-clear delay (Never/15/30/60/120s)
Single choke point: copySecure overrides any positive clearAfterMs with the
clipboardClearSeconds setting (0 = user disabled). The 15 call sites keep
passing 30000 unchanged — positive just means "auto-clear this secret";
explicit 0 (username copies) still never clears. Synced setting + a select in
Settings > Security (Clipboard privacy). Win+V history exclusion is
deliberately NOT exposed — a password manager must not offer to leak into
history / cloud clipboard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 11:48:08 +01:00
r-zakarya 3822c21194 fix(clipboard): auto-clear must not wipe content copied since our SetText
Copy a password (30s auto-clear armed), then copy something else from
another app before the timer fires: the tick emptied the clipboard anyway,
destroying the user's newer content. Guard with the Win32 clipboard sequence
number: SetText snapshots GetClipboardSequenceNumber, ClearIfOurs only
empties when it hasn't moved. Applied to the auto-clear timer AND the
clear-on-minimize path (same bug class); the explicit JS clipboard/clear
command stays unconditional (user-initiated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 11:20:49 +01:00
r-zakarya 72dcc3dd82 fix(autofill): kill residual hotkey chord; dedicated fail-balloon setting
Residual new-entry trigger (1 in 6): password chars go out as
KEYEVENTF_UNICODE (VK_PACKET, can't match a hotkey) — the real chord risk is
the Ctrl+A clear-field, which sends a real VK_A. If the user re-presses
Ctrl+Shift mid-sequence, that VK_A becomes physical Ctrl+Shift+A = our own
new-entry hotkey. ForceReleaseModifiers now runs inside
SendSelectAllAndDelete, at the risky instant, not just once up front.

Balloon: was gated by "Show tray notifications" (OFF for this user) — now
gated by its own synced setting "Tray alert when autofill is blocked"
(autofillFailBalloon, Settings > Autofill, default ON), carried as notify=0
on cmd://autofill/execute. ShowBalloon no longer gates internally; each
caller applies its own setting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 05:23:02 +01:00
r-zakarya 9a72fc0424 fix(build): ShowBalloon must be public (called from UMainForm)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 05:12:23 +01:00
r-zakarya aab0b14be4 fix(autofill): force-release stuck modifiers; balloon when blocked from tray
Root cause of "Ctrl+Shift+P opened the new-entry modal": if the user still
holds Ctrl+Shift when WaitForModifierRelease times out (1s), every password
letter is typed as a Ctrl+Shift+<letter> chord — garbage in the field AND it
fires our own global hotkeys (a password containing 'a' triggers Ctrl+Shift+A
= new entry). ForceReleaseModifiers now injects KEYUP for any still-held
modifier before typing.

Also: when the fill is blocked (elevated target) while the window is hidden
in the tray, the in-app toast is invisible — show a tray balloon instead.
ShowFirstTimeBalloon generalized into ShowBalloon(title, text, warning),
gated by the existing "Show tray notifications" setting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 05:08:50 +01:00
r-zakarya b869effee5 feat(autofill): "Clear the field before typing" setting (default ON)
The Ctrl+A + Del sent before each field misbehaves on targets where Ctrl+A
isn't select-all (terminals, some remote desktops). New synced setting
(autofillClearField, Settings > Autofill) gates it: JS appends clear=0 to
cmd://autofill/execute when off; ExecuteAutofill wraps the three
SendSelectAllAndDelete calls behind AClearFirst. Absent param = ON, so
existing behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 04:52:35 +01:00
r-zakarya da1284cfc3 fix(build): declare PROCESS_QUERY_LIMITED_INFORMATION locally
Winapi.Windows doesn't expose the WinNT.h constant — dcc32 E2003.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 04:41:38 +01:00
r-zakarya a602d05b84 fix(autofill): defer the direct-hotkey success toast too
autofillFillEntry (Ctrl+Shift+L/P path) still toasted "Password filled:"
optimistically alongside the honest UIPI failure toast. Route it through
autofillPendingToast / Bridge.onAutofillResult like the quick-search path,
and label with entryDisplayName (site is often empty -> "filled:" + nothing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 04:41:24 +01:00
r-zakarya 4a47caad55 fix(autofill): honest result reporting + restore maximized from tray
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>
2026-07-11 20:19:34 +01:00
r-zakarya 7103fbf703 fix(quick-search): keep the open window visible during the fill (no minimize)
The minimize-then-SW_SHOWNOACTIVATE approach flickered and sometimes lost the
restore race (window stayed minimized). Root fix: when the window was open
before the hotkey (ARestoreAfter), skip the minimize entirely — being the
foreground process is exactly what allows handing focus to the target, so the
window simply stays in place beside it. Tray-origin flow keeps the old
minimize (the window is a temporary overlay, trayed after the fill anyway).

Safety: if the target refuses the foreground (elevated / UIPI) and our window
is still foreground, bail before typing — otherwise the password would be
typed into the vault's own visible UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:01:05 +01:00
r-zakarya 3b32a621e8 fix(quick-search): stop teleporting + hiding an already-open window
Two bugs when Ctrl+Shift+Q fires while the app window is open beside the
target app:
- RestoreFromTray re-applied FSavedPlacement (captured at the LAST
  MinimizeToTray) to an already-visible window -> it jumped to a stale
  position. Now: visible and not iconic -> just SetForegroundWindow.
- ExecuteAutofill minimizes our window when it is foreground (the user just
  clicked the entry) and never brought it back when hide_after was false.
  New ARestoreAfter param (= not HideAfter): restore with SW_SHOWNOACTIVATE
  after the fill, so the window returns to its position without stealing
  focus from the freshly-filled target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 19:52:26 +01:00
r-zakarya 01426a48b1 refactor(db): drop cleartext template refs, fix ghost columns + fresh-DB schema
Migration verified (0 unmigrated cleartext templates across both accounts)
before contracting:
- GET/POST/PUT/bulk no longer read or write the cleartext template column
  (only template_enc/iv); the PUT partial-update gate stays keyed on the
  'template' JSON key presence.
- Removed AddColumnIfMissing for template AND tags/title — those two had
  silently re-added the dropped columns as empty ghosts at every start.
- CREATE TABLE: removed site/username NOT NULL cleartext columns — a FRESH
  database rejected the very first INSERT (which no longer ships them).

User can now DROP COLUMN template (and re-drop the ghost tags/title).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 19:52:26 +01:00
r-zakarya d583a3f7d3 refactor(js): extract attachments, autofill, unlock modules from app.js (3.1)
app.js 9363 -> 7962 lines. Three new classic-script modules:
- app.attachments.js (250): blob crypto + upload/download UI, pure
  declarations, loads before app.js
- app.autofill.js (276): Win32 combos, title->entry matching, picker,
  pure declarations, loads before app.js
- app.unlock.js (907): Quick Unlock + PIN + recovery code grouped (same
  "enter without master pw" theme); assigns Bridge.onPinResult /
  onQuickUnlockResult at top level so it loads AFTER app.js, like app.sync.js

Audit viewer stays in app.js (only 65 lines, not worth a file). Clipboard
bridge helpers stay too (were interleaved in the quick-unlock section but
unrelated). Registered in BuildAssets whitelist + index.html + APP_PARTS.
Verified in-app: quick unlock cold-start, attachment upload/download.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 19:28:19 +01:00
r-zakarya 83a3dfdfc2 docs: note assets.rc/.inc alongside assets.res as gitignored build output
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:35:54 +01:00
r-zakarya 982b7c082c chore: stop tracking generated embedded-asset artifacts
assets.res/.rc/.inc are regenerated by BuildAssets before every Delphi build
and linked into the exe — tracking them just churned a binary each session.
Untrack + gitignore (matches the intent already stated in CLAUDE.md). A fresh
clone runs BuildAssets first anyway (documented build step 1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:35:28 +01:00
r-zakarya a42e4b205d feat(entries): encrypt template at rest, guided tour, import fixes, cleanup
Batched session work sharing app.js / index.html / Entries.pas, so it can't
split cleanly without interactive hunk staging.

- feat: encrypt `template` metadata at rest (template_enc/iv, added to
  ENCRYPTED_META_FIELDS). withEncryptedMeta skips an absent template key so
  partial re-ships (add-tag, move-to-folder) don't wipe it via LHasTemplate.
  Cleartext column kept as migration fallback. +3 unit tests.
- feat: first-run guided tour ("How it works") — spotlight + bubble, no GIFs,
  re-launchable from Settings, seen-flag in DPAPI prefs.
- fix(import): preserve original created_at on restore (was stamped to import
  time); restore entry icons on overwrite (PUT ignores icon_b64).
- fix(settings): correct clipboard-privacy copy (already excluded from Win+V);
  PIN text 4-6 -> 4-12; reorder Set-PIN above unlock-method; move tray/startup
  toggles to General; dedicated backup-password button + warning status; tab icons.
- chore: remove dead legacy monolith (app-legacy.js, index-legacy.html,
  style-legacy.css) + unused passkeyBtn stub.
- docs: full-source review (CODE_AUDIT 6b), template + favorite/pinned notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:32:37 +01:00
r-zakarya 92ed153bc0 fix(sync): surface attachment/folder restore failures (audit 2.3)
applyRemoteSnapshot swallowed attachment + folder restore errors in silent
catch blocks. Count them (attFailed/folderFailed) and warn in a toast after
the sync summary. These don't abort the push (the entry synced, only its
attachment/folder didn't) unlike a failed entry import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:32:15 +01:00
r-zakarya b9eee0e15e perf(build): skip redundant per-file node --check when the test gate runs
The unit suite concatenates + parses every embedded app.*.js (plus argon2)
in a vm, so a syntax error already fails it. Run the ~10 cold `node --check`
spawns (~3-4s) only when tests are bypassed (PM_SKIP_TESTS) or absent.
Cuts the pre-compile freeze from ~7s to ~4s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:32:15 +01:00
r-zakarya 0925afb689 perf(autofill): keep WebView2 renderer responsive while in tray
Add --disable-renderer-backgrounding / -backgrounding-occluded-windows /
-background-timer-throttling to the WebView2 args. Without them Chromium
freezes a hidden renderer, so the first autofill hotkey after a tray-only
start (Start with Windows) waited 3-5s for onAutofillRequest to run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:32:14 +01:00
r-zakarya 45ba47f772 feat(settings): keep backup pwd on disable, dedicated pwd button, tab icons
Auto-backup no longer wipes the stored password when disabled, so
re-enabling reuses it silently. A dedicated "Set/Change backup password"
button (mirrors sync) owns the password, with a warning status when
unset. Corrected the stale hint that claimed the backup pwd was derived
from the master password. Added icons to each settings tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:53:45 +01:00
r-zakarya 064ad14156 style(settings): move Test connection under the WebDAV credentials 2026-07-10 12:36:14 +01:00
r-zakarya 9bf1e4f571 fix(sync): warn when pre-sync backup has no folder (was silent no-op) 2026-07-10 12:29:07 +01:00
r-zakarya bfce02ae6b style(settings): move Import next to Export 2026-07-10 12:19:22 +01:00
r-zakarya 548b26c518 fix(settings): no tab highlighted while searching (cross-tab results) 2026-07-10 12:14:33 +01:00
r-zakarya be84f0feb5 docs: CLAUDE.md — cleartext meta columns dropped post-migration 2026-07-10 11:14:03 +01:00
r-zakarya 8d7da02b0d fix(settings): force 800px width in centered mode (center rule was 520) 2026-07-10 11:11:59 +01:00
r-zakarya 5144c2aaf7 style(settings): English tab labels to match the UI (General/Security) 2026-07-10 05:26:45 +01:00
r-zakarya e6922d5b19 style(settings): centered modal height 720 (min(94vh,720px)) 2026-07-10 05:23:01 +01:00
r-zakarya 374a6b3fe9 feat(settings): split Autofill into its own tab
Extracted the autofill toggle + hotkey combos out of the Security section into
their own .slideover-field labelled "Autofill", added an Autofill tab + map
entry. 5 tabs now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 05:21:57 +01:00
r-zakarya c109694bd1 style(settings): size panel to 800x600 (viewport-guarded) 2026-07-10 05:18:02 +01:00
r-zakarya a0a6fa8834 fix(settings): widen the settings panel for the left tab column
Side-mode settings panel (420px) clipped its content (Clear cache button) now
that the vertical tab column eats ~100px. Bumped #settingsPanel to 520px (id
beats .slideover's 420; entry #slideover unchanged). Nudged centered height
620→660.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 05:14:49 +01:00
r-zakarya c59a595d1d feat(settings): fixed-height modal + move unlock sections to Account
- Centered settings modal gets a fixed height (min(88vh,620px)) so switching
  between short/long tabs no longer resizes + re-centers it. Entry #slideover
  stays content-sized (edit/new unaffected).
- PIN unlock / Quick unlock / Recovery key moved from Sécurité to the Account
  tab (unlock methods live with the account). Autofill stays in Sécurité.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 05:09:11 +01:00
r-zakarya 18533486b7 style(settings): move tabs to a vertical column on the left
Wrapped the tab bar + body in a .settings-main flex row; tabs now stack
vertically on the left with a right border, body scrolls on the right. JS
unchanged (toggles .is-tab-hidden on sections regardless of layout).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:56:16 +01:00
r-zakarya d333ce00ec build: regenerate embedded assets (settings tabs + sync/settings fixes) 2026-07-10 04:51:14 +01:00
r-zakarya bec94930b4 feat(settings): group settings into 4 tabs
Long settings panel → 4 tabs (Général / Sécurité / Account / Sync & Backup).
Each section (.slideover-field) is keyed by its label text to a tab via
SETTINGS_TAB_OF; applySettingsTab toggles .is-tab-hidden on the rest. No HTML
restructure (sections were already .slideover-field siblings), no dep. The
existing settings search composes: a live query suspends the tab filter so
cross-tab matches show, clearing it restores the active tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:42:22 +01:00
r-zakarya f644f8cb57 fix(settings): Esc doesn't close Settings while busy overlay is up
Pressing Esc during a sync/backup closed Settings under the busy overlay.
The Esc handler already bails for open modals — added the same bail when
#busyOverlay is visible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:13:46 +01:00