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>
This commit is contained in:
r-zakarya
2026-07-11 18:32:37 +01:00
parent 92ed153bc0
commit a42e4b205d
15 changed files with 381 additions and 2173 deletions
+52 -41
View File
@@ -131,10 +131,6 @@
<svg><use href="#i-unlock"/></svg>
Unlock
</button>
<button id="passkeyBtn" type="button" class="btn btn-ghost btn-block" style="display:none">
<svg><use href="#i-lock"/></svg>
Use a passkey
</button>
<button id="recoveryBtn" type="button" class="btn btn-link btn-block" style="font-size:12px;margin-top:4px">
Forgot master password? Use a recovery code
</button>
@@ -422,6 +418,16 @@
<button class="settings-tab" data-tab="sync"><svg><use href="#i-rotate-ccw"/></svg>Sync &amp; Backup</button>
</div>
<div class="slideover-body" id="settingsBody">
<div class="slideover-field">
<div class="slideover-field-label">Getting started</div>
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
A quick guided tour of the main features — search, autofill,
tools and sync.
</p>
<button class="btn btn-ghost btn-sm" id="startTourBtn">
<svg><use href="#i-log-in"/></svg> How it works
</button>
</div>
<div class="slideover-field">
<div class="slideover-field-label">Appearance</div>
<div class="setting-row">
@@ -515,6 +521,38 @@
</div>
</div>
<div class="slideover-field">
<div class="slideover-field-label">Startup &amp; tray</div>
<div class="setting-row" id="settingTrayNotifRow">
<span>
Show tray notifications
<small class="setting-hint">
The first time the app minimises to the tray
it shows a small balloon explaining how to
restore it. Turn off if you've seen it.
</small>
</span>
<label class="toggle">
<input type="checkbox" id="settingTrayNotif">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row" id="settingAutoStartRow">
<span>
Start with Windows
<small class="setting-hint">
Launch this app in the tray when you sign in to
Windows — so the autofill hotkeys are ready
immediately. Device-only setting (per Windows user).
</small>
</span>
<label class="toggle">
<input type="checkbox" id="settingAutoStart">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="slideover-field">
<div class="slideover-field-label">Security</div>
<div class="setting-row">
@@ -574,34 +612,6 @@
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row" id="settingTrayNotifRow">
<span>
Show tray notifications
<small class="setting-hint">
The first time the app minimises to the tray
it shows a small balloon explaining how to
restore it. Turn off if you've seen it.
</small>
</span>
<label class="toggle">
<input type="checkbox" id="settingTrayNotif">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row" id="settingAutoStartRow">
<span>
Start with Windows
<small class="setting-hint">
Launch this app in the tray when you sign in to
Windows — so the autofill hotkeys are ready
immediately. Device-only setting (per Windows user).
</small>
</span>
<label class="toggle">
<input type="checkbox" id="settingAutoStart">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="slideover-field">
<div class="slideover-field-label">Autofill</div>
@@ -649,10 +659,11 @@
<div class="slideover-field">
<div class="slideover-field-label">Clipboard privacy</div>
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
Windows keeps copied passwords in <b>clipboard history (Win+V)</b>.
To prevent that, disable history in <i>Settings → System → Clipboard</i>.
Passwords copied from this app are <b>excluded from Windows
clipboard history (Win+V)</b> and cloud clipboard, and cleared
automatically when the app is minimised — no manual setup needed.
</p>
<button class="btn btn-ghost btn-sm" id="openClipboardSettings">Why?</button>
<button class="btn btn-ghost btn-sm" id="openClipboardSettings">How?</button>
</div>
<div class="slideover-field">
@@ -819,11 +830,15 @@
No PIN set.
</p>
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
Shortcut unlock with a 46 digit PIN. Stored DPAPI-encrypted on
Shortcut unlock with a 412 digit PIN. Stored DPAPI-encrypted on
this device. <b>Master password is still required for sensitive
actions</b> (export, change master password, recovery code…).
</p>
<div class="setting-row" style="margin:0 0 8px">
<div style="display:flex;gap:6px;flex-wrap:wrap;margin:0 0 8px">
<button class="btn btn-ghost btn-sm" id="pinSetBtn">Set PIN</button>
<button class="btn btn-ghost btn-sm is-danger" id="pinRemoveBtn" style="display:none">Remove PIN</button>
</div>
<div class="setting-row" style="margin:0">
<span>
Unlock method
<small class="setting-hint">Master pw (default), PIN only, or both required.</small>
@@ -834,10 +849,6 @@
<option value="both">Master password + PIN</option>
</select>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap">
<button class="btn btn-ghost btn-sm" id="pinSetBtn">Set PIN</button>
<button class="btn btn-ghost btn-sm is-danger" id="pinRemoveBtn" style="display:none">Remove PIN</button>
</div>
</div>
<div class="slideover-field">