Skip to content

Release v2.0.0 — Cluster 5 UX, full localization, TOTP/HIBP polish & fixes#47

Merged
pexatar merged 38 commits into
mainfrom
release/v2.0.0
Jun 3, 2026
Merged

Release v2.0.0 — Cluster 5 UX, full localization, TOTP/HIBP polish & fixes#47
pexatar merged 38 commits into
mainfrom
release/v2.0.0

Conversation

@pexatar

@pexatar pexatar commented Jun 3, 2026

Copy link
Copy Markdown
Owner

PassKey v2.0.0

Major release on top of clusters 1–4 (already on main).

Highlights

  • Cluster 5 UX: toasts, idle auto-lock with countdown (incl. 10s warning for the 30s tier), inline validation, activity log + CSV export, enriched empty states, forgot-password & clear-vault flows.
  • Full localization (6 languages): LOC-02 rollout via x:Uid; localized placeholders (Identity/Password/PIN, example data per language); localized save button; dead panel-title code removed.
  • Identity: Middle name, Company, Username fields; in-place card refresh.
  • Import: Bitwarden/CSV/1PUX robustness (encrypted/ZIP/unsupported handling).
  • Dark-theme fix (U4): theme-aware brushes in code-behind — readable Verifier "Vault" tab.
  • Browser extension (1.0.1): fixed invisible popup action icons (SVG namespace) + always-visible copy actions.
  • Docs/landing: refreshed English-UI screenshots.
  • Version bump: app/installer → 2.0.0.

Verification

  • Build x64: 0 errors / 0 warnings
  • Tests: 222/222
  • Localization audit: 0 hardcoded-string leaks
  • Manual Gate v2.0.0: passed

🤖 Generated with Claude Code

pexatar and others added 30 commits May 25, 2026 19:28
Cluster 5 (T5.1–T5.19) ships the second wave of UX improvements for PassKey 2.0
plus 5 rounds of T5.GATE collaudo fixes on top.

Headline additions
- T5.1–T5.2: IToastService/ToastService — serial queue, bottom-right InfoBar,
  auto-dismiss 3s/5s/manual, inline action button. Migrated Save/Delete/Copy
  toasts across the 4 list VMs; removed the legacy SavedTip TeachingTip and
  ListViewHelpers.cs.
- T5.5: IAutoLockService/AutoLockService — 1s tick on UI thread, Win32
  GetLastInputInfo, lock-at-expiry, 60s/30s countdown toast with "Stay active".
- T5.10: ActivityLogViewModel + ActivityLogView ("Cronologia") + CSV export.
- T5.18: WelcomeView third option "Restore from backup".
- T5.19: backup filename with date PassKey_Backup_yyyyMMdd.pkbak.
- T5.6: inline validation (red border + "Required field") across the 4 Detail
  ViewModels; per-field IsXxxEmpty initialised on ResetFieldsForNew.
- T5.8: EmptyStateControl enriched (illustration + per-list custom copy + actions).
- T5.4: "forgot password" flow in LoginView — zero-knowledge dialog + restore
  backup / new vault paths. Added IVaultStateService.InitializeWithVaultAsync.
- T5.17: "Empty vault" in Impostazioni → Sicurezza (double confirm + master pwd).

Polish, accessibility, settings
- T5.7: hover effect on credit card (final form theme-aware accent — see GATE r4).
- T5.11: AutomationProperties.LiveSetting="Polite" on greeting/stat/InfoBar.
- T5.12: SettingsView reorganised flat (SettingsCard + section headers).
- T5.13: browser extension onboarding dialog.
- T5.14: localisation pass — 30+ resw keys × 6 languages.
- T5.15: quick-delete icon on list rows.
- T5.16: Audit Vault — threshold alignment with Dashboard.

T5.GATE collaudo (5 rounds, all fixes in this commit)
- Hotfix post-installer: COMException 0x80073B17 in IdentityDetailView /
  SecureNoteDetailView when saving — bare ResourceLoader.GetString("ButtonSave")
  converted to slash notation ("ButtonSave/Text") to match the .resw key renamed
  by bug 9c.
- Round 1 (7 fixes): Ctrl+H help shortcut; search-popup orphan defer via
  DispatcherQueue; language-restart single-instance race via --restart flag;
  hover card border; fade-in card via ItemsRepeater; Activity Log back button;
  "Sblocco/Creazione in corso" localised; Identity .resw key rename +
  SearchIdentities.PlaceholderText + orphan x:Uid removal.
