@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 +2 -2
- package/client/app.bundle.js +44 -7
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +32 -11
- package/client/app.js.map +1 -1
- package/client/app.ts +31 -11
- package/client/components/folder-tree.js +18 -0
- package/client/components/folder-tree.js.map +1 -1
- package/client/components/folder-tree.ts +17 -0
- package/client/components/message-list.js +24 -2
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +22 -2
- package/client/styles/components.css +39 -28
- package/package.json +1 -1
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
|
|
787
|
-
- [ ] **Generalize "Eleanor view" into finer view control [M]** — the View-menu "Eleanor view" checkbox (2026-07-04) is a named CSS preset:
|
|
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 (From … Date / 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
|
package/client/app.bundle.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|