@bobfrankston/rmfmail 1.2.5 → 1.2.7

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 (51) hide show
  1. package/client/app.bundle.js +85 -4
  2. package/client/app.bundle.js.map +2 -2
  3. package/client/app.js +13 -1
  4. package/client/app.js.map +1 -1
  5. package/client/app.ts +11 -1
  6. package/client/components/alarms.js +75 -1
  7. package/client/components/alarms.js.map +1 -1
  8. package/client/components/alarms.ts +63 -1
  9. package/client/components/message-viewer.js +7 -2
  10. package/client/components/message-viewer.js.map +1 -1
  11. package/client/components/message-viewer.ts +7 -2
  12. package/client/compose/compose.bundle.js +45 -5
  13. package/client/compose/compose.bundle.js.map +2 -2
  14. package/client/compose/compose.js +3 -1
  15. package/client/compose/compose.js.map +1 -1
  16. package/client/compose/compose.ts +3 -1
  17. package/client/compose/editor.js +4 -4
  18. package/client/compose/editor.js.map +1 -1
  19. package/client/compose/editor.ts +10 -5
  20. package/client/lib/api-client.js +3 -0
  21. package/client/lib/api-client.js.map +1 -1
  22. package/client/lib/api-client.ts +3 -0
  23. package/client/lib/mailxapi.js +3 -0
  24. package/client/lib/rmf-tiny.js +47 -0
  25. package/package.json +5 -5
  26. package/packages/mailx-imap/index.d.ts.map +1 -1
  27. package/packages/mailx-imap/index.js +15 -0
  28. package/packages/mailx-imap/index.js.map +1 -1
  29. package/packages/mailx-imap/index.ts +15 -0
  30. package/packages/mailx-imap/package-lock.json +2 -2
  31. package/packages/mailx-imap/package.json +1 -1
  32. package/packages/mailx-service/index.d.ts +13 -0
  33. package/packages/mailx-service/index.d.ts.map +1 -1
  34. package/packages/mailx-service/index.js +42 -1
  35. package/packages/mailx-service/index.js.map +1 -1
  36. package/packages/mailx-service/index.ts +39 -1
  37. package/packages/mailx-service/jsonrpc.js +2 -0
  38. package/packages/mailx-service/jsonrpc.js.map +1 -1
  39. package/packages/mailx-service/jsonrpc.ts +2 -0
  40. package/packages/mailx-settings/cloud.d.ts +2 -0
  41. package/packages/mailx-settings/cloud.d.ts.map +1 -1
  42. package/packages/mailx-settings/cloud.js +45 -0
  43. package/packages/mailx-settings/cloud.js.map +1 -1
  44. package/packages/mailx-settings/cloud.ts +32 -0
  45. package/packages/mailx-settings/index.d.ts +9 -0
  46. package/packages/mailx-settings/index.d.ts.map +1 -1
  47. package/packages/mailx-settings/index.js +39 -0
  48. package/packages/mailx-settings/index.js.map +1 -1
  49. package/packages/mailx-settings/index.ts +35 -0
  50. package/test/service-boot.mjs +62 -0
  51. package/test/sync-worker-isolation.mjs +71 -0
