@rapidmx/web-client 0.10.0 → 0.12.0

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 (324) hide show
  1. package/README.md +183 -2
  2. package/apps/admin/_layout.tsx +8 -2
  3. package/apps/admin/index.tsx +7 -1
  4. package/apps/admin/ingest-queue/index.tsx +2 -1
  5. package/apps/admin/mailboxes/[uid].tsx +23 -10
  6. package/apps/admin/mailboxes/new/index.tsx +3 -3
  7. package/apps/admin/quarantine/index.tsx +2 -1
  8. package/apps/admin/signing-certificates/index.tsx +343 -0
  9. package/apps/escrow/_layout.tsx +8 -2
  10. package/apps/shared/appearance/AppearanceForm.tsx +392 -0
  11. package/apps/shared/appearance/AppearanceHead.tsx +49 -0
  12. package/apps/shared/appearance/AppearanceProvider.tsx +470 -0
  13. package/apps/shared/appearance/ColorField.tsx +107 -0
  14. package/apps/shared/appearance/appearanceCache.ts +106 -0
  15. package/apps/shared/appearance/appearanceContext.ts +77 -0
  16. package/apps/shared/appearance/bootScript.ts +35 -0
  17. package/apps/shared/appearance/color.ts +169 -0
  18. package/apps/shared/appearance/photo.ts +88 -0
  19. package/apps/shared/appearance/resolvedTheme.ts +67 -0
  20. package/apps/shared/appearance/theme.ts +355 -0
  21. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +4 -4
  22. package/apps/shared/components/admin/layout/AdminShell.tsx +22 -10
  23. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +107 -60
  24. package/apps/shared/components/admin/settings/BrandingForm.tsx +24 -0
  25. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +60 -14
  26. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +65 -16
  27. package/apps/shared/components/admin/settings/PluginsManager.tsx +228 -25
  28. package/apps/shared/components/admin/setup/SetupWizard.tsx +2 -1
  29. package/apps/shared/components/calendar/CalendarListSidebar.tsx +4 -5
  30. package/apps/shared/components/contacts/ContactForm.tsx +11 -5
  31. package/apps/shared/components/contacts/ContactsSidebar.tsx +5 -3
  32. package/apps/shared/components/contacts/ContactsToolbar.tsx +25 -52
  33. package/apps/shared/components/contacts/layout/ContactsShell.tsx +10 -5
  34. package/apps/shared/components/escrow/layout/EscrowShell.tsx +13 -6
  35. package/apps/shared/components/layout/AppShell.tsx +98 -32
  36. package/apps/shared/components/layout/BrandingChrome.tsx +200 -13
  37. package/apps/shared/components/layout/RailIcon.tsx +100 -0
  38. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -0
  39. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -0
  40. package/apps/shared/components/layout/UserMenu.tsx +75 -6
  41. package/apps/shared/components/mail/ConversationList.tsx +4 -2
  42. package/apps/shared/components/mail/ConversationThreadPane.tsx +487 -475
  43. package/apps/shared/components/mail/LazyReadingPane.tsx +14 -16
  44. package/apps/shared/components/mail/MailSelectionBar.tsx +2 -10
  45. package/apps/shared/components/mail/MessageDetailPane.tsx +366 -287
  46. package/apps/shared/components/mail/OutboxBadge.tsx +44 -0
  47. package/apps/shared/components/mail/OutboxRowStatus.tsx +31 -0
  48. package/apps/shared/components/mail/compose/ComposeContext.tsx +35 -2
  49. package/apps/shared/components/mail/compose/ComposeWindow.tsx +239 -437
  50. package/apps/shared/components/mail/compose/encryptionRequirement.ts +86 -0
  51. package/apps/shared/components/mail/layout/MailShell.tsx +39 -25
  52. package/apps/shared/components/mail/reading/EncryptedBody.tsx +65 -0
  53. package/apps/shared/components/mail/reading/EncryptedPreview.tsx +35 -0
  54. package/apps/shared/components/mail/reading/MessageBody.tsx +240 -0
  55. package/apps/shared/components/mail/reading/MessageCard.tsx +174 -0
  56. package/apps/shared/components/mail/reading/bodyContent.ts +100 -0
  57. package/apps/shared/components/mail/reading/bodyHtml.ts +298 -0
  58. package/apps/shared/components/mail/reading/color.ts +245 -0
  59. package/apps/shared/components/mail/reading/frameControl.ts +142 -0
  60. package/apps/shared/components/mail/reading/frameDocument.ts +123 -0
  61. package/apps/shared/components/mail/reading/safeDocument.ts +24 -0
  62. package/apps/shared/components/mail/reading/themeAdaptation.ts +221 -0
  63. package/apps/shared/components/mail/reading/themeSurface.ts +90 -0
  64. package/apps/shared/components/mail/reading/viewOriginal.ts +38 -0
  65. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -0
  66. package/apps/shared/components/settings/layout/SettingsShell.tsx +1 -0
  67. package/apps/shared/components/sharing/PrincipalListField.tsx +82 -0
  68. package/apps/shared/components/sharing/PrincipalPicker.tsx +76 -0
  69. package/apps/shared/components/sharing/PrincipalResolver.tsx +164 -0
  70. package/apps/shared/components/tasks/TasksSidebar.tsx +5 -3
  71. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -5
  72. package/apps/shared/mail/folderCounts.ts +9 -3
  73. package/apps/shared/mail/folderTree.ts +143 -0
  74. package/apps/shared/mail/outbox/UnlockBridge.tsx +23 -0
  75. package/apps/shared/mail/outbox/composeBridge.ts +69 -0
  76. package/apps/shared/mail/outbox/outboxState.ts +88 -0
  77. package/apps/shared/mail/outbox/pendingSends.ts +146 -0
  78. package/apps/shared/mail/outbox/sendDecision.ts +157 -0
  79. package/apps/shared/mail/outbox/sendJob.ts +427 -0
  80. package/apps/shared/mail/outbox/sendOutcomes.ts +146 -0
  81. package/apps/shared/mail/outbox/sendState.ts +35 -0
  82. package/apps/shared/mail/outbox/useOutboxStatus.ts +61 -0
  83. package/apps/shared/mail/useMailConnection.ts +81 -28
  84. package/apps/shared/mail/useMailLiveUpdates.ts +55 -10
  85. package/apps/shared/mail/useNewMailNotifications.ts +47 -32
  86. package/apps/shared/navigation/AppRouter.tsx +2 -2
  87. package/apps/shared/notifications/NotificationCenter.tsx +215 -0
  88. package/apps/shared/notifications/NotificationHistoryDialog.tsx +90 -0
  89. package/apps/shared/notifications/apiErrors.ts +98 -0
  90. package/apps/shared/notifications/headerOffset.ts +35 -0
  91. package/apps/shared/notifications/pushStatus.ts +56 -0
  92. package/apps/shared/notifications/store.ts +549 -0
  93. package/apps/shared/notifications/systemErrors.ts +63 -0
  94. package/apps/shared/notifications/useNotifications.ts +41 -0
  95. package/apps/shared/signing/enrollmentStorage.ts +33 -0
  96. package/apps/shared/signing/enrollmentTracker.ts +385 -0
  97. package/apps/shared/signing/enrollmentView.ts +251 -0
  98. package/apps/shared/signing/signingInfo.ts +58 -0
  99. package/apps/shared/signing/useNow.ts +19 -0
  100. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +90 -0
  101. package/apps/shared/styles/app.css +80 -0
  102. package/apps/www/_layout.tsx +8 -2
  103. package/apps/www/_routes.ts +1 -0
  104. package/apps/www/calendar/index.tsx +9 -14
  105. package/apps/www/contacts/[uid].tsx +5 -8
  106. package/apps/www/contacts/index.tsx +587 -584
  107. package/apps/www/index.tsx +38 -27
  108. package/apps/www/messages/[uid].tsx +3 -1
  109. package/apps/www/settings/appearance/index.tsx +21 -0
  110. package/apps/www/settings/auto-reply/index.tsx +3 -7
  111. package/apps/www/settings/encryption/index.tsx +151 -116
  112. package/apps/www/settings/filters/[uid].tsx +3 -1
  113. package/apps/www/settings/filters/new/index.tsx +3 -1
  114. package/apps/www/settings/privacy/index.tsx +3 -8
  115. package/apps/www/settings/read-receipts/index.tsx +3 -7
  116. package/apps/www/settings/sharing/index.tsx +44 -65
  117. package/apps/www/settings/signatures/[uid].tsx +3 -1
  118. package/apps/www/settings/signatures/new/index.tsx +3 -2
  119. package/apps/www/tasks/index.tsx +11 -16
  120. package/dist/apps/admin/_layout.d.ts +5 -1
  121. package/dist/apps/admin/_layout.js +3 -2
  122. package/dist/apps/admin/index.js +3 -2
  123. package/dist/apps/admin/ingest-queue/index.js +2 -1
  124. package/dist/apps/admin/mailboxes/[uid].js +9 -6
  125. package/dist/apps/admin/mailboxes/new/index.js +1 -1
  126. package/dist/apps/admin/quarantine/index.js +2 -1
  127. package/dist/apps/admin/signing-certificates/index.d.ts +3 -0
  128. package/dist/apps/admin/signing-certificates/index.js +161 -0
  129. package/dist/apps/escrow/_layout.d.ts +5 -1
  130. package/dist/apps/escrow/_layout.js +3 -2
  131. package/dist/apps/shared/appearance/AppearanceForm.d.ts +7 -0
  132. package/dist/apps/shared/appearance/AppearanceForm.js +131 -0
  133. package/dist/apps/shared/appearance/AppearanceHead.d.ts +15 -0
  134. package/dist/apps/shared/appearance/AppearanceHead.js +31 -0
  135. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +32 -0
  136. package/dist/apps/shared/appearance/AppearanceProvider.js +390 -0
  137. package/dist/apps/shared/appearance/ColorField.d.ts +22 -0
  138. package/dist/apps/shared/appearance/ColorField.js +40 -0
  139. package/dist/apps/shared/appearance/appearanceCache.d.ts +45 -0
  140. package/dist/apps/shared/appearance/appearanceCache.js +80 -0
  141. package/dist/apps/shared/appearance/appearanceContext.d.ts +45 -0
  142. package/dist/apps/shared/appearance/appearanceContext.js +29 -0
  143. package/dist/apps/shared/appearance/bootScript.d.ts +16 -0
  144. package/dist/apps/shared/appearance/bootScript.js +33 -0
  145. package/dist/apps/shared/appearance/color.d.ts +65 -0
  146. package/dist/apps/shared/appearance/color.js +132 -0
  147. package/dist/apps/shared/appearance/photo.d.ts +17 -0
  148. package/dist/apps/shared/appearance/photo.js +79 -0
  149. package/dist/apps/shared/appearance/resolvedTheme.d.ts +21 -0
  150. package/dist/apps/shared/appearance/resolvedTheme.js +55 -0
  151. package/dist/apps/shared/appearance/theme.d.ts +111 -0
  152. package/dist/apps/shared/appearance/theme.js +251 -0
  153. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -2
  154. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  155. package/dist/apps/shared/components/admin/layout/AdminShell.js +16 -6
  156. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.d.ts +10 -5
  157. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +49 -34
  158. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +2 -0
  159. package/dist/apps/shared/components/admin/settings/BrandingForm.js +7 -1
  160. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +25 -5
  161. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +18 -7
  162. package/dist/apps/shared/components/admin/settings/PluginsManager.js +106 -20
  163. package/dist/apps/shared/components/admin/setup/SetupWizard.js +2 -1
  164. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +8 -8
  165. package/dist/apps/shared/components/contacts/ContactForm.js +1 -1
  166. package/dist/apps/shared/components/contacts/ContactsSidebar.js +4 -2
  167. package/dist/apps/shared/components/contacts/ContactsToolbar.d.ts +3 -1
  168. package/dist/apps/shared/components/contacts/ContactsToolbar.js +24 -9
  169. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +4 -1
  170. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -4
  171. package/dist/apps/shared/components/layout/AppShell.d.ts +6 -1
  172. package/dist/apps/shared/components/layout/AppShell.js +59 -13
  173. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +69 -10
  174. package/dist/apps/shared/components/layout/BrandingChrome.js +140 -12
  175. package/dist/apps/shared/components/layout/RailIcon.d.ts +21 -0
  176. package/dist/apps/shared/components/layout/RailIcon.js +85 -0
  177. package/dist/apps/shared/components/layout/ResponsiveToolbar.d.ts +55 -0
  178. package/dist/apps/shared/components/layout/ResponsiveToolbar.js +199 -0
  179. package/dist/apps/shared/components/layout/ThemeSwitch.d.ts +9 -0
  180. package/dist/apps/shared/components/layout/ThemeSwitch.js +54 -0
  181. package/dist/apps/shared/components/layout/UserMenu.d.ts +7 -1
  182. package/dist/apps/shared/components/layout/UserMenu.js +41 -7
  183. package/dist/apps/shared/components/mail/ConversationList.js +3 -2
  184. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +2 -1
  185. package/dist/apps/shared/components/mail/ConversationThreadPane.js +42 -20
  186. package/dist/apps/shared/components/mail/LazyReadingPane.js +9 -8
  187. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +3 -4
  188. package/dist/apps/shared/components/mail/MailSelectionBar.js +2 -3
  189. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +18 -7
  190. package/dist/apps/shared/components/mail/MessageDetailPane.js +86 -41
  191. package/dist/apps/shared/components/mail/OutboxBadge.d.ts +17 -0
  192. package/dist/apps/shared/components/mail/OutboxBadge.js +18 -0
  193. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +10 -0
  194. package/dist/apps/shared/components/mail/OutboxRowStatus.js +19 -0
  195. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +8 -0
  196. package/dist/apps/shared/components/mail/compose/ComposeContext.js +28 -3
  197. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +2 -1
  198. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +229 -328
  199. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +49 -0
  200. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +35 -0
  201. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +2 -0
  202. package/dist/apps/shared/components/mail/layout/MailShell.js +20 -14
  203. package/dist/apps/shared/components/mail/reading/EncryptedBody.d.ts +21 -0
  204. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +22 -0
  205. package/dist/apps/shared/components/mail/reading/EncryptedPreview.d.ts +18 -0
  206. package/dist/apps/shared/components/mail/reading/EncryptedPreview.js +22 -0
  207. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +32 -0
  208. package/dist/apps/shared/components/mail/reading/MessageBody.js +136 -0
  209. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +62 -0
  210. package/dist/apps/shared/components/mail/reading/MessageCard.js +58 -0
  211. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +38 -0
  212. package/dist/apps/shared/components/mail/reading/bodyContent.js +84 -0
  213. package/dist/apps/shared/components/mail/reading/bodyHtml.d.ts +47 -0
  214. package/dist/apps/shared/components/mail/reading/bodyHtml.js +267 -0
  215. package/dist/apps/shared/components/mail/reading/color.d.ts +63 -0
  216. package/dist/apps/shared/components/mail/reading/color.js +201 -0
  217. package/dist/apps/shared/components/mail/reading/frameControl.d.ts +33 -0
  218. package/dist/apps/shared/components/mail/reading/frameControl.js +120 -0
  219. package/dist/apps/shared/components/mail/reading/frameDocument.d.ts +52 -0
  220. package/dist/apps/shared/components/mail/reading/frameDocument.js +98 -0
  221. package/dist/apps/shared/components/mail/reading/safeDocument.d.ts +3 -0
  222. package/dist/apps/shared/components/mail/reading/safeDocument.js +12 -0
  223. package/dist/apps/shared/components/mail/reading/themeAdaptation.d.ts +56 -0
  224. package/dist/apps/shared/components/mail/reading/themeAdaptation.js +144 -0
  225. package/dist/apps/shared/components/mail/reading/themeSurface.d.ts +17 -0
  226. package/dist/apps/shared/components/mail/reading/themeSurface.js +83 -0
  227. package/dist/apps/shared/components/mail/reading/viewOriginal.d.ts +9 -0
  228. package/dist/apps/shared/components/mail/reading/viewOriginal.js +32 -0
  229. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +32 -0
  230. package/dist/apps/shared/components/settings/SigningCertificateCard.js +98 -0
  231. package/dist/apps/shared/components/settings/layout/SettingsShell.js +1 -0
  232. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +24 -0
  233. package/dist/apps/shared/components/sharing/PrincipalListField.js +23 -0
  234. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +28 -0
  235. package/dist/apps/shared/components/sharing/PrincipalPicker.js +24 -0
  236. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +44 -0
  237. package/dist/apps/shared/components/sharing/PrincipalResolver.js +92 -0
  238. package/dist/apps/shared/components/tasks/TasksSidebar.js +4 -2
  239. package/dist/apps/shared/components/tasks/layout/TasksShell.js +3 -1
  240. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  241. package/dist/apps/shared/mail/folderCounts.js +9 -3
  242. package/dist/apps/shared/mail/folderTree.d.ts +55 -0
  243. package/dist/apps/shared/mail/folderTree.js +116 -0
  244. package/dist/apps/shared/mail/outbox/UnlockBridge.d.ts +5 -0
  245. package/dist/apps/shared/mail/outbox/UnlockBridge.js +18 -0
  246. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +34 -0
  247. package/dist/apps/shared/mail/outbox/composeBridge.js +32 -0
  248. package/dist/apps/shared/mail/outbox/outboxState.d.ts +41 -0
  249. package/dist/apps/shared/mail/outbox/outboxState.js +42 -0
  250. package/dist/apps/shared/mail/outbox/pendingSends.d.ts +39 -0
  251. package/dist/apps/shared/mail/outbox/pendingSends.js +109 -0
  252. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +80 -0
  253. package/dist/apps/shared/mail/outbox/sendDecision.js +94 -0
  254. package/dist/apps/shared/mail/outbox/sendJob.d.ts +68 -0
  255. package/dist/apps/shared/mail/outbox/sendJob.js +319 -0
  256. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +3 -0
  257. package/dist/apps/shared/mail/outbox/sendOutcomes.js +141 -0
  258. package/dist/apps/shared/mail/outbox/sendState.d.ts +25 -0
  259. package/dist/apps/shared/mail/outbox/sendState.js +8 -0
  260. package/dist/apps/shared/mail/outbox/useOutboxStatus.d.ts +11 -0
  261. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +49 -0
  262. package/dist/apps/shared/mail/useMailConnection.d.ts +9 -8
  263. package/dist/apps/shared/mail/useMailConnection.js +64 -22
  264. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +17 -4
  265. package/dist/apps/shared/mail/useMailLiveUpdates.js +36 -10
  266. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +4 -10
  267. package/dist/apps/shared/mail/useNewMailNotifications.js +43 -23
  268. package/dist/apps/shared/navigation/AppRouter.js +2 -2
  269. package/dist/apps/shared/notifications/NotificationCenter.d.ts +19 -0
  270. package/dist/apps/shared/notifications/NotificationCenter.js +90 -0
  271. package/dist/apps/shared/notifications/NotificationHistoryDialog.d.ts +10 -0
  272. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +36 -0
  273. package/dist/apps/shared/notifications/apiErrors.d.ts +19 -0
  274. package/dist/apps/shared/notifications/apiErrors.js +86 -0
  275. package/dist/apps/shared/notifications/headerOffset.d.ts +8 -0
  276. package/dist/apps/shared/notifications/headerOffset.js +33 -0
  277. package/dist/apps/shared/notifications/pushStatus.d.ts +9 -0
  278. package/dist/apps/shared/notifications/pushStatus.js +54 -0
  279. package/dist/apps/shared/notifications/store.d.ts +136 -0
  280. package/dist/apps/shared/notifications/store.js +416 -0
  281. package/dist/apps/shared/notifications/systemErrors.d.ts +9 -0
  282. package/dist/apps/shared/notifications/systemErrors.js +58 -0
  283. package/dist/apps/shared/notifications/useNotifications.d.ts +17 -0
  284. package/dist/apps/shared/notifications/useNotifications.js +18 -0
  285. package/dist/apps/shared/signing/enrollmentStorage.d.ts +10 -0
  286. package/dist/apps/shared/signing/enrollmentStorage.js +33 -0
  287. package/dist/apps/shared/signing/enrollmentTracker.d.ts +73 -0
  288. package/dist/apps/shared/signing/enrollmentTracker.js +307 -0
  289. package/dist/apps/shared/signing/enrollmentView.d.ts +57 -0
  290. package/dist/apps/shared/signing/enrollmentView.js +214 -0
  291. package/dist/apps/shared/signing/signingInfo.d.ts +11 -0
  292. package/dist/apps/shared/signing/signingInfo.js +48 -0
  293. package/dist/apps/shared/signing/useNow.d.ts +2 -0
  294. package/dist/apps/shared/signing/useNow.js +18 -0
  295. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +16 -0
  296. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +87 -0
  297. package/dist/apps/shared/styles/app.css +80 -0
  298. package/dist/apps/www/_layout.d.ts +5 -1
  299. package/dist/apps/www/_layout.js +3 -2
  300. package/dist/apps/www/_routes.js +1 -0
  301. package/dist/apps/www/calendar/index.js +3 -4
  302. package/dist/apps/www/contacts/[uid].js +5 -7
  303. package/dist/apps/www/contacts/index.js +14 -21
  304. package/dist/apps/www/index.js +37 -19
  305. package/dist/apps/www/messages/[uid].js +3 -1
  306. package/dist/apps/www/settings/appearance/index.d.ts +4 -0
  307. package/dist/apps/www/settings/appearance/index.js +13 -0
  308. package/dist/apps/www/settings/auto-reply/index.js +3 -6
  309. package/dist/apps/www/settings/encryption/index.js +103 -95
  310. package/dist/apps/www/settings/filters/[uid].js +3 -1
  311. package/dist/apps/www/settings/filters/new/index.js +3 -1
  312. package/dist/apps/www/settings/privacy/index.js +5 -8
  313. package/dist/apps/www/settings/read-receipts/index.js +3 -6
  314. package/dist/apps/www/settings/sharing/index.js +19 -33
  315. package/dist/apps/www/settings/signatures/[uid].js +3 -1
  316. package/dist/apps/www/settings/signatures/new/index.js +3 -2
  317. package/dist/apps/www/tasks/index.js +8 -14
  318. package/package.json +2 -2
  319. package/apps/shared/components/mail/NewMailToasts.tsx +0 -141
  320. package/apps/shared/components/mail/compose/SendFailureAlert.tsx +0 -48
  321. package/dist/apps/shared/components/mail/NewMailToasts.d.ts +0 -19
  322. package/dist/apps/shared/components/mail/NewMailToasts.js +0 -55
  323. package/dist/apps/shared/components/mail/compose/SendFailureAlert.d.ts +0 -14
  324. package/dist/apps/shared/components/mail/compose/SendFailureAlert.js +0 -11
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  ///////////////////////////////////////////////////////////////////////////////
3
3
  // Copyright (C) 2026 Jean-Philippe Steinmetz