- Round 2: section-switch freeze (VM state survives view recreation in ShellView
  — UpdateDetailPanel/UpdateDetailContent now called explicitly in SetViewModel
  of all 4 list views, plus unsubscribe-before-subscribe); Ctrl+? reverted to
  Ctrl+H (OEM-2 key not accessible on Italian layout); Identity localisation
  gaps (AddIdentity → AddIdentity.Text; panel titles PwPanelTitle*,
  CardPanelTitle*, IdPanelTitle*, NotePanelTitle* — new resw keys ×6 languages,
  bypass vm.PanelTitle in detail views).
- Round 3: fade-in card switched to Loaded-deferred Storyboard pattern
  (ElementPrepared fires before the element is in the visual tree).
- Round 4: hover border theme-aware — lighten card accent on dark theme, darken
  on light theme (±90 per RGB channel, clamp).
- Round 5: PAN masked in list view (security) — new CreditCardEntry
  .MaskedCardNumber computed property mirroring ExpiryFormatted pattern.

.gitignore: + .claude/ (claude-code workspace artefacts, per-developer) +
pm-export/ (password-manager test exports — real plaintext credentials).

Stats
- 70 files changed (61 modified, 1 deleted, 8 new).
- Build: 0 errors / 0 warnings (dotnet build -p:Platform=x64).
- Installer end-to-end verified through 5 collaudo rounds.

Follow-up items deferred post-GATE (tracked in piano-revisione-correzione-
miglioramento.md): FU1 email format validation; FU2 Ctrl+N stub handler;
FU3 unsupported/encrypted import warnings (Bitwarden + KeePass); FU4 Bitwarden
importer data loss (passport/license/ssn); FU5 IdentityEntry extension + i18n;
FU6 CsvImporter robustness; FU7 OnePuxImporter crash on email-as-object +
locale-independent matching; FU8 Activity Log scroll position. PR
feat/2.0/main → main scheduled after FU closure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…pages

ShellView.NewItem_Invoked was a stub (args.Handled = true with no action) even
though Ctrl+N is advertised in tooltips ("Nuova carta (Ctrl+N)") and in HelpView.

- Added public InvokeAddNew() to the four list views (Passwords, Cards,
  Identities, SecureNotes); each delegates to its VM's AddNewCommand.
- AddButton_Click now delegates to InvokeAddNew() to avoid duplication.
- ShellView.NewItem_Invoked dispatches on the current ShellContent.Content and
  routes the shortcut to the active list page. Non-list pages (Dashboard,
  Generator, Verifier, Settings, Help, Activity Log) are an intentional no-op.

Post-T5.GATE follow-up FU2. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Identity email field accepted obviously invalid input (mailtest.com,
mailtest, mail@test., mail@testcom) with no feedback. Added a lenient, inline,
NON-blocking warning — saving stays allowed because email is optional.

- IdentityDetailViewModel: new IsEmailFormatSuspect observable property, computed
  in OnEmailChanged (true only when something is typed and it doesn't look like an
  email). Does NOT touch CanSave. Helper IsPlausibleEmail does a lenient check
  (single '@' not first/last, domain dot not first/last) — not a strict RFC 5322
  validator. Reset to false in ResetFieldsForNew.
- IdentityDetailView: inline hint below the Email field using
  SystemFillColorCautionBrush (amber, not the red error colour), bound to
  IsEmailFormatSuspect via BoolToVisibility.
- 6× .resw: FieldEmailFormatWarning.Text (555 keys aligned across languages).

Post-T5.GATE follow-up FU1. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d-trip

Returning from the activity-log viewer to Settings dropped the user at the top of
the page instead of where they had clicked "Cronologia attività" (near the bottom,
in the Info section).

- SettingsView.xaml: named the root ScrollViewer (RootScroller).
- SettingsView: GetScrollOffset() reads the current vertical offset;
  RestoreScrollOffset(offset) re-applies it via ChangeView, deferred through the
  dispatcher (and Loaded when needed) because a freshly-created view hasn't
  completed layout yet and an immediate ChangeView would clamp to zero.
