Replace biometric validation logout with frosted lock overlay
Details
Instead of closing the main window and showing the unlock screen when
validating biometric enrollment, the app now displays a frosted glass
overlay at ZIndex 2000 on top of the existing UI. The user sees a
verification prompt: succeed with biometric and it stays enabled, or
enter the master password and biometric gets disabled. The main window
stays open throughout — no shutdown, no window recreation.
Add Exit button to unlock screen
Details
Place Exit and Unlock buttons side by side on the lock screen so users
can quit the app without needing to unlock first. Exit uses the
secondary surface style, Unlock retains the primary accent style.
Add biometric enrollment validation flow
Details
After enabling biometric unlock in settings, the app now locks
immediately and prompts the user to verify biometric works. If the user
successfully unlocks with biometric, the feature stays enabled. If they
cancel biometric and unlock with their master password instead, biometric
is automatically disabled and unenrolled — ensuring the user has proven
the biometric flow works before relying on it.
Fix macOS biometric unlock for unsigned debug builds
Details
The modern SecItem keychain API requires the keychain-access-groups
entitlement, which unsigned .NET debug builds lack (errSecMissingEntitlement
-34018). Split MacBiometricService into two code paths using preprocessor
directives:
- DEBUG: Uses legacy SecKeychainAddGenericPassword / FindGenericPassword
(file-based keychain, no entitlements needed) with separate LAContext
Touch ID evaluation via ObjC block bridging.
- RELEASE: Uses modern SecItemAdd / SecItemCopyMatching with biometric
access control (kSecAccessControlBiometryCurrentSet) for signed builds.
Both paths store the master password in the macOS Keychain gated by
biometric verification and fall back to manual password entry on failure.
Add cloud sync entity progress indicator to status bar pill
Details
Track entity-level sync progress (synced/total) through the full stack:
Rust SyncProgress shared state computed from cursor count + outbox
distinct entities, exposed via FFI get_status, deserialized into C#
CloudSyncStatus, and displayed in the storage state pill as
"Cloud · X/Y" while entities are pending upload. Falls back to the
existing storage percentage display once all entities are synced.
Get notified about new releases