@bobfrankston/rmfmail 1.2.101 → 1.2.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/TODO.md CHANGED
@@ -783,8 +783,8 @@ Was part of S1 "local-first reconciliation refactor" until 2026-04-23 when S1's
783
783
  - [ ] **Full menu bar** — File, Edit, View, Message menus with keyboard shortcuts
784
784
  - [x] **Validate email addresses on send** — To/Cc/Bcc/From all vetted against `local@domain.tld` shape before send.
785
785
  - [x] **Undo move (Ctrl+Z)** — works for last move and last delete (whichever is more recent), 60s window.
786
- - [ ] **Message list management** — column resize (drag headers), column reorder, sort by clicking column headers (from/date/subject/size with secondary sort by date), show/hide columns (size, account) via View menu
787
- - [ ] **Generalize "Eleanor view" into finer view control [M]** — the View-menu "Eleanor view" checkbox (2026-07-04) is a named CSS preset: Thunderbird-classic one-line ★|Subject|From|Date table, extra whitespace, monochrome rows. Replace the one-off preset with real per-view settings — column set + order, row density (compact/normal/airy), row tinting on/off, snippet on/off — persisted per folder or as named view profiles. The preset becomes just a saved profile once profiles exist.
786
+ - [ ] **Message list management** — column resize (drag headers), drag-to-rearrange column order, sort by clicking column headers (from/date/subject/size with secondary sort by date), show/hide columns (size, account) via View menu. Column layout persists LOCALLY (per-machine, localStorage) — explicit user request 2026-07-04.
787
+ - [ ] **Generalize "Eleanor view" into finer view control [M]** — the View-menu "Eleanor view" checkbox (2026-07-04, reworked same day per feedback) is a named CSS preset: always-two-line card list (FromDate / Subject), outlined entries, extra vertical whitespace, monochrome rows, no avatar/snippet, at every width. Replace the one-off preset with real per-view settings — column set + order, row density (compact/normal/airy), row tinting on/off, snippet on/off — persisted per folder or as named view profiles. The preset becomes just a saved profile once profiles exist.
788
788
  - [ ] **Duplicate Message-ID handling** — detect messages with the same Message-ID (copies from multiple accounts, re-delivered mail). Options: deduplicate in unified inbox, show indicator, or let user choose which to keep.
789
789
 
790
790
  ### msger / mailx factoring
@@ -3117,6 +3117,7 @@ __export(message_list_exports, {
3117
3117
  clearSearchMode: () => clearSearchMode,
3118
3118
  exitMultiSelect: () => exitMultiSelect,
3119
3119
  getCurrentFocused: () => getCurrentFocused,
3120
+ getCurrentView: () => getCurrentView,
3120
3121
  getDateBasis: () => getDateBasis,
3121
3122
  getSelectedMessages: () => getSelectedMessages,
3122
3123
  initMessageList: () => initMessageList,
@@ -3954,8 +3955,20 @@ async function loadSearchResults(query, scope = "all", accountId = "", folderId
3954
3955
  body.innerHTML = `<div class="ml-empty">Search error: ${e.message}</div>`;
3955
3956
  }
3956
3957
  }
3957
- function revealMessage(accountId, folderId, uid, specialUse = "") {
3958
- loadMessages(accountId, folderId, 1, specialUse, true, uid);
3958
+ async function revealMessage(accountId, folderId, uid, specialUse = "") {
3959
+ const prevUnified = unifiedMode;
3960
+ const prevAcct = currentAccountId2, prevFolder = currentFolderId, prevSpecial = currentSpecialUse;
3961
+ await loadMessages(accountId, folderId, 1, specialUse, true, uid);
3962
+ if (getMessages2().some((m) => m.accountId === accountId && m.uid === uid))
3963
+ return true;
3964
+ if (!prevUnified && prevAcct && prevFolder)
3965
+ await loadMessages(prevAcct, prevFolder, 1, prevSpecial, true);
3966
+ else
3967
+ await loadUnifiedInbox(true);
3968
+ return false;
3969
+ }
3970
+ function getCurrentView() {
3971
+ return { unified: unifiedMode, accountId: currentAccountId2, folderId: currentFolderId, specialUse: currentSpecialUse };
3959
3972
  }
3960
3973
  async function loadMessages(accountId, folderId, page = 1, specialUse = "", autoSelect = true, focusUid) {
3961
3974
  const myGen = ++loadGen;
@@ -6571,6 +6584,18 @@ function setFolderSynced(accountId, folderId, syncedAt) {
6571
6584
  function getFolderSynced(accountId, folderId) {
6572
6585
  return folderLastSync.get(syncKey(accountId, folderId));
6573
6586
  }
6587
+ function highlightFolder(accountId, folderId) {
6588
+ const el = accountId === null || folderId === -1 ? document.querySelector(".ft-folder.ft-unified") : document.querySelector(`.ft-folder[data-account-id="${CSS.escape(accountId)}"][data-folder-id="${folderId}"]`);
6589
+ if (!el)
6590
+ return null;
6591
+ if (selectedElement)
6592
+ selectedElement.classList.remove("selected");
6593
+ el.classList.add("selected");
6594
+ selectedElement = el;
6595
+ selectedAccountId = accountId;
6596
+ selectedFolderId = folderId;
6597
+ return el.querySelector(".ft-folder-name")?.textContent || null;
6598
+ }
6574
6599
  function formatAge(ms) {
6575
6600
  const secs = Math.round(ms / 1e3);
6576
6601
  if (secs < 60)
@@ -9410,19 +9435,31 @@ var currentAccountId3 = "";
9410
9435
  var currentFolderId2 = 0;
9411
9436
  var reloadDebounceTimer = null;
9412
9437
  var lastListReloadAt = 0;
9413
- function closeSearchAndReveal() {
9438
+ async function closeSearchAndReveal() {
9414
9439
  const focused = getCurrentFocused();
9415
9440
  clearSearchMode();
9416
9441
  const body = document.getElementById("ml-body");
9417
9442
  if (body) body.querySelectorAll(".filter-hidden").forEach((r) => r.classList.remove("filter-hidden"));
9418
9443
  if (focused && focused.folderId) {
9419
- currentAccountId3 = focused.accountId;
9420
- currentFolderId2 = focused.folderId;
9421
- revealMessage(focused.accountId, focused.folderId, focused.uid);
9444
+ await revealMessage(focused.accountId, focused.folderId, focused.uid);
9422
9445
  } else {
9423
9446
  reloadCurrentFolder();
9424
9447
  }
9425
- setTitle(APP_NAME);
9448
+ const vw = getCurrentView();
9449
+ if (vw.unified) {
9450
+ highlightFolder(null, -1);
9451
+ setActiveView({ kind: "unified" }, "All Inboxes");
9452
+ setTitle(`${APP_NAME} - All Inboxes`);
9453
+ setNarrowFolderTitle("All Inboxes");
9454
+ } else {
9455
+ currentAccountId3 = vw.accountId;
9456
+ currentFolderId2 = vw.folderId;
9457
+ currentFolderSpecialUse = vw.specialUse;
9458
+ const name = highlightFolder(vw.accountId, vw.folderId) || "Mail";
9459
+ setActiveView({ kind: "folder", accountId: vw.accountId, folderId: vw.folderId, specialUse: vw.specialUse }, name);
9460
+ setTitle(`${APP_NAME} - ${name}`);
9461
+ setNarrowFolderTitle(name);
9462
+ }
9426
9463
  }
9427
9464
  searchInput?.addEventListener("input", () => {
9428
9465
  clearTimeout(searchTimeout);