4
4
  // SPDX-License-Identifier: MPL-2.0
@@ -8,11 +8,12 @@ import { ApiRequestError } from "@rapidmx/react-shared/util/api.js";
8
8
  import { listAttachments } from "@rapidmx/react-shared/mail/mailApi.js";
9
9
  import { listConversationMessages } from "@rapidmx/react-shared/mail/conversationsApi.js";
10
10
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
11
- import MailAddress from "./MailAddress.js";
12
11
  import MessageDetailPane from "./MessageDetailPane.js";
12
+ import { EncryptedPreview, displaySubject } from "./reading/EncryptedPreview.js";
13
+ import { CollapsedCard, SkeletonCards, SubjectCard } from "./reading/MessageCard.js";
13
14
  import { useMailShell } from "./layout/MailShell.js";
14
15
  import { setReadState } from "../../mail/messageReadState.js";
15
- import { ROW_FOCUS_CLASS, UnreadBar, UnreadLabel, dateClass, isUnread, senderClass } from "./unreadStyle.js";
16
+ import { dateClass, isUnread, senderClass } from "./unreadStyle.js";
16
17
  /** One request's worth of the thread. The server's own default for `listConversationMessages()`. */
17
18
  export const THREAD_PAGE_SIZE = 100;
18
19
  /**
@@ -86,7 +87,8 @@ function hasFocusRing(element) {
86
87
  }
87
88
  }
88
89
  /**
89
- * The reading pane for the conversation list: the whole thread, **newest at the top**, opened at the message
90
+ * The reading pane for the conversation list: the conversation's subject in a card of its own, pinned at the top, and under it the whole thread as
91
+ * a stack of message cards (each exactly as tall as its message; the list scrolls as a whole), **newest at the top**, opened at the message
90
92
  * the reader picked. Every message from that one through to the newest is expanded - which in this order is
91
93
  * the opened message and the entries above it - and the older ones, below it, are collapsed to a one-line
92
94
  * summary (sender, date, preview) that expands on click or Enter. So opening the newest message shows just
@@ -129,6 +131,9 @@ export default function ConversationThreadPane({ conversation, mailboxUid, selec
129
131
  /** Where the toggled message's header sat in the viewport before it expanded, so the run below can put
130
132
  * it back there - expanding a message above the one being read must not shove that one off-screen. */
