@bobfrankston/rmfmail 1.1.102 → 1.1.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/app.ts CHANGED
@@ -2900,20 +2900,26 @@ document.addEventListener("keydown", (e) => {
2900
2900
  }
2901
2901
  }
2902
2902
  // Ctrl+D or Delete = Delete selected messages.
2903
- // P15: don't hijack Delete inside text inputs / textareas / contenteditable
2904
- // JSONC editor's Delete key was being eaten because we always preventDefault'd.
2905
- // 2026-05-09 fix: if the LIST has multiple selected rows, the user
2906
- // clearly means "delete those" regardless of where focus is search
2907
- // input retaining focus after filtering shouldn't make the key dead.
2908
- // Only defer to native behavior when there's no list selection AND
2909
- // focus is in a text-editing surface.
2903
+ // P15: don't hijack Delete inside text inputs / textareas / contenteditable.
2904
+ // Single-selection-while-typing was the bug Bob reported 2026-05-20:
2905
+ // search input has focus, list has an auto-selected row, user presses
2906
+ // Delete to backspace a character mailx hard-deletes the row. Data loss.
2907
+ //
2908
+ // The 2026-05-09 multi-select carve-out (Delete-key works on a list
2909
+ // selection even when focus drifted into search) is preserved only for
2910
+ // *explicit* multi-select (>1 row). Single selection is ALWAYS the
2911
+ // auto-selected first row — never an explicit "delete this" intent when
2912
+ // you're typing in search. So:
2913
+ // - focus in editable + ≤1 selected → native text edit (delete char)
2914
+ // - focus in editable + >1 selected → user multi-selected on purpose, run delete
2915
+ // - focus elsewhere → run delete
2910
2916
  if ((e.ctrlKey && e.key === "d") || e.key === "Delete") {
2911
2917
  const t = e.target as HTMLElement | null;
2912
2918
  const tag = t?.tagName;
2913
2919
  const editable = t?.isContentEditable;
2914
2920
  const inEditable = tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || editable;
2915
2921
  const listSelectionCount = document.querySelectorAll("#ml-body .ml-row.selected").length;
2916
- if (inEditable && listSelectionCount === 0) return;
2922
+ if (inEditable && listSelectionCount <= 1) return;
2917
2923
  e.preventDefault();
2918
2924
  deleteSelection();
2919
2925
  }
@@ -3761,10 +3761,11 @@ function setupAutocomplete(input) {
3761
3761
  emailEl.textContent = r.email;
3762
3762
  const sourceEl = document.createElement("span");
3763
3763
  sourceEl.className = "ac-item-source";
3764
- sourceEl.textContent = r.source || "";
3764
+ const sourceList = r.sources && r.sources.length ? r.sources : r.source ? [r.source] : [];
3765
+ sourceEl.textContent = sourceList.join(", ");
3765
3766
  main.appendChild(nameEl);
3766
3767
  if (r.name) main.appendChild(emailEl);
3767
- if (r.source) main.appendChild(sourceEl);
3768
+ if (sourceList.length) main.appendChild(sourceEl);
3768
3769
  const actions = document.createElement("div");
3769
3770
  actions.className = "ac-item-actions";
3770
3771
  const mkBtn = (glyph, title, run) => {