@bobfrankston/mailx 1.0.12 → 1.0.13

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 (237) hide show
  1. package/bin/mailx.js +47 -29
  2. package/client/app.js +93 -13
  3. package/client/components/folder-tree.js +84 -3
  4. package/client/components/message-list.js +134 -8
  5. package/client/components/message-viewer.js +130 -13
  6. package/client/compose/compose.html +4 -4
  7. package/client/compose/compose.js +53 -34
  8. package/client/index.html +33 -9
  9. package/client/lib/api-client.js +102 -30
  10. package/client/lib/mailxapi.js +123 -0
  11. package/client/package.json +1 -1
  12. package/client/styles/components.css +188 -15
  13. package/client/styles/layout.css +2 -1
  14. package/killmail.cmd +6 -0
  15. package/launch.ps1 +47 -5
  16. package/launcher/bin/mailx-app.exe +0 -0
  17. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Breadcrumbs +0 -0
  18. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Crashpad/metadata +0 -0
  19. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Crashpad/settings.dat +0 -0
  20. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Crashpad/throttle_store.dat +1 -0
  21. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/CrashpadMetrics-active.pma +0 -0
  22. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/BrowsingTopicsSiteData +0 -0
  23. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Cache/No_Vary_Search/journal.baj +1 -0
  24. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/DIPS +0 -0
  25. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/DashTrackerDatabase +0 -0
  26. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/EdgeJourneys/EdgeJourneys.db +0 -0
  27. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Rules/LOCK +0 -0
  28. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Rules/LOG +3 -0
  29. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Rules/MANIFEST-000001 +0 -0
  30. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Scripts/LOCK +0 -0
  31. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Scripts/LOG +3 -0
  32. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension Scripts/MANIFEST-000001 +0 -0
  33. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension State/LOCK +0 -0
  34. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension State/LOG +3 -0
  35. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Extension State/MANIFEST-000001 +0 -0
  36. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/ExtensionActivityComp +0 -0
  37. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/ExtensionActivityEdge +0 -0
  38. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Favicons +0 -0
  39. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/History +0 -0
  40. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/History-journal +0 -0
  41. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/IndexedDB/devtools_devtools_0.indexeddb.leveldb/LOCK +0 -0
  42. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/IndexedDB/devtools_devtools_0.indexeddb.leveldb/LOG +3 -0
  43. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/IndexedDB/devtools_devtools_0.indexeddb.leveldb/MANIFEST-000001 +0 -0
  44. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Local Storage/leveldb/LOCK +0 -0
  45. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Local Storage/leveldb/LOG +3 -0
  46. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Local Storage/leveldb/MANIFEST-000001 +0 -0
  47. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Login Data +0 -0
  48. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Login Data For Account +0 -0
  49. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Network/Cookies +0 -0
  50. NEL +0 -0
  51. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Network/Trust Tokens +0 -0
  52. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Network Action Predictor +0 -0
  53. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Preferences +1 -0
  54. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Safe Browsing Network/Safe Browsing Cookies +0 -0
  55. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/ServerCertificate +0 -0
  56. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Session Storage/LOCK +0 -0
  57. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Session Storage/LOG +3 -0
  58. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Session Storage/MANIFEST-000001 +0 -0
  59. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Shared Dictionary/db +0 -0
  60. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/SharedStorage +0 -0
  61. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Shortcuts +0 -0
  62. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Site Characteristics Database/LOCK +0 -0
  63. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Site Characteristics Database/LOG +3 -0
  64. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Site Characteristics Database/MANIFEST-000001 +0 -0
  65. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Sync Data/LevelDB/LOCK +0 -0
  66. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Sync Data/LevelDB/LOG +3 -0
  67. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Sync Data/LevelDB/MANIFEST-000001 +0 -0
  68. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Top Sites +0 -0
  69. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Vpn Tokens +0 -0
  70. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Web Data +0 -0
  71. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/Web Data-journal +0 -0
  72. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/WebStorage/QuotaManager +0 -0
  73. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/WebStorage/QuotaManager-journal +0 -0
  74. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/heavy_ad_intervention_opt_out.db +0 -0
  75. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/LOCK +0 -0
  76. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/LOG +3 -0
  77. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/MANIFEST-000001 +0 -0
  78. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/LOCK +0 -0
  79. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/LOG +3 -0
  80. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Default/shared_proto_db/metadata/MANIFEST-000001 +0 -0
  81. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/DeferredBrowserMetrics/BrowserMetrics-69CAD063-BE24.pma +0 -0
  82. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Local State +1 -0
  83. package/launcher/bin/mailx-app.exe.WebView2/EBWebView/Variations +1 -0
  84. package/launcher/bin/mailx-app.old.exe +0 -0
  85. package/package.json +1 -1
  86. package/packages/mailx-api/index.js +79 -26
  87. package/packages/mailx-core/index.d.ts +129 -0
  88. package/packages/mailx-core/index.js +323 -0
  89. package/packages/mailx-core/ipc.d.ts +13 -0
  90. package/packages/mailx-core/ipc.js +56 -0
  91. package/packages/mailx-core/package.json +18 -0
  92. package/packages/mailx-imap/index.d.ts +5 -1
  93. package/packages/mailx-imap/index.js +76 -14
  94. package/packages/mailx-server/index.js +42 -31
  95. package/packages/mailx-server/package.json +1 -2
  96. package/packages/mailx-settings/index.d.ts +1 -1
  97. package/packages/mailx-settings/index.js +21 -12
  98. package/packages/mailx-store/db.d.ts +5 -1
  99. package/packages/mailx-store/db.js +64 -12
  100. package/packages/mailx-store/file-store.d.ts +2 -8
  101. package/packages/mailx-store/file-store.js +7 -31
  102. package/packages/mailx-types/index.d.ts +3 -1
  103. package/.tswalk.json +0 -7396
  104. package/launcher/release.cmd +0 -4
  105. package/mailx.json +0 -9
  106. package/packages/mailx-api/node_modules/nodemailer/.ncurc.js +0 -9
  107. package/packages/mailx-api/node_modules/nodemailer/.prettierignore +0 -8
  108. package/packages/mailx-api/node_modules/nodemailer/.prettierrc +0 -12
  109. package/packages/mailx-api/node_modules/nodemailer/.prettierrc.js +0 -10
  110. package/packages/mailx-api/node_modules/nodemailer/.release-please-config.json +0 -9
  111. package/packages/mailx-api/node_modules/nodemailer/LICENSE +0 -16
  112. package/packages/mailx-api/node_modules/nodemailer/README.md +0 -86
  113. package/packages/mailx-api/node_modules/nodemailer/SECURITY.txt +0 -22
  114. package/packages/mailx-api/node_modules/nodemailer/eslint.config.js +0 -88
  115. package/packages/mailx-api/node_modules/nodemailer/lib/addressparser/index.js +0 -383
  116. package/packages/mailx-api/node_modules/nodemailer/lib/base64/index.js +0 -139
  117. package/packages/mailx-api/node_modules/nodemailer/lib/dkim/index.js +0 -253
  118. package/packages/mailx-api/node_modules/nodemailer/lib/dkim/message-parser.js +0 -155
  119. package/packages/mailx-api/node_modules/nodemailer/lib/dkim/relaxed-body.js +0 -154
  120. package/packages/mailx-api/node_modules/nodemailer/lib/dkim/sign.js +0 -117
  121. package/packages/mailx-api/node_modules/nodemailer/lib/fetch/cookies.js +0 -281
  122. package/packages/mailx-api/node_modules/nodemailer/lib/fetch/index.js +0 -280
  123. package/packages/mailx-api/node_modules/nodemailer/lib/json-transport/index.js +0 -82
  124. package/packages/mailx-api/node_modules/nodemailer/lib/mail-composer/index.js +0 -629
  125. package/packages/mailx-api/node_modules/nodemailer/lib/mailer/index.js +0 -441
  126. package/packages/mailx-api/node_modules/nodemailer/lib/mailer/mail-message.js +0 -316
  127. package/packages/mailx-api/node_modules/nodemailer/lib/mime-funcs/index.js +0 -625
  128. package/packages/mailx-api/node_modules/nodemailer/lib/mime-funcs/mime-types.js +0 -2113
  129. package/packages/mailx-api/node_modules/nodemailer/lib/mime-node/index.js +0 -1316
  130. package/packages/mailx-api/node_modules/nodemailer/lib/mime-node/last-newline.js +0 -33
  131. package/packages/mailx-api/node_modules/nodemailer/lib/mime-node/le-unix.js +0 -43
  132. package/packages/mailx-api/node_modules/nodemailer/lib/mime-node/le-windows.js +0 -52
  133. package/packages/mailx-api/node_modules/nodemailer/lib/nodemailer.js +0 -157
  134. package/packages/mailx-api/node_modules/nodemailer/lib/punycode/index.js +0 -460
  135. package/packages/mailx-api/node_modules/nodemailer/lib/qp/index.js +0 -227
  136. package/packages/mailx-api/node_modules/nodemailer/lib/sendmail-transport/index.js +0 -210
  137. package/packages/mailx-api/node_modules/nodemailer/lib/ses-transport/index.js +0 -234
  138. package/packages/mailx-api/node_modules/nodemailer/lib/shared/index.js +0 -754
  139. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-connection/data-stream.js +0 -108
  140. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +0 -143
  141. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-connection/index.js +0 -1870
  142. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-pool/index.js +0 -652
  143. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +0 -259
  144. package/packages/mailx-api/node_modules/nodemailer/lib/smtp-transport/index.js +0 -421
  145. package/packages/mailx-api/node_modules/nodemailer/lib/stream-transport/index.js +0 -135
  146. package/packages/mailx-api/node_modules/nodemailer/lib/well-known/index.js +0 -47
  147. package/packages/mailx-api/node_modules/nodemailer/lib/well-known/services.json +0 -611
  148. package/packages/mailx-api/node_modules/nodemailer/lib/xoauth2/index.js +0 -427
  149. package/packages/mailx-api/node_modules/nodemailer/package.json +0 -47
  150. package/packages/mailx-imap/node_modules/nodemailer/.ncurc.js +0 -9
  151. package/packages/mailx-imap/node_modules/nodemailer/.prettierignore +0 -8
  152. package/packages/mailx-imap/node_modules/nodemailer/.prettierrc +0 -12
  153. package/packages/mailx-imap/node_modules/nodemailer/.prettierrc.js +0 -10
  154. package/packages/mailx-imap/node_modules/nodemailer/.release-please-config.json +0 -9
  155. package/packages/mailx-imap/node_modules/nodemailer/LICENSE +0 -16
  156. package/packages/mailx-imap/node_modules/nodemailer/README.md +0 -86
  157. package/packages/mailx-imap/node_modules/nodemailer/SECURITY.txt +0 -22
  158. package/packages/mailx-imap/node_modules/nodemailer/eslint.config.js +0 -88
  159. package/packages/mailx-imap/node_modules/nodemailer/lib/addressparser/index.js +0 -383
  160. package/packages/mailx-imap/node_modules/nodemailer/lib/base64/index.js +0 -139
  161. package/packages/mailx-imap/node_modules/nodemailer/lib/dkim/index.js +0 -253
  162. package/packages/mailx-imap/node_modules/nodemailer/lib/dkim/message-parser.js +0 -155
  163. package/packages/mailx-imap/node_modules/nodemailer/lib/dkim/relaxed-body.js +0 -154
  164. package/packages/mailx-imap/node_modules/nodemailer/lib/dkim/sign.js +0 -117
  165. package/packages/mailx-imap/node_modules/nodemailer/lib/fetch/cookies.js +0 -281
  166. package/packages/mailx-imap/node_modules/nodemailer/lib/fetch/index.js +0 -280
  167. package/packages/mailx-imap/node_modules/nodemailer/lib/json-transport/index.js +0 -82
  168. package/packages/mailx-imap/node_modules/nodemailer/lib/mail-composer/index.js +0 -629
  169. package/packages/mailx-imap/node_modules/nodemailer/lib/mailer/index.js +0 -441
  170. package/packages/mailx-imap/node_modules/nodemailer/lib/mailer/mail-message.js +0 -316
  171. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-funcs/index.js +0 -625
  172. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-funcs/mime-types.js +0 -2113
  173. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-node/index.js +0 -1316
  174. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-node/last-newline.js +0 -33
  175. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-node/le-unix.js +0 -43
  176. package/packages/mailx-imap/node_modules/nodemailer/lib/mime-node/le-windows.js +0 -52
  177. package/packages/mailx-imap/node_modules/nodemailer/lib/nodemailer.js +0 -157
  178. package/packages/mailx-imap/node_modules/nodemailer/lib/punycode/index.js +0 -460
  179. package/packages/mailx-imap/node_modules/nodemailer/lib/qp/index.js +0 -227
  180. package/packages/mailx-imap/node_modules/nodemailer/lib/sendmail-transport/index.js +0 -210
  181. package/packages/mailx-imap/node_modules/nodemailer/lib/ses-transport/index.js +0 -234
  182. package/packages/mailx-imap/node_modules/nodemailer/lib/shared/index.js +0 -754
  183. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-connection/data-stream.js +0 -108
  184. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +0 -143
  185. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-connection/index.js +0 -1870
  186. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-pool/index.js +0 -652
  187. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +0 -259
  188. package/packages/mailx-imap/node_modules/nodemailer/lib/smtp-transport/index.js +0 -421
  189. package/packages/mailx-imap/node_modules/nodemailer/lib/stream-transport/index.js +0 -135
  190. package/packages/mailx-imap/node_modules/nodemailer/lib/well-known/index.js +0 -47
  191. package/packages/mailx-imap/node_modules/nodemailer/lib/well-known/services.json +0 -611
  192. package/packages/mailx-imap/node_modules/nodemailer/lib/xoauth2/index.js +0 -427
  193. package/packages/mailx-imap/node_modules/nodemailer/package.json +0 -47
  194. package/packages/mailx-send/node_modules/nodemailer/.ncurc.js +0 -9
  195. package/packages/mailx-send/node_modules/nodemailer/.prettierignore +0 -8
  196. package/packages/mailx-send/node_modules/nodemailer/.prettierrc +0 -12
  197. package/packages/mailx-send/node_modules/nodemailer/.prettierrc.js +0 -10
  198. package/packages/mailx-send/node_modules/nodemailer/.release-please-config.json +0 -9
  199. package/packages/mailx-send/node_modules/nodemailer/LICENSE +0 -16
  200. package/packages/mailx-send/node_modules/nodemailer/README.md +0 -86
  201. package/packages/mailx-send/node_modules/nodemailer/SECURITY.txt +0 -22
  202. package/packages/mailx-send/node_modules/nodemailer/eslint.config.js +0 -88
  203. package/packages/mailx-send/node_modules/nodemailer/lib/addressparser/index.js +0 -383
  204. package/packages/mailx-send/node_modules/nodemailer/lib/base64/index.js +0 -139
  205. package/packages/mailx-send/node_modules/nodemailer/lib/dkim/index.js +0 -253
  206. package/packages/mailx-send/node_modules/nodemailer/lib/dkim/message-parser.js +0 -155
  207. package/packages/mailx-send/node_modules/nodemailer/lib/dkim/relaxed-body.js +0 -154
  208. package/packages/mailx-send/node_modules/nodemailer/lib/dkim/sign.js +0 -117
  209. package/packages/mailx-send/node_modules/nodemailer/lib/fetch/cookies.js +0 -281
  210. package/packages/mailx-send/node_modules/nodemailer/lib/fetch/index.js +0 -280
  211. package/packages/mailx-send/node_modules/nodemailer/lib/json-transport/index.js +0 -82
  212. package/packages/mailx-send/node_modules/nodemailer/lib/mail-composer/index.js +0 -629
  213. package/packages/mailx-send/node_modules/nodemailer/lib/mailer/index.js +0 -441
  214. package/packages/mailx-send/node_modules/nodemailer/lib/mailer/mail-message.js +0 -316
  215. package/packages/mailx-send/node_modules/nodemailer/lib/mime-funcs/index.js +0 -625
  216. package/packages/mailx-send/node_modules/nodemailer/lib/mime-funcs/mime-types.js +0 -2113
  217. package/packages/mailx-send/node_modules/nodemailer/lib/mime-node/index.js +0 -1316
  218. package/packages/mailx-send/node_modules/nodemailer/lib/mime-node/last-newline.js +0 -33
  219. package/packages/mailx-send/node_modules/nodemailer/lib/mime-node/le-unix.js +0 -43
  220. package/packages/mailx-send/node_modules/nodemailer/lib/mime-node/le-windows.js +0 -52
  221. package/packages/mailx-send/node_modules/nodemailer/lib/nodemailer.js +0 -157
  222. package/packages/mailx-send/node_modules/nodemailer/lib/punycode/index.js +0 -460
  223. package/packages/mailx-send/node_modules/nodemailer/lib/qp/index.js +0 -227
  224. package/packages/mailx-send/node_modules/nodemailer/lib/sendmail-transport/index.js +0 -210
  225. package/packages/mailx-send/node_modules/nodemailer/lib/ses-transport/index.js +0 -234
  226. package/packages/mailx-send/node_modules/nodemailer/lib/shared/index.js +0 -754
  227. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-connection/data-stream.js +0 -108
  228. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +0 -143
  229. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-connection/index.js +0 -1870
  230. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-pool/index.js +0 -652
  231. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +0 -259
  232. package/packages/mailx-send/node_modules/nodemailer/lib/smtp-transport/index.js +0 -421
  233. package/packages/mailx-send/node_modules/nodemailer/lib/stream-transport/index.js +0 -135
  234. package/packages/mailx-send/node_modules/nodemailer/lib/well-known/index.js +0 -47
  235. package/packages/mailx-send/node_modules/nodemailer/lib/well-known/services.json +0 -611
  236. package/packages/mailx-send/node_modules/nodemailer/lib/xoauth2/index.js +0 -427
  237. package/packages/mailx-send/node_modules/nodemailer/package.json +0 -47
