Both UPDATEs mutated a synced column without touching updated_at, so the
change rode in the sync snapshot but other devices skipped it (last-write-
wins saw "not newer"). Now both SET updated_at = datetime('now') (UTC).
- POST /entries/{id}/icon (PM.Handler.Entries)
- folder delete → entries reassigned to 'All' (PM.Handler.Folders)
accessed_at stays exempt (read timestamp, not synced); bulk "clear all
icons" stays exempt (device-local favicon cache purge). Invariant documented
in CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Bitwarden CSV import: folders auto-created server-side; notes column on
login rows surfaces as a "Notes" custom field instead of polluting tags;
type=card / type=identity rows now mapped to kind=note with the
credit-card / identity template + card_* / identity_* columns
pulled into custom_fields; `fields` column parsed (Bitwarden's
"label: value\nlabel: value" lines + our own JSON shape).
- Settings panel search: live filter at top of the panel, matches each
.setting-row individually, hides whole section when no row matches,
shows a "No matches" banner. Esc clears query (without closing
Settings); Esc with empty query closes the panel.
- Quick-search hotkey customizable: SetQuickSearchHotkey added to
PM.Bridge; cmd://autofill/hotkeys extended with qs_mods/qs_vk
(independent of the autofill enabled flag — quick-search stays
armed even when autofill is off); state.quickSearchHotkey synced
via settings_json; new "Quick search picker" row in Settings.
- FireDAC SQLite folder POST/PUT: pre-declare ftString on color/icon
params so .Clear (NULL) doesn't trip "[FireDAC][Phys][SQLite]-335
type unknown" at Prepare — was crashing the CSV-import folder
auto-creation path.
- Edge form-data autocomplete suppressed on slideover inputs (title,
site, username, password, TOTP, note body, custom fields):
autocomplete=off (new-password on secrets) + spellcheck=false. Fixes
the "Informations enregistrées" dropdown popping over data after a
field was edited.
- closeSlideOver blurs any focused descendant before removing .is-open
so an invisible focused field can't react to arrow-down / backspace
after dismissal.
- Slideover Esc handler upgraded to capture phase so it fires before
the input's own keydown or browser-level Esc swallow on the active
autocomplete popup.
- Settings panel Esc closes the panel when search input is empty;
search keeps the keystroke when it has a query to clear.
- Discard-fantome on note open: customFields working copy and
originalCustomJson now share the SAME normalized array — comparing
raw plainCustom against the .map()'d working copy made notes look
dirty on open.
- Delete / Backspace global shortcut: batch-trash on normal views,
batch perm-delete on trash view, gated on selection + no input
focused + no modal up.
- Toggle thumb vertical centering via top:50% + translateY(-50%);
state checked uses translate(16px, -50%) to keep the centring.
- Batch bar disappears after per-card restore/perm-delete/trash:
state.checked.delete(id) before render for the relevant flows;
state.checked.clear() before render in emptyTrash and the new
moveEntriesToFolder helper.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Introduces the Delphi 12 FMX backend (PMServer) that hosts the embedded
WebView2 vault on 127.0.0.1, and a native bridge between JS and Delphi
that wires three privacy-focused features:
1. Secure clipboard
Copying a password registers the Win32 "ExcludeClipboardContentFromMonitorProcessing"
format alongside CF_UNICODETEXT, so Win+V clipboard history never sees
the value. Auto-clears after 30s via TTimer. Bridge.copySecure() in
app.js routes all password/username/secret copy paths through the
native layer when running inside the Delphi WebView2 (falls back to
navigator.clipboard for the PHP standalone).
2. Tray icon (X-to-tray when server running)
Closing the dev panel hides both the form HWND and the TFMAppClass
per-process proxy window that owns the FMX taskbar entry — the form's
HWND alone is not the taskbar-visible one in FMX (took some iteration
to discover). Tray menu: Open, Lock vault, Quit. Clipboard is force-
cleared on minimize as extra safety. First-time minimize fires a
balloon notification so the user knows the app is still running.
3. Auto-lock on Windows session lock (Win+L)
wtsapi32.dll!WTSRegisterSessionNotification on a dedicated message-only
window. On WM_WTSSESSION_CHANGE / WTS_SESSION_LOCK, the bridge calls
ExecuteJavaScript('lockVault()'). Same path used by the tray "Lock vault"
menu item.
Bridge architecture:
- JS → Delphi via cmd:// URLs intercepted in OnBeforeNavigate
(pattern lifted from DeskInsight Monaco). Currently exposes
cmd://clipboard/copy?text=...&clear=... and cmd://clipboard/clear.
- Delphi → JS via TTMSFNCWebBrowser.ExecuteJavaScript with guarded
calls (typeof check) so the bridge degrades cleanly if app.js isn't
loaded yet.
Files:
- Source/PM.Bridge.pas (new) — TSecureClipboard + TPMBridge
- UMainForm.pas/.fmx — bridge wiring, FormCloseQuery intercept, tray
callbacks (BridgeTrayRestore / BridgeLockRequest / BridgeQuit)
- js/app.js — Bridge object, 5 navigator.clipboard sites migrated to
Bridge.copySecure with PHP-compatible fallback, Bridge.onTrayRestore
handler that resets the auto-lock timer
.gitignore extended with Delphi build artifacts (*.dcu, Win32/, Win64/,
__history/, __recovery/, *.identcache, *.dsk, *.local, etc.) so source
checkouts stay clean.