feat: Bitwarden import bundle + settings search + quick-search hotkey + UX fixes

- 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>
This commit is contained in:
r-zakarya
2026-06-30 23:25:50 +01:00
parent 6869b7c692
commit bd14831449
7 changed files with 405 additions and 36 deletions
+54 -2
View File
@@ -438,10 +438,12 @@ input[type="range"]::-webkit-slider-thumb {
.toggle-slider::before {
content: '';
position: absolute;
left: 2px; top: 1px;
left: 2px;
top: 50%;
width: 14px; height: 14px;
background: var(--text-dim);
border-radius: 50%;
transform: translateY(-50%);
transition: all var(--t-fast);
}
.toggle input:checked + .toggle-slider {
@@ -450,7 +452,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.toggle input:checked + .toggle-slider::before {
background: white;
transform: translateX(16px);
transform: translate(16px, -50%);
}
/* ---- 6. APP SHELL ---------------------------------------- */
@@ -2085,6 +2087,56 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
}
.slideover-body { padding: 20px; overflow-y: auto; flex: 1; }
.slideover-field { margin-bottom: 16px; }
/* Settings panel search — sits below the header, above the body. Only
present inside #settingsPanel. */
.settings-search-wrap {
position: relative;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
}
.settings-search-icon {
position: absolute; left: 26px; top: 50%;
transform: translateY(-50%);
width: 14px; height: 14px;
color: var(--text-faint);
pointer-events: none;
}
.settings-search-input {
flex: 1;
padding: 7px 30px 7px 32px;
background: var(--bg-elev-2);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text);
font-size: 13px;
outline: none;
transition: border-color var(--t-fast);
}
.settings-search-input:focus { border-color: var(--accent); }
.settings-search-clear {
position: absolute; right: 22px; top: 50%;
transform: translateY(-50%);
background: transparent; border: 0; padding: 4px;
cursor: pointer;
color: var(--text-faint);
border-radius: 4px;
display: none;
}
.settings-search-clear svg { width: 12px; height: 12px; }
.settings-search-clear:hover { color: var(--text); background: var(--bg-elev-3); }
.settings-search-wrap.has-query .settings-search-clear { display: inline-flex; }
/* Hidden section + per-row + no-results banner driven by JS. */
.slideover-field.is-search-hidden,
.setting-row.is-search-hidden { display: none; }
.settings-no-results {
padding: 20px;
color: var(--text-faint);
font-size: 13px;
text-align: center;
display: none;
}
.settings-no-results.is-visible { display: block; }
.slideover-field-label { font-size: 11px; font-weight: 500; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
.slideover-field-value {
display: flex; align-items: center; gap: 6px;