131
133
  const anchorRef = useRef(null);
134
+ /** The message whose header button had the focus when it was toggled: expanding or collapsing swaps that button for the other state's, so the
135
+ * focus is put back on the new one. */
136
+ const refocusRef = useRef(null);
132
137
  const conversationId = conversation?.conversationId;
133
138
  useEffect(() => {
134
139
  const generation = ++generationRef.current;
@@ -211,7 +216,8 @@ export default function ConversationThreadPane({ conversation, mailboxUid, selec
211
216
  const active = document.activeElement;
212
217
  if (!(active instanceof HTMLElement && active.hasAttribute("data-row-open") && hasFocusRing(active))) {
213
218
  // `preventScroll` so focusing doesn't scroll it somewhere else again.
214
- headerRefs.current[pendingFocusUid].focus({ preventScroll: true });
219
+ // (A card's header button is drawn by the message pane, which registers it; a stand-in pane that doesn't has none to focus.)
220
+ headerRefs.current[pendingFocusUid]?.focus({ preventScroll: true });
215
221
  }
216
222
  setPendingFocusUid(null);
217
223
  }, [pendingFocusUid, expandedUids]);
@@ -225,6 +231,11 @@ export default function ConversationThreadPane({ conversation, mailboxUid, selec
225
231
  // The row is still there: the anchor was taken from a rendered row, and toggling never removes one.
226
232
  const row = rowRefs.current[anchor.uid];
227
233
  scrollingAncestor(row).scrollTop += row.getBoundingClientRect().top - anchor.top;
234
+ // The button the reader had focused is gone (a collapsed card and an expanded one each draw their own): keep them where they were.
235
+ if (refocusRef.current === anchor.uid) {
236
+ headerRefs.current[anchor.uid]?.focus({ preventScroll: true });
237
+ }
238
+ refocusRef.current = null;
228
239
  }, [expandedUids]);
229
240
  // Attachments and mark-as-read, for expanded messages only - mirrors `mailDetailHooks.ts`'s
230
241
  // `useMessageAttachments`/`useMarkMessageRead`, reimplemented here (rather than called in a loop, which
@@ -288,6 +299,7 @@ export default function ConversationThreadPane({ conversation, mailboxUid, selec
288
299
  markReadRequestedRef.current.delete(uid);
289
300
  // The row this button lives in has rendered, so its ref is set.
290
301
  anchorRef.current = { uid, top: rowRefs.current[uid].getBoundingClientRect().top };
302
+ refocusRef.current = document.activeElement === headerRefs.current[uid] ? uid : null;
291
303
  setExpandedUids((prev) => {
292
304
  const next = new Set(prev);
293
305
  if (next.has(uid)) {
@@ -308,25 +320,35 @@ export default function ConversationThreadPane({ conversation, mailboxUid, selec
308
320
  if (!conversation) {
309
321
  return _jsx("p", { className: "p-8 text-sm text-text-muted", children: "Select a conversation to read it." });
310
322
  }
311
- if (loading) {
312
- return _jsx("p", { className: "p-8 text-sm text-text-muted", children: "Loading\u2026" });
313
- }
314
- if (error) {
315
- return (_jsx("div", { className: "p-4 flex-1", children: _jsx(Alert, { children: error }) }));
316
- }
317
- return (_jsxs("div", { className: "flex-1 min-w-0 min-h-0 flex flex-col", children: [_jsxs("div", { className: "shrink-0 border-b border-border p-4", children: [_jsx("h1", { className: "text-lg font-bold tracking-tight", children: conversation.subject || "(no subject)" }), _jsxs("p", { className: "text-sm text-text-muted mt-1", children: [messages.length, " message", messages.length === 1 ? "" : "s"] }), truncated && (_jsxs("p", { className: "text-xs text-text-muted mt-1", children: ["Only the oldest ", THREAD_MESSAGE_LIMIT, " messages of this conversation are shown here. The rest are still in the message list."] }))] }), _jsx("ul", { className: "flex-1 min-h-0 overflow-y-auto", children: messages.map((message) => {
323
+ const subject = displaySubject(conversation.subject) || "(no subject)";
324
+ // The newest message that is open carries the Reply / Forward buttons at the foot of its card.
325
+ const footerUid = messages.find((message) => expandedUids.has(message.uid))?.uid;
326
+ return (_jsxs("div", { className: "flex-1 min-w-0 min-h-0 flex flex-col", children: [_jsx(SubjectCard, { subject: subject, meta: loading
327
+ ? conversation.messageCount > 1
328
+ ? `${conversation.messageCount} messages`
329
+ : undefined
330
+ : error
331
+ ? undefined
332
+ : `${messages.length} message${messages.length === 1 ? "" : "s"}`, children: truncated && !loading && !error && (_jsxs("p", { className: "text-xs text-text-muted mt-1", children: ["Only the oldest ", THREAD_MESSAGE_LIMIT, " messages of this conversation are shown here. The rest are still in the message list."] })) }), loading ? (_jsx(SkeletonCards, { messageCount: conversation.messageCount })) : error ? (_jsx("div", { className: "p-2 sm:p-3", children: _jsx(Alert, { children: error }) })) : (_jsx("ul", { className: "flex-1 min-h-0 overflow-y-auto p-2 sm:p-3 flex flex-col gap-3", children: messages.map((message) => {
318
333
  const uid = message.uid;
319
334
  const expanded = expandedUids.has(uid);
320
335
  const bodyId = `thread-message-${uid}`;
321
336
  const messageUnread = isUnread(message);
322
- return (_jsxs("li", { ref: (node) => {
337
+ return (_jsx("li", { ref: (node) => {
323
338
  rowRefs.current[uid] = node;
324
- }, "data-unread": messageUnread ? "true" : undefined, className: ["relative border-b border-border", expanded ? "flex flex-col min-h-full" : ""].join(" "), children: [_jsx(UnreadBar, { unread: messageUnread }), _jsx("h2", { className: "shrink-0", children: _jsxs("button", { type: "button", ref: (node) => {
339
+ }, "data-unread": messageUnread ? "true" : undefined, children: expanded ? (_jsx(MessageDetailPane, { inThread: true, threadSubject: subject, threadHeader: {
340
+ bodyId,
341
+ unread: messageUnread,
342
+ onToggle: () => toggleExpanded(uid),
343
+ buttonRef: (node) => {
344
+ headerRefs.current[uid] = node;
345
+ },
346
+ }, footer: uid === footerUid, shortcuts: !!shortcuts && uid === keyboardUid, message: message, attachments: attachmentsByUid[uid] ?? [], isSentItems: folderTypeOf(message) === "sent_items", isOutbox: folderTypeOf(message) === "outbox", draftsFolderUid: folders.find((folder) => folder.type === "drafts")?.uid, folders: folders, onMoved: removeMessage, onFolderCreated: onFolderCreated, onRecalled: patchMessage, onReceiptHandled: patchMessage, onScheduledSendCanceled: removeMessage, onArchived: removeMessage, labels: labels, onLabelsChanged: patchMessage, onLabelCreated: onLabelCreated })) : (_jsxs(_Fragment, { children: [_jsx(CollapsedCard, { from: message.from, date: new Date(message.receivedDate).toLocaleString(), preview: message.bodyPreview || (message.encrypted ? _jsx(EncryptedPreview, {}) : ""), unread: messageUnread, senderClassName: senderClass(messageUnread), dateClassName: dateClass(messageUnread), buttonRef: (node) => {
325
347
  headerRefs.current[uid] = node;
326
- }, onClick: () => toggleExpanded(uid), "aria-expanded": expanded, "aria-controls": bodyId, className: [
327
- "w-full text-left px-4 py-3",
328
- messageUnread ? "bg-primary/[0.07] hover:bg-primary/10" : "hover:bg-surface-alt",
329
- ROW_FOCUS_CLASS,
330
- ].join(" "), children: [_jsx(UnreadLabel, { unread: messageUnread }), _jsxs("span", { className: "flex items-center justify-between gap-2 text-sm", children: [_jsx(MailAddress, { recipient: message.from, className: senderClass(messageUnread) }), _jsx("span", { className: ["text-xs shrink-0", dateClass(messageUnread)].join(" "), children: new Date(message.receivedDate).toLocaleString() })] }), !expanded && (_jsx("span", { className: "block text-xs text-text-muted truncate font-normal", children: message.bodyPreview }))] }) }), _jsx("div", { id: bodyId, hidden: !expanded, className: expanded ? "flex-1 min-h-0 flex" : undefined, children: expanded && (_jsx(MessageDetailPane, { inThread: true, shortcuts: !!shortcuts && uid === keyboardUid, message: message, attachments: attachmentsByUid[uid] ?? [], isSentItems: folderTypeOf(message) === "sent_items", isOutbox: folderTypeOf(message) === "outbox", draftsFolderUid: folders.find((folder) => folder.type === "drafts")?.uid, folders: folders, onMoved: removeMessage, onFolderCreated: onFolderCreated, onRecalled: patchMessage, onReceiptHandled: patchMessage, onScheduledSendCanceled: removeMessage, onArchived: removeMessage, labels: labels, onLabelsChanged: patchMessage, onLabelCreated: onLabelCreated })) })] }, uid));
331
- }) })] }));
348
+ }, buttonProps: {
349
+ onClick: () => toggleExpanded(uid),
350
+ "aria-expanded": false,
351
+ "aria-controls": bodyId,
352
+ } }), _jsx("div", { id: bodyId, hidden: true })] })) }, uid));
353
+ }) }))] }));
332
354
  }
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  import { useEffect, useState } from "react";
7
7
  import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
8
8
  import Button from "@rapidmx/react-shared/components/buttons/Button.js";
9
- import Skeleton from "@rapidmx/react-shared/components/feedback/Skeleton.js";
9
+ import { ReadingPaneSkeleton } from "./reading/MessageCard.js";
10
10
  /**
11
11
  * The reading pane (`MessageDetailPane`, and the thread pane that stacks it) is only rendered once a message is selected, and
12
12
  * it is the heaviest piece of the inbox after the compose window - message security, the body sanitizer, the labels and move
@@ -19,7 +19,7 @@ import Skeleton from "@rapidmx/react-shared/components/feedback/Skeleton.js";
19
19
  * code was already fetched. This reads the loaded component synchronously when there is one, so an already-loaded pane renders
20
20
  * on the click's own frame, and it can retry a failed download.
21
21
  */
22
- function lazyComponent(load) {
22
+ function lazyComponent(load, skeleton) {
23
23
  let loaded;
24
24
  let loading;
25
25
  /** Starts (once) loading the component's code. Rejects if it can't be downloaded - and then a later call tries again. */
@@ -47,20 +47,21 @@ function lazyComponent(load) {
47
47
  const Component = loaded;
48
48
  return _jsx(Component, { ...props });
49
49
  }
50
- return failed ? _jsx(ChunkFailed, {}) : _jsx(PaneSkeleton, {});
50
+ return failed ? _jsx(ChunkFailed, {}) : skeleton(props);
51
51
  }
52
52
  return { Lazy, start };
53
53
  }
54
- function PaneSkeleton() {
55
- return (_jsxs("div", { role: "status", "aria-busy": "true", className: "flex-1 p-6 flex flex-col gap-3", children: [_jsx("span", { className: "sr-only", children: "Loading the message" }), _jsx(Skeleton, { width: "w-2/3", height: "h-6" }), _jsx(Skeleton, { width: "w-1/3" }), _jsx(Skeleton, { height: "h-40" })] }));
56
- }
57
54
  /** A chunk that can't be downloaded (offline, or replaced by a deploy since this page loaded) is the one failure a reading
58
55
  * pane can't recover from by itself: say so, with the fix, rather than leaving a blank pane. */
59
56
  function ChunkFailed() {
60
57
  return (_jsx("div", { className: "flex-1 p-6", children: _jsxs(Alert, { children: [_jsx("p", { className: "mb-2", children: "This part of the page couldn\u2019t be loaded. Check your connection, then reload." }), _jsx(Button, { type: "button", variant: "secondary", className: "!w-auto", onClick: () => window.location.reload(), children: "Reload" })] }) }));
61
58
  }
62
- const messageDetailPane = lazyComponent(() => import("./MessageDetailPane.js"));
63
- const conversationThreadPane = lazyComponent(() => import("./ConversationThreadPane.js"));
59
+ // While the code loads the pane already draws its frame - the header card with the subject the list row knows, and a card of placeholder lines - so
60
+ // the click is answered on its own frame and the messages only fill in.
61
+ const messageDetailPane = lazyComponent(() => import("./MessageDetailPane.js"),
62
+ // `LazyMessageDetailPane` renders the empty state, not this, for no message.
63
+ (props) => _jsx(ReadingPaneSkeleton, { subject: props.message.subject }));
64
+ const conversationThreadPane = lazyComponent(() => import("./ConversationThreadPane.js"), (props) => _jsx(ReadingPaneSkeleton, { subject: props.conversation.subject, messageCount: props.conversation.messageCount }));
64
65
  /** Starts downloading the reading pane's code, for the page to call when it has nothing better to do. Never rejects. */
65
66
  export function prefetchReadingPane() {
66
67
  messageDetailPane.start().catch(() => undefined);
@@ -36,15 +36,14 @@ export interface MailSelectionBarProps {
36
36
  onArchive: () => void;
37
37
  /** Moves the selection into `folderUid`, resolving once the bulk update has settled. It resolves
38
38
  * whether or not that update was rejected: a bulk update is applied element by element, so a failure
39
- * is explained in this bar (along with the reload it triggers) rather than in the prompt, which would
40
- * be claiming the move simply didn't happen. */
39
+ * is explained by a pop-up (see `notifyApiError()`), raised by the page along with the reload it triggers, rather than in the
40
+ * prompt, which would be claiming the move simply didn't happen. */
41
41
  onMoveTo: (folderUid: string) => Promise<void>;
42
42
  onReportJunk: () => void;
43
43
  onDelete: () => void;
44
44
  /** A bulk action is in flight - every action is held until it settles, since the next one would send
45
45
  * `version`s the first has already superseded. */
46
46
  busy: boolean;
47
- error: string | null;
48
47
  /** The keyboard acts on this selection (Delete, Ctrl+Q, Ctrl+U, Insert - registered by the page): Mark read, Mark unread, Flag and
49
48
  * Delete name their shortcut in the tooltip and `aria-keyshortcuts`. */
50
49
  shortcuts?: boolean;
@@ -59,4 +58,4 @@ export interface MailSelectionBarProps {
59
58
  * target folder is the one already being viewed is disabled with a reason rather than hidden, so the set
60
59
  * of actions doesn't shift around between folders.
61
60
  */
62
- export default function MailSelectionBar({ selected, listed, totals, onSelectAll, onClearSelection, onCancel, folders, currentFolderUid, labels, mailboxUid, onLabelCreated, onFolderCreated, onApplyLabels, onSetRead, onSetFlagged, onArchive, onMoveTo, onReportJunk, onDelete, busy, error, shortcuts, }: MailSelectionBarProps): React.JSX.Element;
61
+ export default function MailSelectionBar({ selected, listed, totals, onSelectAll, onClearSelection, onCancel, folders, currentFolderUid, labels, mailboxUid, onLabelCreated, onFolderCreated, onApplyLabels, onSetRead, onSetFlagged, onArchive, onMoveTo, onReportJunk, onDelete, busy, shortcuts, }: MailSelectionBarProps): React.JSX.Element;
@@ -4,7 +4,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  // SPDX-License-Identifier: MPL-2.0
5
5
  ///////////////////////////////////////////////////////////////////////////////
6
6
  import { useState } from "react";
7
- import Alert from "@rapidmx/react-shared/components/feedback/Alert.js";
8
7
  import LabelMenuButton from "./labelMenu.js";
9
8
  import MoveToFolderDialog, { MOVE_TARGET_TYPES } from "./MoveToFolderDialog.js";
10
9
  import { ariaKeyShortcuts, withHint } from "../../keyboard/format.js";
@@ -23,7 +22,7 @@ function actionClassName() {
23
22
  * target folder is the one already being viewed is disabled with a reason rather than hidden, so the set
24
23
  * of actions doesn't shift around between folders.
25
24
  */
26
- export default function MailSelectionBar({ selected, listed, totals, onSelectAll, onClearSelection, onCancel, folders, currentFolderUid, labels, mailboxUid, onLabelCreated, onFolderCreated, onApplyLabels, onSetRead, onSetFlagged, onArchive, onMoveTo, onReportJunk, onDelete, busy, error, shortcuts, }) {
25
+ export default function MailSelectionBar({ selected, listed, totals, onSelectAll, onClearSelection, onCancel, folders, currentFolderUid, labels, mailboxUid, onLabelCreated, onFolderCreated, onApplyLabels, onSetRead, onSetFlagged, onArchive, onMoveTo, onReportJunk, onDelete, busy, shortcuts, }) {
27
26
  const env = useKeyEnvironment();
28
27
  /** `title` and `aria-keyshortcuts` for an action the keyboard also does; `reason` (why it is disabled) wins the tooltip. */
29
28
  const hint = (label, shortcut, reason) => shortcuts
@@ -52,5 +51,5 @@ export default function MailSelectionBar({ selected, listed, totals, onSelectAll
52
51
  const appliedToSome = labels.filter((label) => selected.some((m) => m.labelUids?.includes(label.uid)) && !appliedToAll.includes(label));
53
52
  return (_jsxs("div", { className: "border-b border-border", children: [_jsx(MoveToFolderDialog, { open: movePrompt, onClose: () => setMovePrompt(false), mailboxUid: mailboxUid, folders: folders, currentFolderUid: currentFolderUid, count: selected.length, onMove: onMoveTo, onFolderCreated: onFolderCreated }), _jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-surface-alt", children: [_jsxs("span", { "aria-live": "polite", className: "text-sm font-semibold text-text", children: [selectedCount, totals ? ` ${totals.noun}${selectedCount === 1 ? "" : "s"}` : "", " selected"] }), _jsx("button", { type: "button", onClick: onSelectAll, disabled: allSelected || listedCount === 0, className: actionClassName(), children: "Select all" }), _jsx("button", { type: "button", onClick: onClearSelection, disabled: selectedCount === 0, className: actionClassName(), children: "Clear" }), _jsx("button", { type: "button", onClick: onCancel, className: `${actionClassName()} ml-auto`, children: "Cancel" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-0.5 px-1.5 py-1", children: [_jsx("button", { type: "button", onClick: () => onSetRead(true), disabled: none || busy, className: actionClassName(), ...hint("Mark read", SHORTCUTS.mail.markRead), children: "Mark read" }), _jsx("button", { type: "button", onClick: () => onSetRead(false), disabled: none || busy, className: actionClassName(), ...hint("Mark unread", SHORTCUTS.mail.markUnread), children: "Mark unread" }), _jsx("button", { type: "button", onClick: () => onSetFlagged(true), disabled: none || busy, className: actionClassName(), ...hint("Flag", SHORTCUTS.mail.flag), children: "Flag" }), _jsx("button", { type: "button", onClick: () => onSetFlagged(false), disabled: none || busy, className: actionClassName(), children: "Unflag" }), _jsx("button", { type: "button", onClick: onArchive, disabled: none || busy || !!archiveReason, title: archiveReason, className: actionClassName(), children: "Archive" }), _jsx(LabelMenuButton, { "aria-label": "Apply label", label: "Apply label", className: "text-sm", labels: labels, mailboxUid: mailboxUid, onLabelCreated: onLabelCreated, applied: appliedToAll.map((label) => label.uid), partial: appliedToSome.map((label) => label.uid), onCommit: onApplyLabels, busy: busy, disabled: none || busy, note: selected.length === 1
54
53
  ? "Ticked labels are applied, unticked ones removed."
55
- : "Ticked labels are applied to every selected message and unticked ones removed from all of them; a dash means only some have that label, and leaving it alone keeps it that way.", emptyNote: "This mailbox has no labels yet.", commit: { label: "Apply" }, clear: { label: "Remove all labels" } }), _jsx("button", { type: "button", onClick: () => setMovePrompt(true), disabled: none || busy, className: actionClassName(), children: "Move to" }), _jsx("button", { type: "button", onClick: onReportJunk, disabled: none || busy || !!junkReason, title: junkReason, className: actionClassName(), children: "Report junk" }), _jsx("button", { type: "button", onClick: onDelete, disabled: none || busy || !!deleteReason, className: actionClassName(), ...hint("Delete", SHORTCUTS.mail.delete, deleteReason), children: "Delete" })] }), error && (_jsx("div", { className: "px-3 pb-2", children: _jsx(Alert, { children: error }) }))] }));
54
+ : "Ticked labels are applied to every selected message and unticked ones removed from all of them; a dash means only some have that label, and leaving it alone keeps it that way.", emptyNote: "This mailbox has no labels yet.", commit: { label: "Apply" }, clear: { label: "Remove all labels" } }), _jsx("button", { type: "button", onClick: () => setMovePrompt(true), disabled: none || busy, className: actionClassName(), children: "Move to" }), _jsx("button", { type: "button", onClick: onReportJunk, disabled: none || busy || !!junkReason, title: junkReason, className: actionClassName(), children: "Report junk" }), _jsx("button", { type: "button", onClick: onDelete, disabled: none || busy || !!deleteReason, className: actionClassName(), ...hint("Delete", SHORTCUTS.mail.delete, deleteReason), children: "Delete" })] })] }));
56
55
  }
@@ -24,9 +24,6 @@ export declare function trustSignerErrorMessage(err: unknown): string;
24
24
  /** Groups a fingerprint into 4-character blocks (`AB12 CD34 ...`), ignoring any separators it came with, so it can be
25
25
  * read out and compared with the sender over another channel. */
26
26
  export declare function formatFingerprint(fingerprint: string): string;
27
- /** Sanitizes a client-rendered body for `srcDoc` (DOMPurify plus every remote resource reference stripped - see
28
- * react-shared's `messageBodySanitizer.ts`), with the CSP meta as its very first element. */
29
- export declare function buildSecureSrcDoc(html: string): string;
30
27
  export interface MessageDetailPaneProps {
31
28
  message: Message | null;
32
29
  attachments: Attachment[];
@@ -86,10 +83,23 @@ export interface MessageDetailPaneProps {
86
83
  /** Handed a label created from the Labels menu, for the caller to add to `labels`. Without it the menu
87
84
  * offers no "New label" row, only the link to where labels are managed. */
88
85
  onLabelCreated?: (label: Label) => void;
89
- /** Rendered as one message of a thread (`ConversationThreadPane`) rather than as the pane itself: the
90
- * subject becomes a heading under the thread's own, since a document has one `h1` and the thread's is
91
- * the conversation. Everything else - the badges, the actions, the body - is identical. */
86
+ /** Rendered as one message of a thread (`ConversationThreadPane`) rather than as the pane itself: only its card is drawn - the thread
87
+ * owns the subject card, the scrolling and the `h1` - and the message's own subject appears (as an `h3`) only when it differs from the
88
+ * thread's (`threadSubject`). Everything else - the badges, the actions, the body - is identical. */
92
89
  inThread?: boolean;
90
+ /** The thread's subject, for a message of a thread to tell whether its own subject is worth showing. */
91
+ threadSubject?: string;
92
+ /** In a thread, what makes the sender line of this expanded card the button that collapses it: the id of the body it controls, whether the
93
+ * message is unread (which draws the accent bar and tint), what it does, and where to put its ref. */
94
+ threadHeader?: {
95
+ bodyId: string;
96
+ unread: boolean;
97
+ onToggle: () => void;
98
+ buttonRef: (node: HTMLButtonElement | null) => void;
99
+ };
100
+ /** Draw the Reply and Forward buttons at the foot of the card - by default for the single-message pane, and in a thread only where the
101
+ * caller asks (the newest expanded message). */
102
+ footer?: boolean;
93
103
  /**
94
104
  * Registers this pane's keyboard shortcuts - Reply, Reply all, Forward, Archive, Move to - so they act on this message. Only the message
95
105
  * the keyboard is acting on should set it: the one pane beside the list, or the opened message of a thread (each expanded message of a
@@ -98,7 +108,8 @@ export interface MessageDetailPaneProps {
98
108
  shortcuts?: boolean;
99
109
  }
100
110
  /**
101
- * A message's reading pane — header (subject/from/to/attachments) plus a sandboxed iframe for the body.
111
+ * A message's reading pane - a subject card, and under it the message as a card: header (sender, recipients, time, actions), notices, attachments,
112
+ * the body (`reading/MessageBody`: an isolated frame exactly as tall as its content, or text) and a footer. Rendered with `inThread`, only the card.
102
113
  * Shared by the desktop inline pane (`apps/www/index.tsx`, always visible alongside the message list),
103
114
  * and the mobile detail route (`apps/www/messages/[uid].tsx`, a full page on its own reached by tapping
104
115
  * a message row) — see each call site for how `message`/`attachments`/`isSentItems` are sourced. The