@@ -5,12 +5,14 @@ import { getMessage, updateFlags } from "../lib/api-client.js";
5
5
  /** Currently displayed message (for reply/forward) */
6
6
  let currentMessage = null;
7
7
  let currentAccountId = "";
8
+ let showMessageGeneration = 0; // Cancel stale fetches
8
9
  export function getCurrentMessage() {
9
10
  if (!currentMessage)
10
11
  return null;
11
12
  return { accountId: currentAccountId, message: currentMessage };
12
13
  }
13
- export async function showMessage(accountId, uid) {
14
+ export async function showMessage(accountId, uid, folderId, specialUse) {
15
+ const gen = ++showMessageGeneration;
14
16
  const headerEl = document.getElementById("mv-header");
15
17
  const bodyEl = document.getElementById("mv-body");
16
18
  const attEl = document.getElementById("mv-attachments");
@@ -18,9 +20,10 @@ export async function showMessage(accountId, uid) {
18
20
  headerEl.hidden = true;
19
21
  attEl.hidden = true;
20
22
  try {
21
- // Timeout after 15 seconds
22
- const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout loading message")), 15000));
23
- const msg = await Promise.race([getMessage(accountId, uid), timeout]);
23
+ const msg = await getMessage(accountId, uid, false, folderId);
24
+ // Stale response a newer showMessage was called while we were fetching
25
+ if (gen !== showMessageGeneration)
26
+ return;
24
27
  currentMessage = msg;
25
28
  currentAccountId = accountId;
26
29
  // Mark as read
@@ -37,18 +40,114 @@ export async function showMessage(accountId, uid) {
37
40
  }
38
41
  headerEl.querySelector(".mv-subject").textContent = msg.subject;
39
42
  headerEl.querySelector(".mv-date").textContent = new Date(msg.date).toLocaleString(undefined, { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", hour12: false });
40
- // Remote content banner
43
+ // Unsubscribe button (upper right of header)
44
+ const unsubBtn = document.getElementById("mv-unsubscribe");
45
+ const headerUnsub = msg.listUnsubscribe || "";
46
+ const headerUnsubUrl = headerUnsub.match(/<(https?:\/\/[^>]+)>/)?.[1]
47
+ || headerUnsub.match(/<(mailto:[^>]+)>/)?.[1] || "";
48
+ if (unsubBtn) {
49
+ if (headerUnsubUrl) {
50
+ unsubBtn.hidden = false;
51
+ unsubBtn.href = headerUnsubUrl;
52
+ unsubBtn.target = "_blank";
53
+ unsubBtn.rel = "noopener noreferrer";
54
+ }
55
+ else {
56
+ unsubBtn.hidden = true;
57
+ }
58
+ }
59
+ // View Source button — shows .eml file path
60
+ const srcBtn = document.getElementById("mv-view-source");
61
+ if (srcBtn) {
62
+ if (msg.emlPath) {
63
+ srcBtn.hidden = false;
64
+ srcBtn.title = msg.emlPath;
65
+ srcBtn.onclick = () => {
66
+ // Copy path to clipboard and show in status bar
67
+ navigator.clipboard.writeText(msg.emlPath).then(() => {
68
+ const status = document.getElementById("status-sync");
69
+ if (status)
70
+ status.textContent = `Path copied: ${msg.emlPath}`;
71
+ }).catch(() => {
72
+ prompt("EML file path:", msg.emlPath);
73
+ });
74
+ };
75
+ }
76
+ else {
77
+ srcBtn.hidden = true;
78
+ }
79
+ }
80
+ // Edit Draft / Send from Outbox button
81
+ const editBtn = document.getElementById("mv-edit-draft");
82
+ if (editBtn) {
83
+ const isDraft = specialUse === "drafts" || specialUse === "outbox";
84
+ if (isDraft) {
85
+ editBtn.hidden = false;
86
+ editBtn.textContent = specialUse === "outbox" ? "Edit & Send" : "Edit Draft";
87
+ editBtn.onclick = () => {
88
+ // Open compose window pre-filled with this draft
89
+ const init = {
90
+ mode: "draft",
91
+ accountId,
92
+ to: msg.to || [],
93
+ cc: msg.cc || [],
94
+ subject: msg.subject || "",
95
+ bodyHtml: msg.bodyHtml || "",
96
+ inReplyTo: msg.inReplyTo || "",
97
+ references: msg.references || [],
98
+ accounts: [],
99
+ draftUid: msg.uid,
100
+ draftFolderId: msg.folderId,
101
+ };
102
+ sessionStorage.setItem("composeInit", JSON.stringify(init));
103
+ window.open("/compose/compose.html", "_blank", "width=800,height=600,menubar=no,toolbar=no,status=no");
104
+ };
105
+ }
106
+ else {
107
+ editBtn.hidden = true;
108
+ }
109
+ }
110
+ // Remote content banner (collapsible dropdown with sender/recipient details)
41
111
  bodyEl.innerHTML = "";
42
112
  if (msg.hasRemoteContent) {
43
113
  const senderAddr = msg.from?.address || "";
114
+ const senderName = msg.from?.name || "";
44
115
  const senderDomain = senderAddr.split("@")[1] || "";
116
+ const deliveredTo = msg.deliveredTo || "";
117
+ const toAddr = msg.to?.[0]?.address || "";
118
+ const returnPath = msg.returnPath || "";
45
119
  const banner = document.createElement("div");
46
120
  banner.className = "mv-remote-banner";
47
- banner.innerHTML = `Remote content blocked from <b>${senderAddr}</b>. ` +
48
- `<button id="btn-load-remote">Load this time</button> ` +
49
- `<button id="btn-allow-sender">Always from ${senderAddr}</button> ` +
50
- (senderDomain ? `<button id="btn-allow-domain">Always from ${senderDomain}</button>` : "");
121
+ banner.innerHTML =
122
+ `<div class="mv-rb-summary">` +
123
+ `<span class="mv-rb-toggle">&#x25B8;</span>` +
124
+ `<span>Remote content blocked</span>` +
125
+ `<span class="mv-rb-buttons">` +
126
+ `<button id="btn-load-remote">Load once</button>` +
127
+ `<button id="btn-allow-sender" title="${escapeText(senderAddr)}">Always: ${escapeText(senderAddr)}</button>` +
128
+ (senderDomain ? `<button id="btn-allow-domain" title="*@${escapeText(senderDomain)}">Always: *@${escapeText(senderDomain)}</button>` : "") +
129
+ `</span>` +
130
+ `</div>` +
131
+ `<div class="mv-rb-details" hidden>` +
132
+ `<div class="mv-rb-info">` +
133
+ `<div><span class="mv-rb-label">From:</span> ${escapeText(senderName ? `${senderName} <${senderAddr}>` : senderAddr)}</div>` +
134
+ (deliveredTo ? `<div><span class="mv-rb-label">Delivered-To:</span> ${escapeText(deliveredTo)}</div>` : "") +
135
+ (toAddr && toAddr !== deliveredTo ? `<div><span class="mv-rb-label">To:</span> ${escapeText(toAddr)}</div>` : "") +
136
+ (returnPath && returnPath !== senderAddr ? `<div><span class="mv-rb-label">Return-Path:</span> ${escapeText(returnPath)}</div>` : "") +
137
+ `</div>` +
138
+ (deliveredTo || toAddr ? `<div class="mv-rb-actions"><button id="btn-allow-to">Always allow to: ${escapeText(deliveredTo || toAddr)}</button></div>` : "") +
139
+ `</div>`;
51
140
  bodyEl.appendChild(banner);
141
+ // Toggle dropdown — click arrow or text to expand details
142
+ const summary = banner.querySelector(".mv-rb-summary");
143
+ const details = banner.querySelector(".mv-rb-details");
144
+ const toggle = banner.querySelector(".mv-rb-toggle");
145
+ summary.addEventListener("click", (e) => {
146
+ if (e.target.tagName === "BUTTON")
147
+ return;
148
+ details.hidden = !details.hidden;
149
+ toggle.textContent = details.hidden ? "\u25B8" : "\u25BE";
150
+ });
52
151
  const loadRemote = async () => {
53
152
  banner.remove();
54
153
  const full = await getMessage(accountId, uid, true);
@@ -78,6 +177,17 @@ export async function showMessage(accountId, uid) {
78
177
  });
79
178
  loadRemote();
80
179
  });
180
+ banner.querySelector("#btn-allow-to")?.addEventListener("click", async () => {
181
+ const addr = deliveredTo || toAddr;
182
+ if (!addr)
183
+ return;
184
+ await fetch("/api/settings/allow-remote", {
185
+ method: "POST",
186
+ headers: { "Content-Type": "application/json" },
187
+ body: JSON.stringify({ type: "recipient", value: addr }),
188
+ });
189
+ loadRemote();
190
+ });
81
191
  }
82
192
  // Body in sandboxed iframe
83
193
  if (msg.bodyHtml) {
@@ -85,7 +195,8 @@ export async function showMessage(accountId, uid) {
85
195
  iframe.sandbox.add("allow-same-origin");
86
196
  iframe.sandbox.add("allow-popups");
87
197
  iframe.sandbox.add("allow-popups-to-escape-sandbox");
88
- iframe.srcdoc = wrapHtmlBody(msg.bodyHtml);
198
+ iframe.sandbox.add("allow-top-navigation-by-user-activation");
199
+ iframe.srcdoc = wrapHtmlBody(msg.bodyHtml, msg.remoteAllowed);
89
200
  bodyEl.appendChild(iframe);
90
201
  }
91
202
  else if (msg.bodyText) {
@@ -118,6 +229,11 @@ function formatAddr(addr) {
118
229
  return `${addr.name} <${addr.address}>`;
119
230
  return addr.address;
120
231
  }
232
+ function escapeText(s) {
233
+ const div = document.createElement("div");
234
+ div.textContent = s;
235
+ return div.innerHTML;
236
+ }
121
237
  function formatSize(bytes) {
122
238
  if (bytes < 1024)
123
239
  return `${bytes} B`;
@@ -126,9 +242,10 @@ function formatSize(bytes) {
126
242
  return `${(bytes / 1048576).toFixed(1)} MB`;
127
243
  }
128
244
  function wrapHtmlBody(html, allowRemote = false) {
245
+ // CSP blocks remote resource loading (tracking pixels, external CSS) but allows link clicks
129
246
  const csp = allowRemote
130
- ? "" // no CSP restriction when remote content allowed
131
- : `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data: cid:;">`;
247
+ ? ""
248
+ : `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data: cid:; form-action 'none';">`;
132
249
  return `<!DOCTYPE html>
133
250
  <html><head>
134
251
  <meta charset="UTF-8">
@@ -136,7 +253,7 @@ ${csp}
136
253
  <style>
137
254
  body {
138
255
  font-family: system-ui, sans-serif;
139
- font-size: 14px;
256
+ font-size: 17.5px;
140
257
  line-height: 1.5;
141
258
  color: #1a1a2e;
142
259
  background: #fff;
@@ -13,10 +13,10 @@
13
13
  </head>
14
14
  <body>
15
15
  <div class="compose-header">
16
- <div class="compose-field">
17
- <label for="compose-from">From</label>
18
- <input type="text" id="compose-from" list="compose-from-list" autocomplete="off">
19
- <datalist id="compose-from-list"></datalist>
16
+ <div class="compose-field compose-from-field">
17
+ <label for="compose-from-select">From</label>
18
+ <select id="compose-from-select"></select>
19
+ <input type="text" id="compose-from-custom" placeholder="Custom address..." hidden>
20
20
  </div>
21
21
  <div class="compose-field">
22
22
  <label for="compose-to">To</label>
@@ -18,27 +18,59 @@ const editor = new Quill("#compose-editor", {
18
18
  }
19
19
  });
20
20
  // ── Populate from init data ──
21
- const fromInput = document.getElementById("compose-from");
22
- const fromDatalist = document.getElementById("compose-from-list");
21
+ const fromSelect = document.getElementById("compose-from-select");
22
+ const fromCustom = document.getElementById("compose-from-custom");
23
23
  const toInput = document.getElementById("compose-to");
24
24
  const ccInput = document.getElementById("compose-cc");
25
25
  const bccInput = document.getElementById("compose-bcc");
26
26
  const subjectInput = document.getElementById("compose-subject");
27
- /** Extract account ID from the From field value, or return the raw value for custom addresses */
27
+ /** Populate the From dropdown with accounts */
28
+ function populateFromSelect(accounts, selectedId) {
29
+ fromSelect.innerHTML = "";
30
+ for (const acct of accounts) {
31
+ const opt = document.createElement("option");
32
+ opt.value = acct.id;
33
+ const displayLabel = acct.label || acct.name;
34
+ opt.textContent = `${displayLabel}: ${acct.name} <${acct.email}>`;
35
+ opt.dataset.email = acct.email;
36
+ opt.dataset.name = acct.name;
37
+ if (acct.defaultSend)
38
+ opt.dataset.defaultSend = "true";
39
+ if (acct.id === selectedId)
40
+ opt.selected = true;
41
+ fromSelect.appendChild(opt);
42
+ }
43
+ // "Other..." option for custom address
44
+ const other = document.createElement("option");
45
+ other.value = "__custom__";
46
+ other.textContent = "Other...";
47
+ fromSelect.appendChild(other);
48
+ }
49
+ fromSelect.addEventListener("change", () => {
50
+ if (fromSelect.value === "__custom__") {
51
+ fromCustom.hidden = false;
52
+ fromCustom.focus();
53
+ }
54
+ else {
55
+ fromCustom.hidden = true;
56
+ fromCustom.value = "";
57
+ }
58
+ });
59
+ /** Extract account ID from the From field */
28
60
  function getFromAccountId() {
29
- const val = fromInput.value;
30
- // Check datalist options for a matching account
31
- for (const opt of fromDatalist.options) {
32
- if (opt.value === val && opt.dataset.accountId)
33
- return opt.dataset.accountId;
61
+ if (fromSelect.value === "__custom__") {
62
+ // Custom address use default send account, fallback to first
63
+ const defaultOpt = Array.from(fromSelect.options).find(o => o.dataset.defaultSend === "true");
64
+ return defaultOpt?.value || fromSelect.options[0]?.value || "";
34
65
  }
35
- // No match — return first account ID as the sending account (custom From address)
36
- const first = fromDatalist.options[0];
37
- return first?.dataset.accountId || val;
66
+ return fromSelect.value;
38
67
  }
39
68
  /** Get the From address string for the message headers */
40
69
  function getFromAddress() {
41
- return fromInput.value;
70
+ if (fromSelect.value === "__custom__")
71
+ return fromCustom.value;
72
+ const opt = fromSelect.selectedOptions[0];
73
+ return opt ? `${opt.dataset.name} <${opt.dataset.email}>` : "";
42
74
  }
43
75
  /** Smart tab — skip to next empty field, ending at body */
44
76
  function smartTab(current) {
@@ -145,8 +177,7 @@ function setupAutocomplete(input) {
145
177
  e.preventDefault();
146
178
  const idx = activeIndex >= 0 ? activeIndex : 0;
147
179
  items[idx].dispatchEvent(new MouseEvent("mousedown"));
148
- // After selecting, smart-tab to next empty field
149
- setTimeout(() => smartTab(input), 50);
180
+ // Stay in field — user may want to add more addresses
150
181
  return;
151
182
  }
152
183
  }
@@ -176,16 +207,7 @@ function parseAddrs(s) {
176
207
  }
177
208
  function applyInit(init) {
178
209
  // Populate From dropdown
179
- fromDatalist.innerHTML = "";
180
- for (const acct of init.accounts) {
181
- const opt = document.createElement("option");
182
- opt.value = `${acct.name} <${acct.email}>`;
183
- opt.dataset.accountId = acct.id;
184
- fromDatalist.appendChild(opt);
185
- }
186
- const selectedAcct = init.accounts.find((a) => a.id === init.accountId) || init.accounts[0];
187
- if (selectedAcct)
188
- fromInput.value = `${selectedAcct.name} <${selectedAcct.email}>`;
210
+ populateFromSelect(init.accounts, init.accountId);
189
211
  toInput.value = formatAddrs(init.to);
190
212
  ccInput.value = formatAddrs(init.cc);
191
213
  subjectInput.value = init.subject;
@@ -193,6 +215,10 @@ function applyInit(init) {
193
215
  editor.clipboard.dangerouslyPasteHTML(init.bodyHtml);
194
216
  editor.setSelection(0, 0);
195
217
  }
218
+ // If resuming a draft, track its UID for deletion after send
219
+ if (init.draftUid) {
220
+ draftUid = init.draftUid;
221
+ }
196
222
  document.title = init.subject ? `${init.subject} - Compose` : "Compose - mailx";
197
223
  // Focus first empty field: To → Subject → body
198
224
  if (!toInput.value)
@@ -213,19 +239,11 @@ else {
213
239
  toInput.focus();
214
240
  }
215
241
  // If From dropdown is empty (new compose without init, or init had no accounts), fetch from API
216
- if (fromDatalist.children.length === 0) {
242
+ if (fromSelect.options.length === 0) {
217
243
  fetch("/api/accounts")
218
244
  .then(r => r.json())
219
245
  .then((accounts) => {
220
- for (const acct of accounts) {
221
- const opt = document.createElement("option");
222
- opt.value = `${acct.name} <${acct.email}>`;
223
- opt.dataset.accountId = acct.id;
224
- fromDatalist.appendChild(opt);
225
- }
226
- if (!fromInput.value && accounts.length > 0) {
227
- fromInput.value = `${accounts[0].name} <${accounts[0].email}>`;
228
- }
246
+ populateFromSelect(accounts);
229
247
  })
230
248
  .catch(e => console.error("Failed to load accounts:", e));
231
249
  }
@@ -270,6 +288,7 @@ document.getElementById("btn-send")?.addEventListener("click", async () => {
270
288
  btn.textContent = "Sending...";
271
289
  const body = {
272
290
  from: getFromAccountId(),
291
+ fromAddress: getFromAddress(),
273
292
  to: parseAddrs(toInput.value),
274
293
  cc: parseAddrs(ccInput.value),
275
294
  bcc: parseAddrs(bccInput.value),
package/client/index.html CHANGED
@@ -48,21 +48,29 @@
48
48
  <search>
49
49
  <input type="search" id="search-input" placeholder="Search..." autocomplete="off">
50
50
  </search>
51
- <button class="tb-btn" id="btn-sync" title="Sync">
52
- <span class="tb-icon">&#x21BB;</span>
51
+ <button class="tb-btn" id="btn-sync" title="Sync all folders (F5)">
52
+ <span class="tb-icon">&#x21BB;</span> Sync
53
53
  </button>
54
- <button class="tb-btn" id="btn-restart" title="Restart server + reload">
55
- <span class="tb-icon">&#x26A1;</span>
54
+ <button class="tb-btn" id="btn-restart" title="Restart server and reload page">
55
+ <span class="tb-icon">&#x26A1;</span> Restart
56
56
  </button>
57
57
  </div>
58
58
  </header>
59
59
 
60
- <nav class="folder-tree" id="folder-tree">
61
- <div class="folder-loading">Loading accounts...</div>
62
- </nav>
60
+ <div class="folder-panel">
61
+ <div class="ft-filter">
62
+ <input type="text" id="ft-filter-input" placeholder="Find folder..." autocomplete="off">
63
+ </div>
64
+ <nav class="folder-tree" id="folder-tree">
65
+ <div class="folder-loading">Loading accounts...</div>
66
+ </nav>
67
+ </div>
63
68
 
64
69
  <main class="main-area">
65
70
  <section class="message-list" id="message-list">
71
+ <div class="ml-filter">
72
+ <input type="text" id="ml-filter-input" placeholder="Filter..." autocomplete="off">
73
+ </div>
66
74
  <div class="ml-header">
67
75
  <span class="ml-col ml-col-flag"></span>
68
76
  <span class="ml-col ml-col-from" data-sort="from">From</span>
@@ -78,8 +86,17 @@
78
86
 
79
87
  <section class="message-viewer" id="message-viewer">
80
88
  <div class="mv-header" id="mv-header" hidden>
81
- <div class="mv-from"></div>
82
- <div class="mv-to"></div>
89
+ <div class="mv-header-top">
90
+ <div class="mv-header-info">
91
+ <div class="mv-from"></div>
92
+ <div class="mv-to"></div>
93
+ </div>
94
+ <div class="mv-header-actions">
95
+ <button class="mv-action mv-action-primary" id="mv-edit-draft" hidden>Edit &amp; Send</button>
96
+ <a class="mv-unsubscribe" id="mv-unsubscribe" hidden>Unsubscribe</a>
97
+ <button class="mv-action" id="mv-view-source" title="View source (.eml)" hidden>Source</button>
98
+ </div>
99
+ </div>
83
100
  <div class="mv-subject"></div>
84
101
  <div class="mv-date"></div>
85
102
  </div>
@@ -96,5 +113,12 @@
96
113
  <span id="status-pending"></span>
97
114
  <span id="status-queue">Queue: empty</span>
98
115
  </footer>
116
+
117
+ <div id="startup-overlay" class="startup-overlay">
118
+ <div class="startup-content">
119
+ <div class="startup-spinner"></div>
120
+ <div id="startup-status">Connecting to server...</div>
121
+ </div>
122
+ </div>
99
123
  </body>
100
124
  </html>
@@ -1,8 +1,23 @@
1
1
  /**
2
- * REST + WebSocket client for communicating with mailx-server.
3
- * All server communication goes through here.
2
+ * API client auto-detects IPC (WebView) vs HTTP (browser).
3
+ * When mailxapi is available (injected by launcher), calls go directly via IPC.
4
+ * Otherwise falls back to REST/WebSocket.
4
5
  */
5
- /** Generic fetch wrapper */
6
+ const hasIPC = typeof mailxapi !== "undefined" && mailxapi?.isApp;
7
+ // ── HTTP fallback ──
8
+ // Abort controller for message-list requests — cancel stale fetches when folder changes
9
+ let messageListAbort = null;
10
+ export function abortMessageListRequests() {
11
+ if (messageListAbort) {
12
+ messageListAbort.abort();
13
+ messageListAbort = null;
14
+ }
15
+ }
16
+ function newMessageListSignal() {
17
+ abortMessageListRequests();
18
+ messageListAbort = new AbortController();
19
+ return messageListAbort.signal;
20
+ }
6
21
  async function api(path, options) {
7
22
  const res = await fetch(`/api${path}`, {
8
23
  headers: { "Content-Type": "application/json" },
@@ -14,54 +29,111 @@ async function api(path, options) {
14
29
  }
15
30
  return res.json();
16
31
  }
17
- // ── REST Methods ──
32
+ // ── API Methods (IPC or HTTP) ──
18
33
  export function getAccounts() {
34
+ if (hasIPC)
35
+ return mailxapi.getAccounts();
19
36
  return api("/accounts");
20
37
  }
21
38
  export function getFolders(accountId) {
39
+ if (hasIPC)
40
+ return mailxapi.getFolders(accountId);
22
41
  return api(`/folders/${accountId}`);
23
42
  }
24
43
  export function getMessages(accountId, folderId, page = 1, pageSize = 50) {
25
- return api(`/messages/${accountId}/${folderId}?page=${page}&pageSize=${pageSize}`);
44
+ if (hasIPC)
45
+ return mailxapi.getMessages(accountId, folderId, page, pageSize);
46
+ const signal = newMessageListSignal();
47
+ return api(`/messages/${accountId}/${folderId}?page=${page}&pageSize=${pageSize}`, { signal });
48
+ }
49
+ export function getUnifiedInbox(page = 1, pageSize = 50) {
50
+ if (hasIPC)
51
+ return mailxapi.getUnifiedInbox(page, pageSize);
52
+ const signal = newMessageListSignal();
53
+ return api(`/messages/unified/inbox?page=${page}&pageSize=${pageSize}`, { signal });
26
54
  }
27
55
  export function searchMessages(query, page = 1, pageSize = 50) {
56
+ if (hasIPC)
57
+ return mailxapi.searchMessages(query, page, pageSize);
28
58
  return api(`/search?q=${encodeURIComponent(query)}&page=${page}&pageSize=${pageSize}`);
29
59
  }
30
- export function getUnifiedInbox(page = 1, pageSize = 50) {
31
- return api(`/messages/unified/inbox?page=${page}&pageSize=${pageSize}`);
32
- }
33
- export function getMessage(accountId, uid, allowRemote = false) {
34
- const q = allowRemote ? "?allowRemote=true" : "";
60
+ export function getMessage(accountId, uid, allowRemote = false, folderId) {
61
+ if (hasIPC)
62
+ return mailxapi.getMessage(accountId, uid, allowRemote, folderId);
63
+ const params = new URLSearchParams();
64
+ if (allowRemote)
65
+ params.set("allowRemote", "true");
66
+ if (folderId != null)
67
+ params.set("folderId", String(folderId));
68
+ const q = params.toString() ? `?${params}` : "";
35
69
  return api(`/message/${accountId}/${uid}${q}`);
36
70
  }
37
71
  export function updateFlags(accountId, uid, flags) {
72
+ if (hasIPC)
73
+ return mailxapi.updateFlags(accountId, uid, flags);
38
74
  return api(`/message/${accountId}/${uid}/flags`, {
39
75
  method: "PATCH",
40
76
  body: JSON.stringify({ flags })
41
77
  });
42
78
  }
43
79
  export function triggerSync() {
80
+ if (hasIPC)
81
+ return mailxapi.syncAll();
44
82
  return api("/sync", { method: "POST" });
45
83
  }
46
- let ws;
47
- const handlers = [];
48
- export function connectWebSocket() {
49
- const protocol = location.protocol === "https:" ? "wss:" : "ws:";
50
- ws = new WebSocket(`${protocol}//${location.host}`);
51
- ws.onmessage = (ev) => {
52
- try {
53
- const event = JSON.parse(ev.data);
54
- for (const handler of handlers)
55
- handler(event);
56
- }
57
- catch { /* ignore parse errors */ }
58
- };
59
- ws.onclose = () => {
60
- // Reconnect after 3 seconds
61
- setTimeout(connectWebSocket, 3000);
62
- };
63
- }
64
- export function onWsEvent(handler) {
65
- handlers.push(handler);
84
+ export function getVersion() {
85
+ if (hasIPC)
86
+ return mailxapi.getVersion();
87
+ return api("/version");
88
+ }
89
+ export function getSyncPending() {
90
+ if (hasIPC)
91
+ return mailxapi.getSyncPending();
92
+ return api("/sync/pending");
93
+ }
94
+ export function searchContacts(query) {
95
+ if (hasIPC)
96
+ return mailxapi.searchContacts(query);
97
+ return api(`/contacts?q=${encodeURIComponent(query)}`);
98
+ }
99
+ export function allowRemoteContent(type, value) {
100
+ if (hasIPC)
101
+ return mailxapi.allowRemoteContent(type, value);
102
+ return api("/settings/allow-remote", {
103
+ method: "POST",
104
+ body: JSON.stringify({ type, value })
105
+ });
106
+ }
107
+ const eventHandlers = [];
108
+ export function onEvent(handler) {
109
+ eventHandlers.push(handler);
110
+ }
111
+ export function connectEvents() {
112
+ if (hasIPC) {
113
+ // IPC events come via mailxapi.onEvent
114
+ mailxapi.onEvent((event) => {
115
+ for (const h of eventHandlers)
116
+ h(event);
117
+ });
118
+ }
119
+ else {
120
+ // WebSocket for HTTP mode
121
+ const protocol = location.protocol === "https:" ? "wss:" : "ws:";
122
+ const ws = new WebSocket(`${protocol}//${location.host}`);
123
+ ws.onmessage = (ev) => {
124
+ try {
125
+ const event = JSON.parse(ev.data);
126
+ for (const h of eventHandlers)
127
+ h(event);
128
+ }
129
+ catch { /* ignore */ }
130
+ };
131
+ ws.onclose = () => {
132
+ setTimeout(connectEvents, 3000);
133
+ };
134
+ }
66
135
  }
136
+ // Legacy exports for backward compatibility
137
+ export const connectWebSocket = connectEvents;
138
+ export const onWsEvent = onEvent;
67
139
  //# sourceMappingURL=api-client.js.map