@@ -59,6 +59,7 @@ __export(api_client_exports, {
59
59
  getOutboxStatus: () => getOutboxStatus,
60
60
  getPrimaryAccount: () => getPrimaryAccount,
61
61
  getPriorityLists: () => getPriorityLists,
62
+ getReminderSound: () => getReminderSound,
62
63
  getSettings: () => getSettings,
63
64
  getSyncPending: () => getSyncPending,
64
65
  getTasks: () => getTasks,
@@ -576,6 +577,9 @@ function getThreadMessages(accountId, threadId) {
576
577
  function readJsoncFile(name) {
577
578
  return ipc().readJsoncFile?.(name);
578
579
  }
580
+ function getReminderSound() {
581
+ return ipc().getReminderSound?.();
582
+ }
579
583
  function writeJsoncFile(name, content) {
580
584
  return ipc().writeJsoncFile?.(name, content);
581
585
  }
@@ -2237,7 +2241,7 @@ ${csp}
2237
2241
  // dismissers it sees. The parent's debounced 500 ms show-timer still
2238
2242
  // suppresses flicker.
2239
2243
  var lastHoveredHref = "";
2240
- function postLinkHover(href, rect) {
2244
+ function postLinkHover(href, rect, mouse) {
2241
2245
  // Only post on transitions to avoid spamming the parent on every
2242
2246
  // mousemove inside a link.
2243
2247
  if (href === lastHoveredHref) return;
@@ -2246,6 +2250,11 @@ ${csp}
2246
2250
  type: "linkHover",
2247
2251
  url: href,
2248
2252
  rect: rect ? { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom } : null,
2253
+ // Cursor position (iframe-viewport coords) so the parent can anchor
2254
+ // the tooltip AT the pointer, not at the link's bottom-left corner \u2014
2255
+ // on a wide link/banner the corner is far from the cursor (Bob
2256
+ // 2026-06-13: "displays the URL far from the cursor").
2257
+ mouse: mouse || null,
2249
2258
  }, "*");
2250
2259
  }
2251
2260
  document.addEventListener("mouseover", function (e) {
@@ -2254,7 +2263,7 @@ ${csp}
2254
2263
  var href = a.getAttribute("href") || "";
2255
2264
  if (!href || href.charAt(0) === "#" || href.indexOf("javascript:") === 0) return;
2256
2265
  var r = a.getBoundingClientRect();
2257
- postLinkHover(a.href || href, r);
2266
+ postLinkHover(a.href || href, r, { x: e.clientX, y: e.clientY });
2258
2267
  }, true);
2259
2268
  document.addEventListener("mouseout", function (e) {
2260
2269
  var a = e.target && e.target.closest ? e.target.closest("a[href]") : null;
@@ -5375,6 +5384,68 @@ function alog(tag, data) {
5375
5384
  } catch {
5376
5385
  }
5377
5386
  }
5387
+ async function playReminderSound() {
5388
+ const now = Date.now();
5389
+ if (now - _lastSoundAt < 2e3)
5390
+ return;
5391
+ _lastSoundAt = now;
5392
+ try {
5393
+ if (_soundCache === null) {
5394
+ const r = await getReminderSound().catch(() => ({}));
5395
+ if (r?.mute)
5396
+ _soundCache = { mute: true };
5397
+ else if (r?.dataBase64) {
5398
+ const bin = atob(r.dataBase64);
5399
+ const bytes = new Uint8Array(bin.length);
5400
+ for (let i = 0; i < bin.length; i++)
5401
+ bytes[i] = bin.charCodeAt(i);
5402
+ _soundCache = { url: URL.createObjectURL(new Blob([bytes], { type: r.mime || "audio/mpeg" })) };
5403
+ } else
5404
+ _soundCache = {};
5405
+ }
5406
+ if (_soundCache.mute)
5407
+ return;
5408
+ if (_soundCache.url) {
5409
+ const a = new Audio(_soundCache.url);
5410
+ a.play().catch(() => playChime());
5411
+ return;
5412
+ }
5413
+ playChime();
5414
+ } catch {
5415
+ try {
5416
+ playChime();
5417
+ } catch {
5418
+ }
5419
+ }
5420
+ }
5421
+ function playChime() {
5422
+ const Ctx = window.AudioContext || window.webkitAudioContext;
5423
+ if (!Ctx)
5424
+ return;
5425
+ const ctx = new Ctx();
5426
+ const t0 = ctx.currentTime;
5427
+ const tone = (freq, start, dur) => {
5428
+ const o = ctx.createOscillator();
5429
+ const g = ctx.createGain();
5430
+ o.type = "sine";
5431
+ o.frequency.value = freq;
5432
+ g.gain.setValueAtTime(1e-4, t0 + start);
5433
+ g.gain.linearRampToValueAtTime(0.22, t0 + start + 0.02);
5434
+ g.gain.exponentialRampToValueAtTime(1e-4, t0 + start + dur);
5435
+ o.connect(g);
5436
+ g.connect(ctx.destination);
5437
+ o.start(t0 + start);
5438
+ o.stop(t0 + start + dur + 0.05);
5439
+ };
5440
+ tone(880, 0, 0.35);
5441
+ tone(1174.66, 0.16, 0.42);
5442
+ setTimeout(() => {
5443
+ try {
5444
+ ctx.close();
5445
+ } catch {
5446
+ }
5447
+ }, 1300);
5448
+ }
5378
5449
  function loadDismissed() {
5379
5450
  try {
5380
5451
  return JSON.parse(localStorage.getItem(DISMISSED_KEY) || "{}");
@@ -5652,6 +5723,7 @@ async function firePopupForItem(item) {
5652
5723
  });
5653
5724
  <\/script>
5654
5725
  </body></html>`;
5726
+ void playReminderSound();
5655
5727
  const title = item.kind === "calendar" ? "Calendar reminder" : "Task reminder";
5656
5728
  const hasHost = !!window.mailxapi?.showReminderPopup;
5657
5729
  let r;
@@ -5804,7 +5876,7 @@ function startAlarmPoller() {
5804
5876
  });
5805
5877
  });
5806
5878
  }
5807
- var DISMISSED_KEY, SNOOZED_KEY, LOOKBACK_MS, LOOKAHEAD_MS, POLL_INTERVAL_MS, firedThisSession, openPopups;
5879
+ var DISMISSED_KEY, SNOOZED_KEY, LOOKBACK_MS, LOOKAHEAD_MS, POLL_INTERVAL_MS, _soundCache, _lastSoundAt, firedThisSession, openPopups;
5808
5880
  var init_alarms = __esm({
5809
5881
  "client/components/alarms.js"() {
5810
5882
  "use strict";
@@ -5814,6 +5886,8 @@ var init_alarms = __esm({
5814
5886
  LOOKBACK_MS = 60 * 60 * 1e3;
5815
5887
  LOOKAHEAD_MS = 2 * 60 * 60 * 1e3;
5816
5888
  POLL_INTERVAL_MS = 3e4;
5889
+ _soundCache = null;
5890
+ _lastSoundAt = 0;
5817
5891
  firedThisSession = /* @__PURE__ */ new Set();
5818
5892
  openPopups = /* @__PURE__ */ new Set();
5819
5893
  }
@@ -9063,7 +9137,14 @@ window.addEventListener("message", (e) => {
9063
9137
  }
9064
9138
  }
9065
9139
  const r = data.rect;
9066
- if (iframeRect && r) {
9140
+ const m = data.mouse;
9141
+ if (iframeRect && m) {
9142
+ const x = Math.max(4, Math.min(window.innerWidth - 528, iframeRect.left + m.x + 12));
9143
+ let y = iframeRect.top + m.y + 18;
9144
+ if (y + 60 > window.innerHeight) y = Math.max(4, iframeRect.top + m.y - 60);
9145
+ pop.style.left = x + "px";
9146
+ pop.style.top = y + "px";
9147
+ } else if (iframeRect && r) {
9067
9148
  const x = Math.max(4, Math.min(window.innerWidth - 528, iframeRect.left + r.left));
9068
9149
  let y = iframeRect.top + r.bottom + 4;
9069
9150
  if (y + 60 > window.innerHeight) y = Math.max(4, iframeRect.top + r.top - 60);