@bobfrankston/rmfmail 1.2.172 → 1.2.173

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.
Files changed (40) hide show
  1. package/TODO.md +1 -0
  2. package/client/app.bundle.js +58 -0
  3. package/client/app.bundle.js.map +3 -3
  4. package/client/components/message-viewer.js +60 -0
  5. package/client/components/message-viewer.js.map +1 -1
  6. package/client/components/message-viewer.ts +60 -0
  7. package/client/compose/compose.bundle.js +7 -0
  8. package/client/compose/compose.bundle.js.map +2 -2
  9. package/client/lib/api-client.js +9 -0
  10. package/client/lib/api-client.js.map +1 -1
  11. package/client/lib/api-client.ts +9 -0
  12. package/client/lib/mailxapi.js +6 -0
  13. package/package.json +5 -5
  14. package/packages/mailx-imap/index.d.ts.map +1 -1
  15. package/packages/mailx-imap/index.js +22 -0
  16. package/packages/mailx-imap/index.js.map +1 -1
  17. package/packages/mailx-imap/index.ts +22 -0
  18. package/packages/mailx-imap/package-lock.json +2 -2
  19. package/packages/mailx-imap/package.json +1 -1
  20. package/packages/mailx-service/index.d.ts +23 -0
  21. package/packages/mailx-service/index.d.ts.map +1 -1
  22. package/packages/mailx-service/index.js +206 -0
  23. package/packages/mailx-service/index.js.map +1 -1
  24. package/packages/mailx-service/index.ts +198 -0
  25. package/packages/mailx-service/jsonrpc.js +2 -0
  26. package/packages/mailx-service/jsonrpc.js.map +1 -1
  27. package/packages/mailx-service/jsonrpc.ts +2 -0
  28. package/packages/mailx-service/package.json +1 -1
  29. package/packages/mailx-store/db.d.ts +3 -0
  30. package/packages/mailx-store/db.d.ts.map +1 -1
  31. package/packages/mailx-store/db.js +5 -0
  32. package/packages/mailx-store/db.js.map +1 -1
  33. package/packages/mailx-store/db.ts +8 -0
  34. package/packages/mailx-store/package.json +1 -1
  35. package/packages/mailx-store-web/package.json +1 -1
  36. package/packages/mailx-types/mailx-api.d.ts +8 -0
  37. package/packages/mailx-types/mailx-api.d.ts.map +1 -1
  38. package/packages/mailx-types/mailx-api.ts +4 -0
  39. package/packages/mailx-types/package.json +1 -1
  40. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-74140 → node_modules.npmglobalize-stash-7284}/.package-lock.json +0 -0
package/TODO.md CHANGED
@@ -22,6 +22,7 @@ These items don't need user input — source-only changes, compile-verified befo
22
22
  | **8** | **C157 — Android fresh install: paint folder list before message sync** [S/M] | S/M | First sync ran minutes with an empty tree. Prioritize per-account folder-LIST fetch + paint ahead of message sync so the skeleton appears in seconds; message counts stream in behind. |
23
23
  | **10** | **C159 — Harper local grammar check** [M] | M | **Desktop SHIPPED v1.2.168** (2026-07-22): daemon-side Harper engine + `grammarLint` IPC, generic squiggle overlay + suggestion popup across ALL THREE editors, Settings toggle default ON, quoted chains + spelling lints skipped. **Remaining: Android** (host LocalLinter in android-bootstrap, harper.js dist in APK assets like sql.js, ~+18 MB) + per-rule config + userdict merge. Details: `docs/harper-integration.md`. |
24
24
  | **9** | **C158 — Android double-init after fold-triggered reload** [M] | M | 2026-07-22 13:07:49 logit trail: ONE page reload after unfolding produced TWO module loads, duplicate "bridge installed", duplicate GDrive lookups, and THREE "Starting OAuth flow" launches. Activity did NOT recreate (ConfigChanges already declared; no OnCreate in trail) — find what reloads the WebView on fold and make android-bootstrap init idempotent (single-flight guard on the module-level init, dedupe OAuth intent launches). |
