feat: website favicons + vault health dashboard

Favicons
- PM.Favicon (new): THTTPClient/WinHTTP proxy to icons.duckduckgo.com.
  Native Windows TLS — no OpenSSL DLLs to ship (Indy would fail
  silently without them). 5 s timeout, max 3 redirects, 64 KB cap,
  magic-byte MIME sniffing.
- DB: vault_entries.icon_b64 TEXT (idempotent migration).
- Endpoints: POST /entries/{id}/icon stores a cached data URI without
  forcing a full PUT (which would re-encrypt the password). DELETE
  /entries/icons/all purges the cache.
- Bridge cmd://favicon/fetch?host=X&reqId=Y runs in an anonymous thread
  so the up-to-5 s HTTP GET doesn't block the main thread; result
  shipped back via Bridge.onFaviconResult(reqId, host, dataUri).
- Hostname validated on both sides (JS faviconHost + Delphi
  NormalizeHost) so brand labels like "Gitea" never leak upstream.
- Settings: opt-in "Fetch website icons" toggle (synced), three explicit
  actions (Fetch missing / Re-fetch all / Clear cache) that bypass the
  toggle — manual user actions always work.
- Entry card avatar shows <img> when cached, falls back to initials.
  onerror handler recovers silently from a corrupt data URI.

Vault health
- New sidebar Tools → "Vault health" view. Four category cards:
  Weak (strength < 50), Reused (same plaintext on ≥ 2 entries), Old
  (updated_at > 365d), Pwned (HIBP cache).
- Score 0-100 with colour band (Good/Fair/At risk/Critical).
- One-shot computation cached per session (healthCache), invalidated
  on lockVault, entry save, and the explicit "Recompute" button.
- "Fix" button on each item opens the slideover for the affected
  entry, unmasks the password, focuses it, and pulses the dice button
  — full context preserved, user decides how to fix.
- Click handler stopPropagation prevents the document-level
  "click outside slideover" listener from closing the panel that
  we just opened in the same click event.

Fixes
- openSlideover typo (lowercase O) → openSlideOver across all call
  sites. Was silently breaking the Authenticator card click and the
  Vault health Fix button.
- W1050 WideChar warning in PM.Favicon — replaced set-membership
  with explicit Ord-style range comparisons.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 00:05:54 +01:00
parent 33e4b4b614
commit ad5fb21a18
11 changed files with 967 additions and 5 deletions
+30
View File
@@ -215,6 +215,10 @@
<svg><use href="#i-key"/></svg>
<span>TOTP generator</span>
</button>
<button class="nav-item" id="sidebarHealthBtn">
<svg><use href="#i-alert"/></svg>
<span>Vault health</span>
</button>
<button class="nav-item" id="sidebarImportBtn">
<svg><use href="#i-log-in"/></svg>
<span>Import vault</span>
@@ -364,6 +368,32 @@
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row" id="settingFaviconsRow">
<span>
Fetch website icons
<small class="setting-hint">
Loads a small icon for each entry via DuckDuckGo
(privacy-friendly proxy). Each entry's domain is
sent to <b>icons.duckduckgo.com</b> once and cached
locally. OFF by default.
</small>
</span>
<label class="toggle">
<input type="checkbox" id="settingFavicons">
<span class="toggle-slider"></span>
</label>
</div>
<div id="settingFaviconActionsRow" style="display:flex;gap:6px;margin-top:6px">
<button class="btn btn-ghost btn-sm" id="settingFaviconsRefresh">
<svg><use href="#i-rotate-ccw"/></svg> Fetch missing
</button>
<button class="btn btn-ghost btn-sm" id="settingFaviconsRefreshAll">
Re-fetch all
</button>
<button class="btn btn-ghost btn-sm is-danger" id="settingFaviconsClear">
<svg><use href="#i-trash"/></svg> Clear cache
</button>
</div>
</div>
<div class="slideover-field">