- ShellView: _settingsScrollOffset field (the Settings page is recreated on every
  navigation, so the offset can't live on the view). OnSettingsNavigateToActivityLog
  captures it before leaving; OnActivityLogBackRequested restores it on the new
  SettingsView. The double NavigateToSettings (direct + via SelectionChanged) is a
  no-op the second time since CurrentPage is an ObservableProperty re-assigned the
  same VM instance, so SetVm runs once.

Post-T5.GATE follow-up FU8. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verified against real exports (Firefox, KeePass) during T5.GATE testing.

(a) Title fallback to URL host: Firefox exports have no title/name column and
    identify logins by URL only, so entries imported anonymously. When the title
    is missing/empty the importer now derives it from the URL host
    (https://accounts.google.com/ -> accounts.google.com). Applied after the
    skip-empty check so the skip still judges raw fields.

(b) Header alias coverage: new NormalizeHeader lowercases, trims, treats
    underscores as spaces and collapses whitespace, so "login_name", "Login Name"
    and "login name" all match one alias. Added KeePass aliases: "account" -> Title,
    "login name" -> Username, "web site" -> Url. KeePass CSV
    ("Account","Login Name","Web Site","Comments") now imports complete instead of
    nearly empty.

Semantic fix: "login_name" previously mapped to Title; "Login Name" is KeePass's
username field, so it now maps to Username (no existing test covered the old
behaviour).

Tests: 216/216 (+4 FU6: Firefox host fallback, KeePass spaced headers,
titleless-no-url, alongside the 10 existing).

Post-T5.GATE follow-up FU6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tings page

Repeated backup/restore/import operations made dialogs (master-password prompt,
import-format chooser, merge strategy) and the file picker appear once, then twice,
then three times, and so on.

Root cause: SettingsView.SetViewModel subscribed 12 VM events (PropertyChanged,
ThemeChangeRequested, Backup/Restore/Import password & format & merge prompts,
completion and error callbacks) without ever detaching them. SettingsViewModel is a
persistent singleton in ShellViewModel while SettingsView is recreated on every
navigation, so each return to the page stacked another 12 handlers on the same VM.
When the VM raised an event, every accumulated handler fired — N dialogs, and N
runs of the import flow that opens the file picker right after those events.

Fix: extracted SubscribeVmEvents/UnsubscribeVmEvents; SetViewModel does a defensive
unsubscribe, subscribes, and registers Unloaded += OnUnloaded; OnUnloaded detaches
all 12 events when the view is unloaded (navigated away). The persistent VM no longer
retains handlers from discarded views. The defensive '-=' alone was insufficient
(dead views' handlers are different instances) — the Unloaded detach is the fix.

Verified Settings is the only persistent view subscribing dialog-raising VM events;
the four list views share the same anti-pattern but only on PropertyChanged with
idempotent effects (no visible symptom) — tracked as FU10.

Post-T5.GATE follow-up FU9. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dent mapping

Verified on a real 1Password .1pux export during T5.GATE testing.

(a) [critical] Email-as-object crash. OnePuxFieldValue.Email is string?, but current
    1Password exports store the email as an object
    { "email_address": "...", "provider": null }. System.Text.Json couldn't convert
    the object to a string and threw, aborting the ENTIRE import (every recent export
    carries this in its default "Starter Kit" identity, so 1PUX import was broken out
    of the box). Added OnePuxEmailConverter that tolerates both the legacy string and
    the object form. MapItem is now wrapped in try/catch so one malformed item can't
    abort the whole import (defence-in-depth).

(b) Locale-independent mapping. MapToIdentity/MapToCreditCard matched on the localized
    field title ("first name", "cvv"), so a non-English export (e.g. Italian "Nome",
    "Titolare") wasn't mapped. Added Id to OnePuxSectionField and match on the stable,
    language-independent id (firstname, lastname, email, ccnum, cvv, cardholder, …) with
    a title fallback for older exports and existing tests.

Tests: 219/219 (+3 FU7: email-as-object reproduces the real crash, identity-via-id,
card-via-id). AOT publish clean -> the JsonConverter is source-gen compatible.

Post-T5.GATE follow-up FU7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…X refinements

Robustness (verified on real Bitwarden/KeePass files):
- New ImportFileException whose Message is a ready-to-display, user-facing string
  (the existing ImportDataAsync try/catch surfaces it directly).
- Bitwarden encrypted export: BitwardenImporter detects "encrypted": true and throws
  a clear message instead of silently importing an empty vault.
- Bitwarden ZIP ("with attachments"): ImportOrchestrator detects the ZIP magic bytes,
  extracts data.json and imports it as Bitwarden JSON (ignoring attachments/). The
  Bitwarden picker now accepts .json and .zip.
- KeePass 1.x (.kdb): detected by its signature (5th byte 0x65 vs KDBX 0x67) -> a
  dedicated "convert to .kdbx" message. The KeePass picker now also accepts .kdb so
  the file is selectable and the message can be shown. Any other KDBX open failure
  (wrong password, invalid file) is wrapped in a readable message.

Import-flow UX refinements (raised during collaudo):
- Order: ask for the KDBX password AFTER the file is chosen, not before, so the prompt
  refers to a file the user actually selected and a cancelled picker wastes no entry.
- Format dialog labels now show accepted extensions: "CSV generico (.csv)",
  "KeePass 2.x (.kdbx)", "1Password (.1pux)", "Bitwarden (.json / .zip)".
- KDBX password dialog keeps "OK" disabled until a password is typed.
- Help -> FAQ "How do I import…" enriched with the full format/extension details,
  including what is NOT importable (encrypted Bitwarden, KeePass 1.x) and how to
  convert — translated across all 6 languages.

FilePickerService gains a multi-extension PickOpenFileAsync overload.
Tests: 221/221 (+2 FU3: encrypted -> exception, plaintext -> normal import).

Post-T5.GATE follow-up FU3. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enriches IdentityEntry with three fields inspired by Bitwarden/1Password identity
records: Middle name, Company and Username.

- IdentityEntry: + MiddleName, Company, Username (backward-compatible defaults).
- IdentityDetailViewModel: + three observable properties, wired through
  ResetFieldsForNew / LoadFromEntry / CreateNewEntry / ApplyToEntry.
- IdentityDetailView: Middle name between First and Last name; Company and Username
  after Phone, in the Personal Data section. Tab order updated.
- 6× .resw: FieldMiddleName.Text, FieldCompany.Text and FieldIdentityUsername.Text
  (558 keys aligned), with semantic translations.

Note: the username key is FieldIdentityUsername, NOT FieldUsername — the latter
already exists for the password login field ("Email / User ID") and reusing it would
mislabel the identity field.

FU5(b) "international localisation" was already satisfied: the existing identity
labels are semantically localised per country (Provincia -> Bundesland/Distrito,
Regione -> Comunidad autónoma, Carta d'identità -> DNI/Personalausweis/Cartão de
cidadão, Tessera sanitaria -> Carte vitale), so no further work was needed there.

Post-T5.GATE follow-up FU5. Build 0/0, tests 221/221.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lace edit

Editing an existing identity left its list card showing the old values until the
page was revisited. ApplyFilterAndSort does Entries.Clear()+Add() with the SAME
object references (the edit mutates the entry in place), so the GridView recycles
the container for the identical reference without re-running ContainerContentChanging
— which is what populates the identity cards' computed fields (avatar initial, full
name, formatted phone). The other lists are unaffected because they use direct
bindings.

Fix: in IdentitiesListView, when the detail panel closes (IsDetailOpen -> false),
rebind ItemsSource (RefreshListContainers) to force a full container regeneration so
the edited card reflects the change without revisiting the page.

Post-T5.GATE follow-up FU11. Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The vault audit (Verifica → Vault) walked the vault sequentially, one HIBP call at
a time with a 200ms courtesy delay between calls. With ~113 passwords it took ~45s;
at 1000 it would take ~6 minutes, showing a frozen "0" the whole time.

Parallelisation (WatchtowerScanService):
- RunScanAsync rewritten from a sequential for-loop + Task.Delay throttle to
  Parallel.ForEachAsync with MaxDegreeOfParallelism = HibpConcurrency (8) when HIBP
  is enabled (1 when disabled — the remaining work is purely local). The HIBP range
  (k-anonymity) endpoint is CDN-backed and built for volume, so bounded concurrency
  is safe. Thread-safe aggregation via ConcurrentBag + Interlocked. The strength
  analyzer is stateless and the HIBP HttpClient is a shared singleton.
  Estimated 1000-password scan: ~6 min -> ~19s.

Live progress:
- IWatchtowerScanService.Progress changed from Action<double> to Action<int,int>
  (scanned, total). Only PasswordVerifierViewModel subscribed; the dashboard is
  unaffected.
- PasswordVerifierViewModel gains ScannedCount/TotalToScan; AuditProgress is now 0-100.
- PasswordVerifierView repurposes the existing determinate VaultScoreRing as a live
  progress ring during the scan — the arc grows and the centre shows the live "X / N"
  count — then restores the final score when the scan completes. The separate
  indeterminate spinner is disabled.

Known side effect: issue lists (compromised/weak/duplicate) are now in arbitrary
order (ConcurrentBag) instead of vault order — acceptable, they are grouped by category.

Post-T5.GATE follow-up FU12. Build 0/0, tests 221/221.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The BitwardenIdentity DTO declared only 10 of the ~18 fields Bitwarden exports for an
identity, so middleName, company, username, ssn, passportNumber, licenseNumber and
address3 were silently lost on import — the worst being passport and licence, whose
destination fields already existed on IdentityEntry.

- BitwardenIdentity DTO: + MiddleName, Username, Company, Ssn, PassportNumber,
  LicenseNumber, Address3.
- MapIdentity now maps:
    middleName     -> MiddleName       (FU5 field)
    company        -> Company          (FU5 field)
    username       -> Username         (FU5 field)
    ssn            -> HealthCardNumber  (Codice Fiscale / Tessera Sanitaria for IT users)
    passportNumber -> PassportNumber
    licenseNumber  -> DrivingLicenseNumber
    address3       -> folded into Street with address1/address2
  CombineAddress now joins all three address lines. "title" (Mr/Sig.) stays unmapped,
  consistent with FU5 dropping the Title field.

Verified on a real Bitwarden export: passport AA1234BB, licence CC1234DD, codice
fiscale JHSROS92H10H264C and username now import correctly.

Tests: 222/222 (+1 FU4 covering all extended fields).

Post-T5.GATE follow-up FU4 (final FU). Build 0/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ders

Categoria A (bare-key → .Text, valori già tradotti):
- PageIdentities/PageNotes (titoli pagina)
- HelpView: 41 chiavi bare → .Text (guide, FAQ, scorciatoie, PageHelp)
- ButtonAddNote (pulsante Aggiungi note)
- EmptyEditorHint (hint editor note, nuova chiave ×6)

Categoria B (x:Uid + 9 nuove chiavi ×6 lingue):
- Placeholder note (Password/Identity detail) via chiave condivisa PlaceholderNotes
- Placeholder campo Password (Password detail)
- Header "Chiave Base32" (TotpSecretReadout)
- Placeholder master password Setup (×2)
- Placeholder + nota offline Verifier
- "Escludi caratteri ambigui" (Generator)
- RequiredFieldLegendIdentity (split da RequiredFieldLegend: testi divergenti Carte/Identità)

CreditCardDetailView non toccato: CardNotesBox.PlaceholderText già presente.
resw allineati a 569 chiavi ×6. Build 0/0, test 222/222. Collaudato EN+IT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Generator: hero card fixed Width=560 (no longer grows with password length,
  which dragged the options layout). Password on a single line clipped via a
  scroll-disabled ScrollViewer + right-edge LinearGradientBrush fade
  (transparent -> CardBackgroundFillColorDefault, theme-aware). Full value via Copy.
- Verifier: Password tab StackPanel fixed Width=640 (no longer stretches to the
  whole window nor shrinks to content). Vault tab untouched.

Build 0/0, test 222/222. Collaudato EN+IT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restore the original name-based delete dialog using the already-translated
(but orphaned) keys DeleteConfirmTitle/DeleteConfirmMessage/DeleteButton/CancelButton (×6 langs).

- BaseDetailViewModel.DeleteAsync: build dialog via ResourceLoader + the 4 keys.
  Removed dead/hardcoded methods: GetDeleteDialogTitle (abstract) + the 3 IT virtuals
  GetDeleteDialogContent/GetDeletePrimaryButtonText/GetDeleteCloseButtonText.
- 4 DetailViewModels: removed GetDeleteDialogTitle overrides ("Elimina password/carta/…").
- 4 ListViewModels (quick-delete): replaced IT literals with the same 4 keys.

No new resw keys (reuse). Build 0/0, test 222/222. Collaudato EN+IT (detail + list paths).
Fallback display names ("Carta senza nome" etc.) deferred to C-f.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 new resw keys ×6 langs:
- Tray context menu (MainWindow): TrayMenuShow/Lock/Exit (x:Name + ResourceLoader in ctor;
  Window XAML has no x:Uid resource map, so localized in code-behind).
- Close-confirmation dialog (MainWindow): TrayCloseContent/Minimize/Exit (+ reuse CancelButton).
- Manual 2FA-key dialog (PasswordDetailView): Totp2faDialogTitle/Body, TotpSeedPlaceholder,
  SaveButton (+ reuse CancelButton).
- WelcomeView: 2× "OK" -> ButtonOk.

resw aligned at 580 ×6. Build 0/0, test 222/222. Collaudato EN+IT.
Deferred: TOTP tooltip "Chiave Base32 non valida" -> C-f; "Startup error" -> QUAL-01.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SecureNotesListViewModel static helpers (added a static ResourceLoader):
- GetCategoryName: 10 note categories (Generale..Altro) -> 10 resw keys ×6 langs.
- GetRelativeDate: "Adesso/{n} min fa/{n} ore fa/Ieri/{n}g fa" -> localized with placeholders;
  date format culture now follows the selected language (NoteDateCulture key) instead of
  hardcoded it-IT, so month names match the UI language.

16 new resw keys ×6 (aligned at 596). Build 0/0, test 222/222. Collaudato EN+IT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AddIdentity.Text had a literal "+ " prefix in all 6 langs while the button already
renders a "+" FontIcon (E710), causing a doubled "+ + Add identity". Stripped the
prefix to match the Passwords/Cards add buttons. resw values only (aligned at 596).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
i18n (14 new resw keys ×6, aligned at 610):
- Filter "All categories" + announce, section headers Pinned/Notes, pinned a11y status,
  no-results announce, char/word count + announce, unsaved/saving/saved announces, pin/unpin names.
- Category filter "All categories" gets a neutral grey dot so it aligns with the coloured
  category dots (was a large dot-to-text gap).
- Filter button tooltip + accessible name localized (NoteFilterTooltip) via code-behind.

Layout redesign (per user mock-up):
- Master panel header now shows only the page title + filter icon (title NoWrap → fits on a
  single line in every language; previously wrapped in all but English).
- Search box + Add button moved to a top-right toolbar in the detail panel.

Build 0/0, test 222/222. Collaudato EN+IT+DE/FR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SecureNoteDetailView: add a Cancel button to the footer (Delete · Pin · — · Cancel · Save).
  Reuses ButtonCancel; the Cancelled action was already wired to CloseEditor in the list VM.
- ButtonAddNote label now includes the noun: "Add note"/"Aggiungi nota" (was bare "Add").
- UI-02 (harmonize add-button labels to the "Add X" convention across all four lists):
  AddPasswordLabel "New password" -> "Add password", AddCardLabel "New card" -> "Add card"
  (×6 langs). Identities/Notes already used "Add X".

resw aligned at 610. Build 0/0, test 222/222. Collaudato EN+IT+DE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dicator

UI-03: Passwords/Cards/Identities search box MaxWidth 300 -> 280, matching Dashboard
and Secure Notes (consistent search width across all pages).

UI-04: colour the NavigationView selection indicator per category, matching the Dashboard
colour coding — Passwords #0078D4, Cards #8764B8, Identities #00B294, Notes #FFB900
(per-item NavigationViewSelectionIndicatorForeground overrides). Non-category pages
(Dashboard/Generator/Verifier/Help/Settings/Lock) use a theme-reactive neutral grey defined
in the theme dictionaries (Light #5F5F5F, Dark #A6A6A6) for proper contrast in both themes.

Build 0/0, test 222/222. Collaudato light+dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GeneratorView static crack-time helpers (added a static ResourceLoader):
- GetCrackTimeLabel / LocalizeCrackTimeString: instant/seconds/centuries/millennia +
  {n} minutes/hours/days/years -> 8 resw keys ×6.
- Announce on generate/copy localized (GeneratorPwGenerated / GeneratorPwCopied).

10 new resw keys ×6 (aligned at 620). Build 0/0, test 222/222. Localization collaudata EN+IT.
Note: separate Generator quality issues (dark-theme letter colour, recent-passwords box,
crack-time bucketing, missing visible copy feedback) tracked next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#1 Copy feedback: GeneratorViewModel injects IToastService -> shows "Copied" toast on
   password/history copy (consistent with other pages); history copy tooltip localized.
#3 Dark-theme password syntax colours: code-behind resolved brushes via
   Application.Current.Resources[...] which is NOT theme-aware for ThemeDictionaries keys.
   Added ThemeBrush(key) (resolves by ActualTheme) + re-render on ActualThemeChanged.
#5 Recent-passwords boxes: moved to theme-aware XAML Styles (ThemeResource); inactive
   strength segments use ClearValue to revert to the XAML ThemeResource.
#4 Fade: WinUI 3 has no OpacityMask (WMC0011) and the overlay-gradient double-drew on the
   semi-transparent card in dark theme -> replaced with a clean hard clip.
#2 Crack-time: refined high-end buckets (thousand/million/billion years + "over a trillion
   years") so it no longer jumps straight from years to millennia. 4 resw keys swapped ×6.

Build 0/0, test 222/222. Collaudato light+dark. (Verifier crack-time localization: C-c2.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uggestions, issues)

- New shared Helpers/CrackTimeFormatter: single crack-time localization used by both the
  Generator and the Verifier (Generator refactored to use it; removed its duplicate methods).
- Verifier crack-time now localized (was raw "trillionyears").
- Strength label reuses the existing Strength* keys (was hardcoded "Molto forte" etc.).
- Expander headers "Compromised/Weak/Reused passwords (N)" -> formatted resw keys.
- 7 improvement suggestions localized.
- Issue rows "(untitled)", "{n} breaches", "reused" localized.

13 new resw keys ×6 (aligned at 635). Build 0/0, test 222/222. Collaudato EN+IT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tions

Import errors now use error CODES (Core/Orchestrator stay i18n-free), mapped to localized
strings in SettingsView.OnOperationError (same pattern as WRONG_PASSWORD/INVALID_FILE):
- BitwardenImporter (Core): encrypted export -> IMPORT_BW_ENCRYPTED
- ImportOrchestrator: BW zip missing data.json -> IMPORT_BW_ZIP; .1pux missing export.data
  -> IMPORT_1PUX (now ImportFileException); KeePass 1.x -> IMPORT_KEEPASS_1X; open failure
  -> IMPORT_KEEPASS_OPEN
- SettingsViewModel: no recognizable entries -> IMPORT_NO_ENTRIES
- File-picker descriptions (Bitwarden/KeePass) localized

8 new resw keys ×6 (aligned at 643). Build 0/0, test 222/222 (updated BitwardenImporter test
to assert the error code). Collaudato EN+IT with crafted fixture files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… fixes

i18n (15 new resw keys ×6, aligned at 658):
- TOTP error feedback (no QR / not otpauth / empty clipboard / invalid Base32 / icon too large)
- Login errors (reuse LoginIncorrectPassword + new LoginUnlockFailed)
- SetupView strength labels (reuse Strength*), error button, generic create-error message
- Shell update-available InfoBar title, ActivityLog CSV filename, CardCategory.Online
- Fallback display names (Card/Identity/Note) — BaseDetailViewModel._res made protected

Quality fixes folded in:
- SetupView no longer dumps ex.ToString() (stack trace) to the UI — shows a generic localized
  message and logs via Debug (QUAL-01).
- TOTP/icon error feedback converted from hover-only ToolTipService tooltips to visible toasts
  (ToastSeverity.Warning) — they were effectively invisible before.
- DashboardViewModel: removed dead hardcoded Italian default labels (overwritten by code-behind).

Final sweep: no user-facing Italian literals remain (only a comment + dead PanelTitle/P2).
Build 0/0, test 222/222. Collaudato EN+IT. Closes Category C / the bulk of LOC-01 i18n.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Added CenterOnScreen() (called from the MainWindow constructor): positions the window at the
centre of the WorkArea of the display it is created on (DisplayArea.GetFromWindowId +
AppWindow.Move). Multi-monitor aware (DisplayAreaFallback.Nearest); centres only at startup,
not on every tray-restore, so a user-moved window keeps its position.

Build 0/0, test 222/222.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t brush

- App.xaml.cs: browser IPC startup failure now logged via Debug.WriteLine (was empty catch).
- DashboardView.xaml.cs: invalid recent-item URL now logged (was empty catch).
- PasswordDetailView.xaml: TOTP countdown ProgressRing Foreground used the SystemAccentColor
  *Color* on a Brush property -> switched to AccentFillColorDefaultBrush (proper theme brush).

Build 0/0, test 222/222.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ssage

SettingsViewModel backup/restore/import generic catches surfaced the raw framework
ex.Message (often English, may leak internals). Now they log via Debug.WriteLine and show a
generic localized message (OperationGenericError / OPERATION_FAILED), mapped in
SettingsView + WelcomeView. Import keeps a dedicated catch (ImportFileException) BEFORE the
generic one so the localized import error codes are preserved.

1 new resw key ×6 (aligned at 659). Build 0/0, test 222/222. Collaudato EN+IT
(import-specific messages intact; generic error via unsupported-version .pkbak fixture).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- GeneratorViewModel: removed unused GenerateAndCopy command (no XAML binding) +
  orphaned ButtonGenerateAndCopy resw key.
- IdentitiesListViewModel: removed unused CopyEmail/CopyPhone commands (no UI).
- PasswordVerifierView: removed AuditLoadingRing ProgressRing (never activated) + the
  code that disabled it. Kept Identities sort logic (functional, not dead).

Build 0/0, test 222/222. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pexatar and others added 8 commits June 2, 2026 16:47
Passwords/Cards/Identities search boxes used MaxWidth + HorizontalAlignment=Right in a star
column, which made the AutoSuggestBox size to its content (placeholder when empty, then
shrinking to the typed text and growing per character). Switched to a fixed Width=280 so the
box stays a stable 280px regardless of content, matching Dashboard and Secure Notes.

Build 0/0, test 222/222. Collaudato.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…a x:Uid

Validates the x:Uid attached-property localization technique at runtime, including
ToolTipService.ToolTip (Uid.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip).
ActivityLog Back/Export buttons: removed inline IT, added x:Uid + 3 resw keys ×6.

Build 0/0, test 222/222. Tooltip confirmed localized at runtime (EN+IT).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OnWindowClosing showed the close-confirmation ContentDialog via a direct ShowAsync(),
bypassing the shared dialog queue. If another ContentDialog was open (or the X was clicked
again), WinUI threw "Only a single ContentDialog can be open at any time" as an unhandled
exception. Now the close prompt is routed through IDialogQueueService.EnqueueAndWait
(serialized with all other dialogs) and guarded by a _closePromptOpen re-entrancy flag.

Build 0/0, test 222/222. Collaudato (normal close, repeated X, close while a dialog is open).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LOC-02 full rollout: localize remaining hardcoded strings (tooltips,
AutomationProperties, placeholders) across Dashboard, Shell, Welcome,
Settings, Generator, Login, Setup, Verifier and the list/detail views
via x:Uid, with matching resw keys in all 6 languages.

Gate v2.0.0 cluster fixes:
- i18n: localized placeholders for Identity (15), Password (3) and the
  credit-card PIN field; example data localized per language (G1/G2/G6).
- i18n: password Save button uses resources instead of hardcoded
  "Salva"/"Salvataggio..." (G5).
- ux: remove the redundant category colour dot in the Secure Note editor (U1).
- ux: auto-lock shows a 10s "stay active" warning for the 30s tier,
  consistent with the longer tiers (U2).
- extensions (Chrome+Firefox): fix invisible action icons — setSvgIcon
  parsed inline SVG as image/svg+xml without an xmlns, leaving icons in the
  null namespace; now parsed as text/html. Copy actions always visible with
  clear affordance (U3).
- cleanup: remove dead PanelTitle/GetPanelTitle code from the detail VMs.

Verified: build x64 0/0, tests 222/222, localization audit clean, manual
Gate v2.0.0 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ifier (U4)

System theme brushes fetched via Application.Current.Resources (e.g.
TextFillColorSecondaryBrush) do not honour the active theme and always
returned the light variant, making secondary text and the "reused" dots in
the Verifier "Vault" tab unreadable on dark backgrounds.

- add theme-aware MutedTextBrush (Light/Dark) to ThemeColors.xaml
- PasswordVerifierView: use MutedTextBrush; strength-bar empty track now
  reverts to the XAML {ThemeResource} via ClearValue (matches GeneratorView)
- same latent bug fixed in SecureNotesListView (category filter dot) and
  EmptyStateControl (badge fallback)

Build 0/0, tests 222/222.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the March/v1.x Italian screenshots with fresh English-UI captures
from the v2.0.0 build (Cluster 3/4/5 features, localization, new look).
Drop the granular settings/help sub-screenshots that were unreferenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nsions)

- PassKey.Desktop.csproj: 1.0.17 -> 2.0.0 (Version/Assembly/File)
- Installer/PassKey.iss: AppVersion 1.0.17 -> 2.0.0
- README download links -> v2.0.0
- chrome/firefox manifests: 1.0.0 -> 1.0.1 (ships the U3 popup icon fix)

Build 0/0, tests 222/222.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update dashboard, password and card screenshots on pass-key.it to the
v2.0.0 English-UI build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pexatar pexatar merged commit 4445f60 into main Jun 3, 2026
1 check passed
@pexatar pexatar deleted the release/v2.0.0 branch June 3, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant