feat: quick-search fill modes + editable custom-field combobox + JS build gate

Quick search (Ctrl+Shift+Q fill mode)
- Enter / left-click → full autofill (username + Tab + password), like
  Ctrl+Shift+L.
- Shift+Enter / right-click → username only (new Delphi username-only
  SendInput path via field=user; ExecuteAutofill AUsernameOnly param).
- Ctrl+Enter / Ctrl+click → password only.
- Copy mode (tray / palette) unchanged: Enter/left = password,
  Shift+Enter/right = username.
- Clipboard fix: copy-then-minimise no longer wipes the just-copied
  password — MinimizeToTray takes an AClearClipboard flag (False on the
  quick-search copy path, driven by app/minimize?keepclip=1). The 30s
  auto-clear still guards it.
- Right-click on a result row suppresses the native/custom context menu
  (preventDefault + stopPropagation).

Editable custom-field combobox
- Option-backed custom fields (card brand, expiry year/month, etc.) now
  render a custom editable combobox instead of a locked <select>: an
  arrow drops a menu of ALL options (a native <datalist> filtered to the
  typed text, which confused users), while the input stays freely
  typeable for values not in the list. Storage shape unchanged.
- Outside-click closes the menu via the existing slideover mousedown
  handler; item mousedown + preventDefault so blur doesn't race the pick.

Build safety
- BuildAssets.ps1 runs `node --check` on every embedded .js before
  generating assets.res. A syntax error now aborts the asset build
  (exit 1, file + line logged) instead of shipping a dead bundle that
  only surfaces after a full Delphi rebuild. Node is optional: absent →
  warn and continue.

Docs
- CODE_AUDIT.md: full static-analysis report (security, latent bugs,
  maintainability, future features, prioritized action plan).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-03 08:13:25 +01:00
parent 7440d07793
commit 3076fec710
7 changed files with 507 additions and 73 deletions
+26 -7
View File
@@ -129,8 +129,11 @@ type
public
constructor Create(AMainForm: TForm);
destructor Destroy; override;
// Hide main window and show tray icon.
procedure MinimizeToTray;
// Hide main window and show tray icon. AClearClipboard defaults to
// True (a manual minimise wipes any copied password immediately);
// the quick-search "copy then hide so I can paste" flow passes False
// so it doesn't nuke the password it just placed on the clipboard.
procedure MinimizeToTray(AClearClipboard: Boolean = True);
// Restore main window and remove tray icon.
procedure RestoreFromTray;
// Apply Windows dark-mode title bar to the main form. Win10 19044+
@@ -160,8 +163,10 @@ type
// If AUsername is empty, only the password is typed (no Tab) — matches
// the password-only hotkey path AND avoids spurious Tab on entries
// without a stored username.
// AUsernameOnly = True → type ONLY the username (no Tab, no password);
// used by the quick-search "autofill username" action.
procedure ExecuteAutofill(ATargetHWND: HWND;
const AUsername, APassword: string);
const AUsername, APassword: string; AUsernameOnly: Boolean = False);
property SecureClipboard: TSecureClipboard read FSecureClipboard;
property TrayAdded: Boolean read FTrayAdded;
property AutofillRegistered: Boolean read FAutofillRegistered;
@@ -551,7 +556,7 @@ begin
until LWnd = 0;
end;
procedure TPMBridge.MinimizeToTray;
procedure TPMBridge.MinimizeToTray(AClearClipboard: Boolean = True);
var
LFormHwnd, LAppHwnd: HWND;
begin
@@ -561,8 +566,12 @@ begin
// Extra safety: clear the clipboard immediately when the user minimizes,
// rather than waiting for the 30s auto-clear timer to fire. A password
// the user just copied shouldn't sit in the clipboard while the app is
// out of sight.
FSecureClipboard.Clear;
// out of sight. SKIPPED when AClearClipboard=False — the quick-search
// copy-then-hide flow deliberately keeps the password on the clipboard
// (the 30s auto-clear timer still guards it) so the user can paste it
// into their target app after we minimise.
if AClearClipboard then
FSecureClipboard.Clear;
LFormHwnd := MainFormHWND(FMainForm);
LAppHwnd := FindFMXAppWindow;
@@ -1115,7 +1124,7 @@ begin
end;
procedure TPMBridge.ExecuteAutofill(ATargetHWND: HWND;
const AUsername, APassword: string);
const AUsername, APassword: string; AUsernameOnly: Boolean = False);
const
MinimizeSettleMs = 80;
FocusSettleDelayMs = 120;
@@ -1135,6 +1144,16 @@ begin
WaitForModifierRelease(1000);
Sleep(FocusSettleDelayMs);
// Username-only: type just the username into the focused field, no Tab,
// no password. Used by the quick-search right-click / Shift+Enter path.
if AUsernameOnly then
begin
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(AUsername);
Exit;
end;
if AUsername = '' then
begin
SendSelectAllAndDelete;