@bobfrankston/rmfmail 1.2.232 → 1.2.233

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 (146) hide show
  1. package/client/android-bootstrap.bundle.js +164 -152
  2. package/client/android-bootstrap.bundle.js.map +4 -4
  3. package/client/app.bundle.js +10 -104
  4. package/client/app.bundle.js.map +4 -4
  5. package/client/components/message-list.js +0 -60
  6. package/client/components/message-list.js.map +1 -1
  7. package/client/components/message-list.ts +0 -56
  8. package/client/compose/compose.bundle.js +1 -12
  9. package/client/compose/compose.bundle.js.map +2 -2
  10. package/client/compose/compose.html +0 -8
  11. package/client/compose/ghost-text.js +0 -8
  12. package/client/compose/ghost-text.js.map +1 -1
  13. package/client/compose/ghost-text.ts +0 -6
  14. package/client/compose/spellcheck-core.js +0 -1
  15. package/client/compose/spellcheck-core.js.map +1 -1
  16. package/client/compose/spellcheck-core.ts +0 -1
  17. package/client/index.html +0 -8
  18. package/client/lib/local-service.js +0 -73
  19. package/client/lib/local-service.js.map +1 -1
  20. package/client/lib/local-service.ts +0 -71
  21. package/client/lib/local-store.js +0 -3
  22. package/client/lib/local-store.js.map +1 -1
  23. package/client/lib/local-store.ts +0 -4
  24. package/package.json +1 -3
  25. package/packages/mailx-host/index.d.ts +0 -1
  26. package/packages/mailx-host/index.d.ts.map +1 -1
  27. package/packages/mailx-host/index.js +0 -1
  28. package/packages/mailx-host/index.js.map +1 -1
  29. package/packages/mailx-host/index.ts +0 -1
  30. package/packages/mailx-host/package.json +1 -1
  31. package/packages/mailx-service/index.d.ts.map +1 -1
  32. package/packages/mailx-service/index.js +36 -139
  33. package/packages/mailx-service/index.js.map +1 -1
  34. package/packages/mailx-service/index.ts +36 -148
  35. package/packages/mailx-settings/cloud.d.ts +0 -5
  36. package/packages/mailx-settings/cloud.d.ts.map +1 -1
  37. package/packages/mailx-settings/cloud.js +0 -16
  38. package/packages/mailx-settings/cloud.js.map +1 -1
  39. package/packages/mailx-settings/cloud.ts +0 -12
  40. package/packages/mailx-settings/index.d.ts +0 -3
  41. package/packages/mailx-settings/index.d.ts.map +1 -1
  42. package/packages/mailx-settings/index.js +0 -5
  43. package/packages/mailx-settings/index.js.map +1 -1
  44. package/packages/mailx-settings/index.ts +0 -7
  45. package/packages/mailx-store-web/main-thread-host.d.ts +1 -1
  46. package/packages/mailx-store-web/main-thread-host.d.ts.map +1 -1
  47. package/packages/mailx-store-web/main-thread-host.js +1 -38
  48. package/packages/mailx-store-web/main-thread-host.js.map +1 -1
  49. package/packages/mailx-store-web/main-thread-host.ts +0 -42
  50. package/packages/mailx-store-web/package.json +1 -1
  51. package/packages/mailx-store-web/web-service.d.ts.map +1 -1
  52. package/packages/mailx-store-web/web-service.js +28 -114
  53. package/packages/mailx-store-web/web-service.js.map +1 -1
  54. package/packages/mailx-store-web/web-service.ts +28 -124
  55. package/packages/mailx-store-web/web-settings.d.ts +0 -4
  56. package/packages/mailx-store-web/web-settings.d.ts.map +1 -1
  57. package/packages/mailx-store-web/web-settings.js +0 -29
  58. package/packages/mailx-store-web/web-settings.js.map +1 -1
  59. package/packages/mailx-store-web/web-settings.ts +0 -24
  60. package/packages/mailx-types/index.d.ts +2 -0
  61. package/packages/mailx-types/index.d.ts.map +1 -1
  62. package/packages/mailx-types/index.js +1 -0
  63. package/packages/mailx-types/index.js.map +1 -1
  64. package/packages/mailx-types/index.ts +2 -0
  65. package/packages/mailx-types/mime-build.d.ts +74 -0
  66. package/packages/mailx-types/mime-build.d.ts.map +1 -0
  67. package/packages/mailx-types/mime-build.js +160 -0
  68. package/packages/mailx-types/mime-build.js.map +1 -0
  69. package/packages/mailx-types/mime-build.ts +217 -0
  70. package/packages/mailx-types/package.json +1 -1
  71. package/packages/mailx-types/test/mime-build.test.mjs +200 -0
  72. package/client/components/calendar.js +0 -220
  73. package/client/components/calendar.js.map +0 -1
  74. package/client/components/calendar.ts +0 -213
  75. package/client/components/tasks.js +0 -261
  76. package/client/components/tasks.js.map +0 -1
  77. package/client/components/tasks.ts +0 -234
  78. package/packages/mailx-compose/.gitattributes +0 -10
  79. package/packages/mailx-compose/index.d.ts +0 -21
  80. package/packages/mailx-compose/index.d.ts.map +0 -1
  81. package/packages/mailx-compose/index.js +0 -66
  82. package/packages/mailx-compose/index.js.map +0 -1
  83. package/packages/mailx-compose/index.ts +0 -91
  84. package/packages/mailx-compose/package.json +0 -18
  85. package/packages/mailx-compose/tsconfig.json +0 -9
  86. package/packages/mailx-core/index.d.ts +0 -142
  87. package/packages/mailx-core/index.d.ts.map +0 -1
  88. package/packages/mailx-core/index.js +0 -430
  89. package/packages/mailx-core/index.js.map +0 -1
  90. package/packages/mailx-core/index.ts +0 -450
  91. package/packages/mailx-core/ipc.d.ts +0 -13
  92. package/packages/mailx-core/ipc.d.ts.map +0 -1
  93. package/packages/mailx-core/ipc.js +0 -56
  94. package/packages/mailx-core/ipc.js.map +0 -1
  95. package/packages/mailx-core/ipc.ts +0 -62
  96. package/packages/mailx-core/package.json +0 -18
  97. package/packages/mailx-core/tsconfig.json +0 -9
  98. package/packages/mailx-send/.gitattributes +0 -10
  99. package/packages/mailx-send/README.md +0 -217
  100. package/packages/mailx-send/cli-queue.d.ts +0 -11
  101. package/packages/mailx-send/cli-queue.d.ts.map +0 -1
  102. package/packages/mailx-send/cli-queue.js +0 -59
  103. package/packages/mailx-send/cli-queue.js.map +0 -1
  104. package/packages/mailx-send/cli-queue.ts +0 -62
  105. package/packages/mailx-send/cli-send.d.ts +0 -17
  106. package/packages/mailx-send/cli-send.d.ts.map +0 -1
  107. package/packages/mailx-send/cli-send.js +0 -75
  108. package/packages/mailx-send/cli-send.js.map +0 -1
  109. package/packages/mailx-send/cli-send.ts +0 -83
  110. package/packages/mailx-send/cli.d.ts +0 -22
  111. package/packages/mailx-send/cli.d.ts.map +0 -1
  112. package/packages/mailx-send/cli.js +0 -115
  113. package/packages/mailx-send/cli.js.map +0 -1
  114. package/packages/mailx-send/cli.ts +0 -126
  115. package/packages/mailx-send/index.d.ts +0 -77
  116. package/packages/mailx-send/index.d.ts.map +0 -1
  117. package/packages/mailx-send/index.js +0 -269
  118. package/packages/mailx-send/index.js.map +0 -1
  119. package/packages/mailx-send/index.ts +0 -338
  120. package/packages/mailx-send/mailsend/README.md +0 -133
  121. package/packages/mailx-send/mailsend/cli.d.ts +0 -12
  122. package/packages/mailx-send/mailsend/cli.d.ts.map +0 -1
  123. package/packages/mailx-send/mailsend/cli.js +0 -70
  124. package/packages/mailx-send/mailsend/cli.js.map +0 -1
  125. package/packages/mailx-send/mailsend/cli.ts +0 -81
  126. package/packages/mailx-send/mailsend/index.d.ts +0 -77
  127. package/packages/mailx-send/mailsend/index.d.ts.map +0 -1
  128. package/packages/mailx-send/mailsend/index.js +0 -264
  129. package/packages/mailx-send/mailsend/index.js.map +0 -1
  130. package/packages/mailx-send/mailsend/index.ts +0 -336
  131. package/packages/mailx-send/mailsend/package-lock.json +0 -65
  132. package/packages/mailx-send/mailsend/package.json +0 -26
  133. package/packages/mailx-send/mailsend/tsconfig.json +0 -21
  134. package/packages/mailx-send/package-lock.json +0 -65
  135. package/packages/mailx-send/package.json +0 -37
  136. package/packages/mailx-send/tsconfig.json +0 -21
  137. package/packages/mailx-server/.gitattributes +0 -10
  138. package/packages/mailx-server/index.d.ts +0 -8
  139. package/packages/mailx-server/index.d.ts.map +0 -1
  140. package/packages/mailx-server/index.js +0 -412
  141. package/packages/mailx-server/index.js.map +0 -1
  142. package/packages/mailx-server/index.ts +0 -445
  143. package/packages/mailx-server/package.json +0 -28
  144. package/packages/mailx-server/tsconfig.json +0 -9
  145. package/packages/mailx-server/tsconfig.tsbuildinfo +0 -1
  146. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-40260 → node_modules.npmglobalize-stash-40004}/.package-lock.json +0 -0