25
+ | **11** | **C160 — Attachment delete: Gmail + Android halves** [M] | M | Desktop IMAP shipped v1.2.173 (right-click chip → Delete one/all: verified local strip + append-confirm-then-delete server replace, .pre-strip.bak backup, UID rebind). Remaining: Gmail REST path (messages.insert + trash of original — changes message id, provider_id rebind) and Android (bridge method + web-service strip using its own parser). Strip helper lives in mailx-service (enumerateMimeLeafParts) — move to mailx-types when Android needs it. |
25
26
  | **5** | **C124 — mailto handler on macOS** (Linux ✅ 2026-07-08 v1.2.115) | S | Linux half shipped: `--register-mailto` on linux writes `~/.local/share/applications/rmfmail.desktop` (`MimeType=x-scheme-handler/mailto;`, quoted node+mailx.js Exec, bare `%u`), runs `xdg-mime default` + `update-desktop-database`; `--unregister-mailto` removes it. **Remaining: macOS** — real `.app` bundle (LaunchServices won't register a bare script) with `Info.plist` `CFBundleURLTypes` declaring `mailto` + a tiny launcher binary; `LSSetDefaultHandlerForURLScheme` programmatically sets the default. Rust `rmfmailto-src/` crate already portable — drop the Win32 imports and `cargo build --target x86_64-apple-darwin` produces the macOS launcher. Needs a Mac to build/verify. |
26
27
  | ~~7~~ | ~~**C154 — Audit Android bridge vs MailxApi contract**~~ — **DONE** (2026-07-08, v1.2.115). Bridge now has a generic contract-backstop: every service method not explicitly adapted gets a positional pass-through, so a missing method can never again fail as `parent bridge has no method "X"`. Wire-shape mismatches adapted explicitly (`readJsoncFile`/`writeJsoncFile`/`formatJsonc`/`readConfigHelp` → `{content}`/`{ok}`, `drainStoreSync`/`syncFolderNow` → `{ok}`). Contract gained the missing entries (`getMessageSource`, `copyMessages`, `syncFolderNow`, `cancelServerSearch`, `getReminderSound`, `getCalendars`, optional `popoutWindow`) with web-service stubs per the policy (reads → graceful default, writes/OS → notImpl). Bonus fixes: bridge `getMessages` was dropping sort/sortDir/search and `searchMessages` was dropping scope/accountId/folderId — Android sort toggles and scoped search silently didn't work. | S | |
27
28
  ~~done — C120 read transport diagnostics~~ shipped v1.0.583 (per-folder timeout error now includes `[conn#X r=YB w=ZB writes=N sinceLastRead=Tms]` snapshot for the doomed socket).
@@ -38,6 +38,7 @@ __export(api_client_exports, {
38
38
  createFolder: () => createFolder,
39
39
  createTask: () => createTask,
40
40
  debugEvalResult: () => debugEvalResult,
41
+ deleteAttachments: () => deleteAttachments,
41
42
  deleteCalendarEvent: () => deleteCalendarEvent,
42
43
  deleteContact: () => deleteContact,
43
44
  deleteDraft: () => deleteDraft,
@@ -633,6 +634,12 @@ async function openAttachment(accountId, uid, attachmentId, folderId, filename)
633
634
  async function getMessageSource(accountId, uid, folderId) {
634
635
  return ipc().getMessageSource(accountId, uid, folderId);
635
636
  }
637
+ async function deleteAttachments(accountId, uid, folderId, ids) {
638
+ const fn = ipc().deleteAttachments;
639
+ if (!fn)
640
+ throw new Error("Attachment delete not available in this host");
641
+ return fn(accountId, uid, folderId, ids);
642
+ }
636
643
  async function grammarLint(blocks) {
637
644
  const fn = ipc().grammarLint;
638
645
  if (!fn)
@@ -1558,6 +1565,47 @@ function clearViewer() {
1558
1565
  if (attEl)
1559
1566
  attEl.hidden = true;
1560
1567
  }
1568
+ function confirmDeleteAttachments(msg, accountId, uid, ids, question) {
1569
+ const backdrop = document.createElement("div");
1570
+ backdrop.className = "mailx-modal-backdrop";
1571
+ backdrop.style.cssText = "position:fixed;inset:0;z-index:2100;background:rgba(0,0,0,0.4);display:flex;align-items:center;justify-content:center;";
1572
+ const panel = document.createElement("div");
1573
+ panel.style.cssText = "background:var(--color-bg,#fff);color:var(--color-text,#000);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.3);width:min(440px,90vw);padding:16px;display:flex;flex-direction:column;gap:12px;";
1574
+ const body = document.createElement("div");
1575
+ body.style.cssText = "line-height:1.5;";
1576
+ body.textContent = `${question} The attachment is removed from the local copy and from the server copy. The message text is kept. This cannot be undone.`;
1577
+ const status = document.createElement("div");
1578
+ status.style.cssText = "min-height:1.2em;font-size:0.9em;color:var(--color-text-muted);";
1579
+ const row = document.createElement("div");
1580
+ row.style.cssText = "display:flex;gap:8px;justify-content:flex-end;";
1581
+ const cancel = document.createElement("button");
1582
+ cancel.textContent = "Cancel";
1583
+ cancel.style.cssText = "padding:6px 14px;";
1584
+ cancel.addEventListener("click", () => backdrop.remove());
1585
+ const del = document.createElement("button");
1586
+ del.textContent = "Delete";
1587
+ del.style.cssText = "padding:6px 14px;font-weight:600;background:#c0392b;color:#fff;border:none;border-radius:4px;cursor:pointer;";
1588
+ del.addEventListener("click", async () => {
1589
+ del.disabled = true;
1590
+ cancel.disabled = true;
1591
+ status.textContent = "Removing\u2026";
1592
+ try {
1593
+ const { deleteAttachments: deleteAttachments2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
1594
+ const r = await deleteAttachments2(accountId, uid, msg.folderId, ids);
1595
+ backdrop.remove();
1596
+ void showMessage(accountId, uid, msg.folderId);
1597
+ void r;
1598
+ } catch (e) {
1599
+ status.textContent = e?.message || "Delete failed \u2014 message unchanged.";
1600
+ del.disabled = false;
1601
+ cancel.disabled = false;
1602
+ }
1603
+ });
1604
+ row.append(cancel, del);
1605
+ panel.append(body, status, row);
1606
+ backdrop.append(panel);
1607
+ document.body.append(backdrop);
1608
+ }
1561
1609
  async function showMessage(accountId, uid, folderId, specialUse, isRetry = false, envelope) {
1562
1610
  const gen = ++showMessageGeneration;
1563
1611
  if (!isRetry)
@@ -2308,6 +2356,16 @@ async function showMessage(accountId, uid, folderId, specialUse, isRetry = false
2308
2356
  void saveAllAttachments();
2309
2357
  } });
2310
2358
  }
2359
+ if (window.mailxapi?.platform !== "android") {
2360
+ items.push({ label: `Delete "${att.filename}"\u2026`, separator: true, action: () => {
2361
+ confirmDeleteAttachments(msg, accountId, uid, [att.id], `Delete "${att.filename}" from this message?`);
2362
+ } });
2363
+ if (msg.attachments.length > 1) {
2364
+ items.push({ label: `Delete all (${msg.attachments.length})\u2026`, action: () => {
2365
+ confirmDeleteAttachments(msg, accountId, uid, "all", `Delete all ${msg.attachments.length} attachments from this message?`);
2366
+ } });
2367
+ }
2368
+ }
2311
2369
  showContextMenu(e.clientX, e.clientY, items);
2312
2370
  });
2313
2371
  attEl.appendChild(chip);