@bobfrankston/rmfmail 1.1.243 → 1.1.245
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.bundle.js +14 -9
- package/client/app.bundle.js.map +2 -2
- package/client/app.js +30 -17
- package/client/app.js.map +1 -1
- package/client/app.ts +31 -15
- package/client/compose/compose.bundle.js +145 -518
- package/client/compose/compose.bundle.js.map +3 -3
- package/client/compose/spellcheck.js +209 -758
- package/client/compose/spellcheck.js.map +1 -1
- package/client/compose/spellcheck.ts +209 -707
- package/package.json +5 -5
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +15 -0
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +12 -0
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
package/client/app.ts
CHANGED
|
@@ -1418,7 +1418,7 @@ interface MovedBatch {
|
|
|
1418
1418
|
// when older entries get shifted off the front of the stack).
|
|
1419
1419
|
type UndoFlagEntry = { accountId: string; uid: number; prevFlagged: boolean };
|
|
1420
1420
|
type UndoOp =
|
|
1421
|
-
| { kind: "delete"; at: number; payload: DeletedMessage }
|
|
1421
|
+
| { kind: "delete"; at: number; payload: DeletedMessage[] }
|
|
1422
1422
|
| { kind: "move"; at: number; payload: MovedBatch }
|
|
1423
1423
|
| { kind: "flag"; at: number; payload: UndoFlagEntry[] };
|
|
1424
1424
|
|
|
@@ -1466,6 +1466,18 @@ async function deleteSelectedMessages(): Promise<void> {
|
|
|
1466
1466
|
selected.push({ accountId: current.accountId, uid: current.message.uid, folderId: current.message.folderId });
|
|
1467
1467
|
}
|
|
1468
1468
|
|
|
1469
|
+
// SAFETY GATE: confirm before a BULK delete. `Ctrl+A` selects every
|
|
1470
|
+
// visible row, and in the "All Inboxes" view that's a scattered screenful
|
|
1471
|
+
// across accounts — so a single Ctrl+A then Delete could silently trash
|
|
1472
|
+
// dozens of messages with no prompt. That is exactly what trashed 114 of
|
|
1473
|
+
// Bob's messages on 2026-06-12. A single-message delete (the common quick-
|
|
1474
|
+
// triage case) stays instant; anything larger must be confirmed.
|
|
1475
|
+
if (selected.length > 1) {
|
|
1476
|
+
if (!confirm(`Move ${selected.length} messages to Trash?\n\n(Ctrl+Z restores them if this was a mistake.)`)) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1469
1481
|
const statusSync = document.getElementById("status-sync");
|
|
1470
1482
|
|
|
1471
1483
|
// Optimistic UI: remove from list IMMEDIATELY, then queue the IPC.
|
|
@@ -1477,17 +1489,14 @@ async function deleteSelectedMessages(): Promise<void> {
|
|
|
1477
1489
|
const snapshot = [...selected];
|
|
1478
1490
|
removeMessagesAndReconcile(selected);
|
|
1479
1491
|
|
|
1480
|
-
// Undo
|
|
1481
|
-
//
|
|
1482
|
-
//
|
|
1483
|
-
//
|
|
1484
|
-
|
|
1485
|
-
if (snapshot.length === 1
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
} else {
|
|
1489
|
-
if (statusSync) statusSync.textContent = `Trashed ${snapshot.length} messages (syncing)`;
|
|
1490
|
-
}
|
|
1492
|
+
// Undo restores the WHOLE batch, not just the first message — a bulk
|
|
1493
|
+
// delete must be fully recoverable via Ctrl+Z (the old single-slot undo
|
|
1494
|
+
// left the other N-1 unrecoverable, which made an accidental mass-delete
|
|
1495
|
+
// unrecoverable; Bob 2026-06-12).
|
|
1496
|
+
pushUndo({ kind: "delete", at: Date.now(), payload: snapshot.map(m => ({ ...m, subject: "" })) });
|
|
1497
|
+
if (statusSync) statusSync.textContent = snapshot.length === 1
|
|
1498
|
+
? `Trashed 1 message (syncing) — Ctrl+Z to undo`
|
|
1499
|
+
: `Trashed ${snapshot.length} messages (syncing) — Ctrl+Z to undo`;
|
|
1491
1500
|
|
|
1492
1501
|
// Fire-and-forget per local-first: optimistic remove above already
|
|
1493
1502
|
// updated the UI; the daemon-side trash is sync DB + queued IMAP.
|
|
@@ -1519,9 +1528,16 @@ async function performUndo(): Promise<void> {
|
|
|
1519
1528
|
const statusSync = document.getElementById("status-sync");
|
|
1520
1529
|
try {
|
|
1521
1530
|
if (op.kind === "delete") {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1531
|
+
// payload is the full batch (was a single object pre-2026-06-12;
|
|
1532
|
+
// tolerate both shapes so an in-flight undo from an old build
|
|
1533
|
+
// doesn't throw).
|
|
1534
|
+
const msgs = Array.isArray(op.payload) ? op.payload : [op.payload];
|
|
1535
|
+
for (const m of msgs) {
|
|
1536
|
+
await undeleteMessage(m.accountId, m.uid, m.folderId);
|
|
1537
|
+
}
|
|
1538
|
+
if (statusSync) statusSync.textContent = msgs.length === 1
|
|
1539
|
+
? "Message restored"
|
|
1540
|
+
: `Restored ${msgs.length} messages`;
|
|
1525
1541
|
} else if (op.kind === "move") {
|
|
1526
1542
|
const { messages } = op.payload;
|
|
1527
1543
|
const byDest = new Map<string, { accountId: string; folderId: number; uids: number[] }>();
|