@@ -2408,6 +2408,141 @@ var init_mime_inline_images = __esm({
2408
2408
  }
2409
2409
  });
2410
2410
 
2411
+ // packages/mailx-types/mime-build.js
2412
+ function wrap76(s) {
2413
+ return s.match(/.{1,76}/g)?.join("\r\n") || s;
2414
+ }
2415
+ function buildMimeMessage(opts) {
2416
+ const now = opts.now || /* @__PURE__ */ new Date();
2417
+ const unique = opts.unique || (() => `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`);
2418
+ const newBoundary = () => `mailx_${unique()}`;
2419
+ const messageId = opts.omitMessageId ? "" : opts.messageId || `<${Date.now()}.${Math.random().toString(36).slice(2)}@${opts.domain}>`;
2420
+ const hasHtml = !!opts.bodyHtml;
2421
+ const headers = [
2422
+ `From: ${encodeAddressHeader(opts.from)}`,
2423
+ opts.to ? `To: ${encodeAddressHeader(opts.to)}` : null,
2424
+ opts.cc ? `Cc: ${encodeAddressHeader(opts.cc)}` : null,
2425
+ // Bcc belongs IN a draft — the SEND path strips it, so that a saved
2426
+ // draft reopened for editing still shows who was on Bcc.
2427
+ opts.bcc ? `Bcc: ${encodeAddressHeader(opts.bcc)}` : null,
2428
+ `Subject: ${encodeHeaderWord(opts.subject)}`,
2429
+ `Date: ${now.toUTCString()}`,
2430
+ messageId ? `Message-ID: ${messageId}` : null,
2431
+ opts.inReplyTo ? `In-Reply-To: ${opts.inReplyTo}` : null,
2432
+ opts.references?.length ? `References: ${opts.references.join(" ")}` : null,
2433
+ ...opts.extraHeaders || [],
2434
+ `MIME-Version: 1.0`
2435
+ ];
2436
+ const commonHeaders = headers.filter((h) => h !== null);
2437
+ if (opts.simpleHtml) {
2438
+ const body = opts.bodyHtml || opts.bodyText || "";
2439
+ const raw2 = [
2440
+ ...commonHeaders,
2441
+ `Content-Type: text/html; charset=UTF-8`,
2442
+ `Content-Transfer-Encoding: quoted-printable`
2443
+ ].join("\r\n") + `\r
2444
+ \r
2445
+ ` + encodeQuotedPrintable(body);
2446
+ return { raw: raw2, messageId, inlineImageCount: 0 };
2447
+ }
2448
+ const extracted = hasHtml ? extractInlineImages(opts.bodyHtml, opts.domain) : { html: "", images: [] };
2449
+ const htmlBody = extracted.html;
2450
+ const inlineImages = extracted.images;
2451
+ const textBody = opts.bodyText || (hasHtml ? htmlToPlainText(htmlBody) : "");
2452
+ const htmlEncoded = hasHtml ? encodeQuotedPrintable(htmlBody) : "";
2453
+ const textEncoded = encodeQuotedPrintable(textBody);
2454
+ const makeInner = () => {
2455
+ if (hasHtml) {
2456
+ const altBoundary = newBoundary();
2457
+ const body = `--${altBoundary}\r
2458
+ Content-Type: text/plain; charset=UTF-8\r
2459
+ Content-Transfer-Encoding: quoted-printable\r
2460
+ \r
2461
+ ${textEncoded}\r
2462
+ --${altBoundary}\r
2463
+ Content-Type: text/html; charset=UTF-8\r
2464
+ Content-Transfer-Encoding: quoted-printable\r
2465
+ \r
2466
+ ${htmlEncoded}\r
2467
+ --${altBoundary}--\r
2468
+ `;
2469
+ const alt = {
2470
+ headers: [`Content-Type: multipart/alternative; boundary="${altBoundary}"`],
2471
+ body
2472
+ };
2473
+ if (inlineImages.length === 0)
2474
+ return alt;
2475
+ const relBoundary = newBoundary();
2476
+ const relParts = [
2477
+ `--${relBoundary}\r
2478
+ ` + alt.headers.join("\r\n") + `\r
2479
+ \r
2480
+ ` + alt.body,
2481
+ ...inlineImages.map((im) => `--${relBoundary}\r
2482
+ Content-Type: ${im.mime}\r
2483
+ Content-ID: <${im.contentId}>\r
2484
+ Content-Disposition: inline\r
2485
+ Content-Transfer-Encoding: base64\r
2486
+ \r
2487
+ ${wrap76(im.base64)}\r
2488
+ `)
2489
+ ];
2490
+ return {
2491
+ headers: [`Content-Type: multipart/related; boundary="${relBoundary}"; type="multipart/alternative"`],
2492
+ body: relParts.join("") + `--${relBoundary}--\r
2493
+ `
2494
+ };
2495
+ }
2496
+ return {
2497
+ headers: [
2498
+ `Content-Type: text/plain; charset=UTF-8`,
2499
+ `Content-Transfer-Encoding: quoted-printable`
2500
+ ],
2501
+ body: textEncoded
2502
+ };
2503
+ };
2504
+ const attachments = opts.attachments || [];
2505
+ let raw;
2506
+ if (attachments.length > 0) {
2507
+ const mixedBoundary = newBoundary();
2508
+ const inner = makeInner();
2509
+ const parts = [
2510
+ `--${mixedBoundary}\r
2511
+ ` + inner.headers.join("\r\n") + `\r
2512
+ \r
2513
+ ` + inner.body
2514
+ ];
2515
+ for (const att of attachments) {
2516
+ const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
2517
+ const mime = att.mimeType || "application/octet-stream";
2518
+ parts.push(`--${mixedBoundary}\r
2519
+ Content-Type: ${mime}; name="${filename}"\r
2520
+ Content-Disposition: attachment; filename="${filename}"\r
2521
+ Content-Transfer-Encoding: base64\r
2522
+ \r
2523
+ ${wrap76(att.dataBase64 || "")}\r
2524
+ `);
2525
+ }
2526
+ raw = [...commonHeaders, `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`].join("\r\n") + `\r
2527
+ \r
2528
+ ` + parts.join("") + `--${mixedBoundary}--\r
2529
+ `;
2530
+ } else {
2531
+ const inner = makeInner();
2532
+ raw = [...commonHeaders, ...inner.headers].join("\r\n") + `\r
2533
+ \r
2534
+ ` + inner.body;
2535
+ }
2536
+ return { raw, messageId, inlineImageCount: inlineImages.length };
2537
+ }
2538
+ var init_mime_build = __esm({
2539
+ "packages/mailx-types/mime-build.js"() {
2540
+ "use strict";
2541
+ init_mailx_types();
2542
+ init_mime_inline_images();
2543
+ }
2544
+ });
2545
+
2411
2546
  // packages/mailx-types/index.js
2412
2547
  var mailx_types_exports = {};
2413
2548
  __export(mailx_types_exports, {
@@ -2416,6 +2551,7 @@ __export(mailx_types_exports, {
2416
2551
  addContactsDenylistEntry: () => addContactsDenylistEntry,
2417
2552
  addContactsPreferredEntry: () => addContactsPreferredEntry,
2418
2553
  answeredOf: () => answeredOf,
2554
+ buildMimeMessage: () => buildMimeMessage,
2419
2555
  deletedOf: () => deletedOf,
2420
2556
  draftOf: () => draftOf,
2421
2557
  encodeAddressHeader: () => encodeAddressHeader,
@@ -2698,6 +2834,7 @@ var init_mailx_types = __esm({
2698
2834
  init_groups();
2699
2835
  init_reminder_state();
2700
2836
  init_mime_inline_images();
2837
+ init_mime_build();
2701
2838
  _SEEN = "\\Seen";
2702
2839
  _FLAGGED = "\\Flagged";
2703
2840
  _ANSWERED = "\\Answered";
@@ -2732,14 +2869,12 @@ __export(web_settings_exports, {
2732
2869
  loadAccountsFromCloud: () => loadAccountsFromCloud,
2733
2870
  loadAllowlist: () => loadAllowlist,
2734
2871
  loadAutocomplete: () => loadAutocomplete,
2735
- loadDeviceState: () => loadDeviceState,
2736
2872
  loadPreferences: () => loadPreferences,
2737
2873
  loadSettings: () => loadSettings,
2738
2874
  primeSharedConfigs: () => primeSharedConfigs,
2739
2875
  saveAccounts: () => saveAccounts,
2740
2876
  saveAllowlist: () => saveAllowlist,
2741
2877
  saveAutocomplete: () => saveAutocomplete,
2742
- saveDeviceState: () => saveDeviceState,
2743
2878
  savePreferences: () => savePreferences,
2744
2879
  saveSettings: () => saveSettings,
2745
2880
  setGDriveFolderId: () => setGDriveFolderId,
@@ -3236,33 +3371,6 @@ function getDeviceId() {
3236
3371
  }
3237
3372
  return id;
3238
3373
  }
3239
- async function saveDeviceState(state) {
3240
- const deviceId = getDeviceId();
3241
- const filename = `devices/${deviceId}/state.json`;
3242
- const content = JSON.stringify(state, null, 2);
3243
- await idbWrite(filename, content);
3244
- await gDriveWrite(filename, content);
3245
- }
3246
- async function loadDeviceState() {
3247
- const deviceId = getDeviceId();
3248
- const filename = `devices/${deviceId}/state.json`;
3249
- const cached = await idbRead(filename);
3250
- if (cached) {
3251
- try {
3252
- return parseJsonc(cached);
3253
- } catch {
3254
- }
3255
- }
3256
- const content = await gDriveRead(filename);
3257
- if (content) {
3258
- await idbWrite(filename, content);
3259
- try {
3260
- return parseJsonc(content);
3261
- } catch {
3262
- }
3263
- }
3264
- return {};
3265
- }
3266
3374
  var IDB_NAME2, IDB_VERSION, STORE_NAME2, gDriveFolderId, gDriveFolderName, gDriveFolderPath, gDriveFolderOwner, tokenProvider, PROVIDERS, DEFAULT_PREFERENCES, DEFAULT_ALLOWLIST, PREV_KEEP, SHARED_REFRESH_MS, SHARED_FILES, __lastSharedRefresh, ALLOWLIST_KEYS, DEVICE_ID_KEY;
3267
3375
  var init_web_settings = __esm({
3268
3376
  "packages/mailx-store-web/web-settings.js"() {
@@ -6299,110 +6407,20 @@ var WebMailxService = class _WebMailxService {
6299
6407
  const to = msg.to.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
6300
6408
  const cc = msg.cc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
6301
6409
  const bcc = msg.bcc?.map((a) => a.name ? `${a.name} <${a.address}>` : a.address).join(", ");
6302
- const hasHtml = !!msg.bodyHtml;
6303
- const domain = account.email.split("@")[1] || "mailx.local";
6304
- const messageId = `<${Date.now()}.${Math.random().toString(36).slice(2)}@${domain}>`;
6305
- const extracted = hasHtml ? extractInlineImages(msg.bodyHtml, domain) : { html: "", images: [] };
6306
- const htmlBody = extracted.html;
6307
- const inlineImages = extracted.images;
6308
- const textBody = msg.bodyText || (hasHtml ? htmlToPlainText(htmlBody) : "");
6309
- const htmlEncoded = hasHtml ? encodeQuotedPrintable(htmlBody) : "";
6310
- const textEncoded = encodeQuotedPrintable(textBody);
6311
- const envelope = [
6312
- `From: ${fromHeader}`,
6313
- `To: ${to}`,
6314
- cc ? `Cc: ${cc}` : null,
6315
- bcc ? `Bcc: ${bcc}` : null,
6316
- `Subject: ${encodeHeaderWord(msg.subject)}`,
6317
- `Date: ${(/* @__PURE__ */ new Date()).toUTCString()}`,
6318
- `Message-ID: ${messageId}`,
6319
- msg.inReplyTo ? `In-Reply-To: ${msg.inReplyTo}` : null,
6320
- msg.references?.length ? `References: ${msg.references.join(" ")}` : null,
6321
- `MIME-Version: 1.0`
6322
- ].filter((h) => h !== null);
6323
- const newBoundary = () => `mailx_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
6324
- const wrap76 = (s) => s.match(/.{1,76}/g)?.join("\r\n") || s;
6325
- const makeInner = () => {
6326
- if (hasHtml) {
6327
- const altBoundary = newBoundary();
6328
- const body = `--${altBoundary}\r
6329
- Content-Type: text/plain; charset=UTF-8\r
6330
- Content-Transfer-Encoding: quoted-printable\r
6331
- \r
6332
- ${textEncoded}\r
6333
- --${altBoundary}\r
6334
- Content-Type: text/html; charset=UTF-8\r
6335
- Content-Transfer-Encoding: quoted-printable\r
6336
- \r
6337
- ${htmlEncoded}\r
6338
- --${altBoundary}--\r
6339
- `;
6340
- const alt = { headers: [`Content-Type: multipart/alternative; boundary="${altBoundary}"`], body };
6341
- if (inlineImages.length === 0)
6342
- return alt;
6343
- const relBoundary = newBoundary();
6344
- const relParts = [
6345
- `--${relBoundary}\r
6346
- ` + alt.headers.join("\r\n") + `\r
6347
- \r
6348
- ` + alt.body,
6349
- ...inlineImages.map((im) => `--${relBoundary}\r
6350
- Content-Type: ${im.mime}\r
6351
- Content-ID: <${im.contentId}>\r
6352
- Content-Disposition: inline\r
6353
- Content-Transfer-Encoding: base64\r
6354
- \r
6355
- ${wrap76(im.base64)}\r
6356
- `)
6357
- ];
6358
- return {
6359
- headers: [`Content-Type: multipart/related; boundary="${relBoundary}"; type="multipart/alternative"`],
6360
- body: relParts.join("") + `--${relBoundary}--\r
6361
- `
6362
- };
6363
- }
6364
- return {
6365
- headers: [`Content-Type: text/plain; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`],
6366
- body: textEncoded
6367
- };
6368
- };
6369
- const atts = Array.isArray(msg.attachments) ? msg.attachments : [];
6370
- let rawMessage;
6371
- if (atts.length > 0) {
6372
- const mixedBoundary = `mailxmix_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
6373
- const inner = makeInner();
6374
- const parts = [];
6375
- parts.push(`--${mixedBoundary}\r
6376
- ` + inner.headers.join("\r\n") + `\r
6377
- \r
6378
- ` + inner.body);
6379
- for (const att of atts) {
6380
- const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
6381
- const mime = att.mimeType || "application/octet-stream";
6382
- const wrapped = wrap76(att.dataBase64 || "");
6383
- parts.push(`--${mixedBoundary}\r
6384
- Content-Type: ${mime}; name="${filename}"\r
6385
- Content-Disposition: attachment; filename="${filename}"\r
6386
- Content-Transfer-Encoding: base64\r
6387
- \r
6388
- ${wrapped}\r
6389
- `);
6390
- }
6391
- const headers = [
6392
- ...envelope,
6393
- `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`
6394
- ].join("\r\n");
6395
- rawMessage = `${headers}\r
6396
- \r
6397
- ${parts.join("")}--${mixedBoundary}--\r
6398
- `;
6399
- } else {
6400
- const inner = makeInner();
6401
- const headers = [...envelope, ...inner.headers].join("\r\n");
6402
- rawMessage = `${headers}\r
6403
- \r
6404
- ${inner.body}`;
6405
- }
6410
+ const built = buildMimeMessage({
6411
+ from: fromHeader,
6412
+ to,
6413
+ cc,
6414
+ bcc,
6415
+ subject: msg.subject,
6416
+ bodyHtml: msg.bodyHtml,
6417
+ bodyText: msg.bodyText,
6418
+ attachments: msg.attachments,
6419
+ inReplyTo: msg.inReplyTo,
6420
+ references: msg.references,
6421
+ domain: account.email.split("@")[1] || "mailx.local"
6422
+ });
6423
+ const rawMessage = built.raw;
6406
6424
  await this.syncManager.queueOutgoingLocal(account.id, rawMessage);
6407
6425
  for (const addr of msg.to)
6408
6426
  this.db.recordSentAddress(addr.name, addr.address);
@@ -6458,25 +6476,19 @@ ${inner.body}`;
6458
6476
  if (!account)
6459
6477
  throw new Error(`Unknown account: ${accountId}`);
6460
6478
  const id = draftId || `mailx-draft-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
6461
- const body = bodyHtml || bodyText || "";
6462
- const bodyEncoded = encodeQuotedPrintable(body);
6463
- const headers = [
6464
- // Same rule as desktop: the compose From (reply-to-alias) wins
6465
- // over the account default (Bob 2026-07-30).
6466
- `From: ${from?.trim() || `${account.name} <${account.email}>`}`,
6467
- to ? `To: ${to}` : null,
6468
- cc ? `Cc: ${cc}` : null,
6469
- bcc ? `Bcc: ${bcc}` : null,
6470
- `Subject: ${encodeHeaderWord(subject || "(no subject)")}`,
6471
- `Date: ${(/* @__PURE__ */ new Date()).toUTCString()}`,
6472
- `X-Mailx-Draft-ID: ${id}`,
6473
- `MIME-Version: 1.0`,
6474
- `Content-Type: text/html; charset=UTF-8`,
6475
- `Content-Transfer-Encoding: quoted-printable`
6476
- ].filter((h) => h !== null).join("\r\n");
6477
- const raw = `${headers}\r
6478
- \r
6479
- ${bodyEncoded}`;
6479
+ const raw = buildMimeMessage({
6480
+ from: from?.trim() || `${account.name} <${account.email}>`,
6481
+ to,
6482
+ cc,
6483
+ bcc,
6484
+ subject: subject || "(no subject)",
6485
+ bodyHtml,
6486
+ bodyText,
6487
+ domain: account.email.split("@")[1] || "mailx.local",
6488
+ extraHeaders: [`X-Mailx-Draft-ID: ${id}`],
6489
+ omitMessageId: true,
6490
+ simpleHtml: true
6491
+ }).raw;
6480
6492
  const draftUid = await this.syncManager.saveDraft(accountId, raw, previousDraftUid, id);
6481
6493
  return { draftUid, draftId: id };
6482
6494
  }