@rapidmx/web-client 0.20.0 → 0.22.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 (906) hide show
  1. package/README.md +415 -381
  2. package/apps/admin/_layout.tsx +2 -2
  3. package/apps/admin/audit-log/index.tsx +4 -4
  4. package/apps/admin/branding/index.tsx +39 -39
  5. package/apps/admin/data-requests/index.tsx +482 -482
  6. package/apps/admin/distribution-lists/[uid].tsx +3 -3
  7. package/apps/admin/distribution-lists/index.tsx +4 -4
  8. package/apps/admin/distribution-lists/new/index.tsx +5 -5
  9. package/apps/admin/domains/[uid].tsx +166 -166
  10. package/apps/admin/domains/index.tsx +4 -4
  11. package/apps/admin/domains/new/index.tsx +5 -5
  12. package/apps/admin/encryption-policy/index.tsx +1 -1
  13. package/apps/admin/escrow-scopes/[uid].tsx +350 -350
  14. package/apps/admin/escrow-scopes/index.tsx +4 -4
  15. package/apps/admin/escrow-scopes/new/index.tsx +5 -5
  16. package/apps/admin/index.tsx +129 -129
  17. package/apps/admin/ingest-queue/index.tsx +4 -4
  18. package/apps/admin/mailbox-policy/index.tsx +1 -1
  19. package/apps/admin/mailboxes/[uid].tsx +271 -271
  20. package/apps/admin/mailboxes/new/index.tsx +30 -30
  21. package/apps/admin/plugins/index.tsx +15 -15
  22. package/apps/admin/quarantine/index.tsx +5 -5
  23. package/apps/admin/retention-policy/index.tsx +39 -39
  24. package/apps/admin/signing-certificates/index.tsx +343 -343
  25. package/apps/admin/transport-rules/[uid].tsx +5 -5
  26. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  27. package/apps/admin/transport-rules/index.tsx +4 -4
  28. package/apps/admin/transport-rules/new/index.tsx +5 -5
  29. package/apps/escrow/_layout.tsx +1 -1
  30. package/apps/escrow/audit-log/index.tsx +196 -196
  31. package/apps/escrow/index.tsx +4 -4
  32. package/apps/escrow/matters/[uid].tsx +621 -621
  33. package/apps/escrow/matters/new/index.tsx +6 -6
  34. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  35. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  36. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  37. package/apps/shared/appearance/appearanceCache.ts +1 -1
  38. package/apps/shared/appearance/appearanceContext.ts +1 -1
  39. package/apps/shared/appearance/theme.ts +1 -1
  40. package/apps/shared/auth/adminAccess.ts +99 -99
  41. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  42. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  43. package/apps/shared/calendar/calendarReminders.ts +1 -1
  44. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  45. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  46. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  47. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  48. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  49. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  51. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  52. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  53. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  54. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  55. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  56. package/apps/shared/components/admin/elevation.ts +1 -1
  57. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  58. package/apps/shared/components/admin/layout/AdminShell.tsx +384 -384
  59. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +238 -238
  60. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +154 -152
  61. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +194 -194
  62. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  63. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  64. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  65. package/apps/shared/components/admin/settings/BrandingForm.tsx +423 -423
  66. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  67. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +119 -119
  68. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  69. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  70. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +198 -198
  71. package/apps/shared/components/admin/settings/PluginsManager.tsx +2093 -2093
  72. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +182 -182
  73. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +288 -288
  74. package/apps/shared/components/admin/setup/SetupWizard.tsx +450 -446
  75. package/apps/shared/components/admin/signOut.ts +1 -1
  76. package/apps/shared/components/admin/usePagedList.tsx +129 -129
  77. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  78. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  79. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  80. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  81. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  82. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  83. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  84. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  85. package/apps/shared/components/calendar/EventModal.tsx +206 -206
  86. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  87. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  88. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  89. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  90. package/apps/shared/components/calendar/MonthView.tsx +185 -185
  91. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  92. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  93. package/apps/shared/components/calendar/RecurrenceEditor.tsx +227 -227
  94. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  95. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  96. package/apps/shared/components/calendar/SplitDayView.tsx +144 -144
  97. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  98. package/apps/shared/components/calendar/TimeGridView.tsx +246 -246
  99. package/apps/shared/components/calendar/allDay.ts +124 -124
  100. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  101. package/apps/shared/components/calendar/eventForm.ts +3 -3
  102. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  103. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  104. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  105. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  106. package/apps/shared/components/contacts/ContactForm.tsx +393 -383
  107. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  108. package/apps/shared/components/contacts/ContactsToolbar.tsx +103 -103
  109. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  110. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  111. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  112. package/apps/shared/components/escrow/layout/EscrowShell.tsx +155 -155
  113. package/apps/shared/components/forms/StringListField.tsx +1 -1
  114. package/apps/shared/components/layout/AppShell.tsx +465 -461
  115. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  116. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +398 -398
  117. package/apps/shared/components/layout/MailboxProvisioning.tsx +170 -168
  118. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  119. package/apps/shared/components/layout/ResponsiveToolbar.tsx +315 -315
  120. package/apps/shared/components/layout/ThemeSwitch.tsx +84 -84
  121. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  122. package/apps/shared/components/layout/UserMenu.tsx +441 -439
  123. package/apps/shared/components/mail/ConversationList.tsx +334 -332
  124. package/apps/shared/components/mail/ConversationThreadPane.tsx +624 -613
  125. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  126. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  127. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  128. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  129. package/apps/shared/components/mail/MailSelectionBar.tsx +240 -240
  130. package/apps/shared/components/mail/MenuButton.tsx +404 -404
  131. package/apps/shared/components/mail/MessageDetailPane.tsx +1759 -1757
  132. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  133. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  134. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  135. package/apps/shared/components/mail/compose/ComposeContext.tsx +323 -312
  136. package/apps/shared/components/mail/compose/ComposeToolbar.tsx +422 -422
  137. package/apps/shared/components/mail/compose/ComposeWindow.tsx +1709 -1681
  138. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  139. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  140. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  141. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  142. package/apps/shared/components/mail/compose/RichTextEditor.tsx +147 -147
  143. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  144. package/apps/shared/components/mail/compose/composeFlushRegistry.ts +60 -60
  145. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  146. package/apps/shared/components/mail/compose/quotedBody.ts +161 -161
  147. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  148. package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
  149. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
  150. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  151. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  152. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  153. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  154. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  155. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  156. package/apps/shared/components/mail/listPreferences.ts +2 -2
  157. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  158. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  159. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  160. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  161. package/apps/shared/components/mail/reading/MessageMoreMenu.tsx +258 -258
  162. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +79 -79
  163. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  164. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  165. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  166. package/apps/shared/components/mail/reading/messageExport.ts +59 -59
  167. package/apps/shared/components/mail/reading/printMessage.ts +99 -99
  168. package/apps/shared/components/mail/reading/useMessageActions.ts +450 -443
  169. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  170. package/apps/shared/components/mail/verificationSeals.ts +129 -125
  171. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  172. package/apps/shared/components/rules/RuleBuilder.tsx +311 -311
  173. package/apps/shared/components/settings/SigningCertificateCard.tsx +344 -344
  174. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  175. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  176. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  177. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  178. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  179. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  180. package/apps/shared/keyboard/GlobalShortcuts.tsx +51 -51
  181. package/apps/shared/keyboard/ShortcutProvider.tsx +62 -62
  182. package/apps/shared/keyboard/ShortcutsDialog.tsx +84 -84
  183. package/apps/shared/keyboard/dispatch.ts +124 -124
  184. package/apps/shared/keyboard/format.ts +89 -89
  185. package/apps/shared/keyboard/keymap.ts +114 -114
  186. package/apps/shared/keyboard/registry.ts +65 -65
  187. package/apps/shared/keyboard/targets.ts +79 -79
  188. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  189. package/apps/shared/mail/folderCounts.ts +8 -4
  190. package/apps/shared/mail/folderOfType.ts +51 -49
  191. package/apps/shared/mail/folderTree.ts +143 -143
  192. package/apps/shared/mail/listAllPages.ts +39 -39
  193. package/apps/shared/mail/listSnapshots.ts +2 -2
  194. package/apps/shared/mail/messageReadState.ts +6 -5
  195. package/apps/shared/mail/newMailNotifications.ts +171 -171
  196. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  197. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  198. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  199. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  200. package/apps/shared/mail/outbox/sendJob.ts +450 -445
  201. package/apps/shared/mail/outbox/sendOutcomes.ts +160 -155
  202. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  203. package/apps/shared/mail/permanentDelete.ts +8 -7
  204. package/apps/shared/mail/primaryMailbox.ts +1 -1
  205. package/apps/shared/mail/reportNotices.ts +66 -66
  206. package/apps/shared/mail/senderBlocking.ts +157 -141
  207. package/apps/shared/mail/useMailConnection.ts +207 -205
  208. package/apps/shared/mail/useMailLiveUpdates.ts +277 -277
  209. package/apps/shared/mail/useMailboxUpdateAccess.ts +84 -60
  210. package/apps/shared/mail/useMarkMessageRead.ts +54 -47
  211. package/apps/shared/mail/useNewMailNotifications.ts +178 -178
  212. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  213. package/apps/shared/navigation/pageTitle.ts +1 -1
  214. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  215. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  216. package/apps/shared/notifications/apiErrors.ts +2 -2
  217. package/apps/shared/notifications/pushStatus.ts +1 -1
  218. package/apps/shared/notifications/store.ts +560 -560
  219. package/apps/shared/notifications/systemErrors.ts +1 -1
  220. package/apps/shared/search/LocalIndexLifecycle.tsx +120 -114
  221. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  222. package/apps/shared/search/localIndexBuilder.ts +493 -481
  223. package/apps/shared/search/localIndexKey.ts +1 -1
  224. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  225. package/apps/shared/search/localIndexTransport.ts +84 -0
  226. package/apps/shared/search/localIndexWorker.ts +1 -1
  227. package/apps/shared/search/searchTier2.ts +3 -3
  228. package/apps/shared/signing/enrollmentStorage.ts +33 -33
  229. package/apps/shared/signing/enrollmentTracker.ts +385 -385
  230. package/apps/shared/signing/enrollmentView.ts +251 -251
  231. package/apps/shared/signing/signingInfo.ts +1 -1
  232. package/apps/shared/signing/useNow.ts +19 -19
  233. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +92 -90
  234. package/apps/shared/styles/app.css +387 -396
  235. package/apps/www/_layout.tsx +2 -2
  236. package/apps/www/calendar/index.tsx +583 -581
  237. package/apps/www/contacts/[uid].tsx +114 -112
  238. package/apps/www/contacts/index.tsx +19 -17
  239. package/apps/www/index.tsx +3021 -3012
  240. package/apps/www/messages/[uid].tsx +141 -139
  241. package/apps/www/settings/auto-reply/index.tsx +138 -136
  242. package/apps/www/settings/blocked-senders/index.tsx +305 -303
  243. package/apps/www/settings/encryption/index.tsx +1296 -1290
  244. package/apps/www/settings/filters/[uid].tsx +184 -179
  245. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  246. package/apps/www/settings/filters/index.tsx +105 -105
  247. package/apps/www/settings/filters/new/index.tsx +167 -165
  248. package/apps/www/settings/labels/index.tsx +209 -207
  249. package/apps/www/settings/privacy/index.tsx +499 -495
  250. package/apps/www/settings/profile/index.tsx +256 -251
  251. package/apps/www/settings/read-receipts/index.tsx +155 -150
  252. package/apps/www/settings/sharing/index.tsx +259 -259
  253. package/apps/www/settings/signatures/[uid].tsx +175 -175
  254. package/apps/www/settings/signatures/index.tsx +91 -91
  255. package/apps/www/settings/signatures/new/index.tsx +138 -138
  256. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  257. package/apps/www/tasks/index.tsx +659 -654
  258. package/dist/apps/admin/_layout.d.ts +1 -1
  259. package/dist/apps/admin/_layout.js +1 -1
  260. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  261. package/dist/apps/admin/audit-log/index.js +4 -4
  262. package/dist/apps/admin/branding/index.js +3 -3
  263. package/dist/apps/admin/data-requests/index.js +8 -8
  264. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  265. package/dist/apps/admin/distribution-lists/index.js +4 -4
  266. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  267. package/dist/apps/admin/domains/[uid].js +6 -6
  268. package/dist/apps/admin/domains/index.js +4 -4
  269. package/dist/apps/admin/domains/new/index.js +5 -5
  270. package/dist/apps/admin/encryption-policy/index.js +1 -1
  271. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  272. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  273. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  274. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  275. package/dist/apps/admin/index.js +6 -6
  276. package/dist/apps/admin/ingest-queue/index.js +4 -4
  277. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  278. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  279. package/dist/apps/admin/quarantine/index.js +5 -5
  280. package/dist/apps/admin/retention-policy/index.js +3 -3
  281. package/dist/apps/admin/signing-certificates/index.js +5 -5
  282. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  283. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  284. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  285. package/dist/apps/admin/transport-rules/index.js +4 -4
  286. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  287. package/dist/apps/escrow/_layout.d.ts +1 -1
  288. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  289. package/dist/apps/escrow/audit-log/index.js +4 -4
  290. package/dist/apps/escrow/index.js +4 -4
  291. package/dist/apps/escrow/matters/[uid].js +10 -10
  292. package/dist/apps/escrow/matters/new/index.js +6 -6
  293. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  294. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  295. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  296. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  297. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  298. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  299. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  300. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  301. package/dist/apps/shared/auth/adminAccess.js +1 -1
  302. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  303. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  304. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  305. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  306. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  307. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  309. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  310. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  311. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  312. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  313. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  314. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  315. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  316. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  317. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  318. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  319. package/dist/apps/shared/components/admin/elevation.js +1 -1
  320. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  321. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  322. package/dist/apps/shared/components/admin/layout/AdminShell.js +8 -8
  323. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  325. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  327. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  328. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  330. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  331. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  332. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  333. package/dist/apps/shared/components/admin/settings/BrandingForm.js +8 -8
  334. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  335. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  336. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  337. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  338. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  339. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  340. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  341. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  342. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  343. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  344. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  345. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  346. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  347. package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
  348. package/dist/apps/shared/components/admin/signOut.js +1 -1
  349. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  350. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  351. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  352. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  353. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  354. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  355. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  356. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  357. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  358. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  359. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  360. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  362. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  363. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  364. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  365. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  366. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  367. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  369. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  370. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  371. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  372. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  374. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  375. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  376. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  377. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  378. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  379. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  380. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  381. package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
  382. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  383. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  384. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  385. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  386. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  387. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  388. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  389. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  390. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  391. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  392. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  393. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  394. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  395. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  396. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  397. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  398. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  399. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  400. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  401. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  402. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  403. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  404. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  405. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +7 -7
  406. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  407. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  408. package/dist/apps/shared/components/layout/AppShell.js +14 -10
  409. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  410. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  411. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  412. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  413. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  414. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  415. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  416. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  417. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  418. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  419. package/dist/apps/shared/components/mail/ConversationList.js +6 -4
  420. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  421. package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
  422. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  423. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  424. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  425. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  426. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  427. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  428. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  429. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  430. package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
  431. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  432. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  433. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  435. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  436. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  437. package/dist/apps/shared/components/mail/compose/ComposeWindow.d.ts +7 -1
  438. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +54 -31
  439. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  440. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  441. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  442. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  443. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  444. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  445. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  446. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  447. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  448. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  449. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  450. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
  451. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
  452. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
  453. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  454. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  455. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  456. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  457. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  458. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  459. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  460. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  461. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  462. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  463. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  464. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  465. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  466. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  467. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  468. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  469. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  470. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  471. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  472. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  473. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  474. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  475. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  476. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  477. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  478. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
  479. package/dist/apps/shared/components/mail/reading/printMessage.js +11 -11
  480. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  481. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  482. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  483. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  484. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  485. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  486. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  487. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  488. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  489. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  490. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  491. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  492. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  493. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  494. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  495. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  496. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  497. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  498. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  499. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  500. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  501. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  502. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  503. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  504. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  505. package/dist/apps/shared/mail/folderCounts.js +6 -4
  506. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  507. package/dist/apps/shared/mail/folderOfType.js +4 -4
  508. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  509. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  510. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  511. package/dist/apps/shared/mail/messageReadState.js +5 -5
  512. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  513. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  514. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  515. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  516. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  517. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  518. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  519. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  520. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  521. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  522. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  523. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  524. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  525. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  526. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  527. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  528. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  529. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  530. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  531. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  532. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  533. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  534. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  535. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  536. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  537. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  538. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  539. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  540. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  541. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  542. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  543. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  544. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  545. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  546. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  547. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  548. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  549. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  550. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  551. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  552. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  553. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  554. package/dist/apps/shared/search/localIndexKey.js +1 -1
  555. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  556. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  557. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  558. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  559. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  560. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  561. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  562. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  563. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  564. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  565. package/dist/apps/shared/signing/signingInfo.js +1 -1
  566. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  567. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  568. package/dist/apps/shared/styles/app.css +387 -396
  569. package/dist/apps/www/_layout.d.ts +1 -1
  570. package/dist/apps/www/_layout.js +1 -1
  571. package/dist/apps/www/calendar/index.d.ts +1 -1
  572. package/dist/apps/www/calendar/index.js +20 -18
  573. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  574. package/dist/apps/www/contacts/[uid].js +8 -6
  575. package/dist/apps/www/contacts/index.d.ts +1 -1
  576. package/dist/apps/www/contacts/index.js +19 -17
  577. package/dist/apps/www/index.d.ts +1 -1
  578. package/dist/apps/www/index.js +54 -50
  579. package/dist/apps/www/messages/[uid].d.ts +1 -1
  580. package/dist/apps/www/messages/[uid].js +11 -9
  581. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  582. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  583. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  584. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  585. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  586. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  587. package/dist/apps/www/settings/encryption/index.js +31 -29
  588. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  589. package/dist/apps/www/settings/filters/[uid].js +11 -9
  590. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  591. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  592. package/dist/apps/www/settings/filters/index.js +4 -4
  593. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  594. package/dist/apps/www/settings/filters/new/index.js +10 -8
  595. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  596. package/dist/apps/www/settings/labels/index.js +12 -10
  597. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  598. package/dist/apps/www/settings/privacy/index.js +18 -14
  599. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  600. package/dist/apps/www/settings/profile/index.js +7 -5
  601. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  602. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  603. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  604. package/dist/apps/www/settings/sharing/index.js +6 -6
  605. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  606. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  607. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  608. package/dist/apps/www/settings/signatures/index.js +4 -4
  609. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  610. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  611. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  612. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  613. package/dist/apps/www/tasks/index.d.ts +1 -1
  614. package/dist/apps/www/tasks/index.js +17 -15
  615. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  616. package/dist/lib/admin/auditLogApi.js +27 -0
  617. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  618. package/dist/lib/admin/distributionListsApi.js +34 -0
  619. package/dist/lib/admin/domainsApi.d.ts +75 -0
  620. package/dist/lib/admin/domainsApi.js +44 -0
  621. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  622. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  623. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  624. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  625. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  626. package/dist/lib/admin/escrowScopesApi.js +50 -0
  627. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  628. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  629. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  630. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  631. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  632. package/dist/lib/admin/matterExportApi.js +39 -0
  633. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  634. package/dist/lib/admin/matterSearchApi.js +27 -0
  635. package/dist/lib/admin/mattersApi.d.ts +56 -0
  636. package/dist/lib/admin/mattersApi.js +40 -0
  637. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  638. package/dist/lib/admin/pluginsApi.js +102 -0
  639. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  640. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  641. package/dist/lib/admin/setupApi.d.ts +23 -0
  642. package/dist/lib/admin/setupApi.js +26 -0
  643. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  644. package/dist/lib/admin/transportRulesApi.js +43 -0
  645. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  646. package/dist/lib/appearance/preferencesApi.js +257 -0
  647. package/dist/lib/auth/profileApi.d.ts +33 -0
  648. package/dist/lib/auth/profileApi.js +52 -0
  649. package/dist/lib/auth/session.d.ts +70 -0
  650. package/dist/lib/auth/session.js +210 -0
  651. package/dist/lib/branding/brandingApi.d.ts +47 -0
  652. package/dist/lib/branding/brandingApi.js +61 -0
  653. package/dist/lib/branding/useBranding.d.ts +23 -0
  654. package/dist/lib/branding/useBranding.js +65 -0
  655. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  656. package/dist/lib/calendar/calendarApi.js +106 -0
  657. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  658. package/dist/lib/calendar/calendarColors.js +39 -0
  659. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  660. package/dist/lib/calendar/calendarDragIds.js +92 -0
  661. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  662. package/dist/lib/calendar/calendarMutations.js +190 -0
  663. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  664. package/dist/lib/calendar/eventDescription.js +195 -0
  665. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  666. package/dist/lib/calendar/freeBusyApi.js +119 -0
  667. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  668. package/dist/lib/calendar/inviteApi.js +54 -0
  669. package/dist/lib/calendar/recurrence.d.ts +60 -0
  670. package/dist/lib/calendar/recurrence.js +225 -0
  671. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  672. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  673. package/dist/lib/components/buttons/Button.d.ts +9 -0
  674. package/dist/lib/components/buttons/Button.js +14 -0
  675. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  676. package/dist/lib/components/buttons/CopyButton.js +14 -0
  677. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  678. package/dist/lib/components/feedback/Alert.js +4 -0
  679. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  680. package/dist/lib/components/feedback/Skeleton.js +14 -0
  681. package/dist/lib/components/forms/FormField.d.ts +8 -0
  682. package/dist/lib/components/forms/FormField.js +5 -0
  683. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  684. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  685. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  686. package/dist/lib/components/overlays/Drawer.js +27 -0
  687. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  688. package/dist/lib/components/overlays/Modal.js +25 -0
  689. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  690. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  691. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  692. package/dist/lib/components/overlays/overlayStack.js +143 -0
  693. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  694. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  695. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  696. package/dist/lib/contacts/contactsApi.js +139 -0
  697. package/dist/lib/contacts/vcard.d.ts +23 -0
  698. package/dist/lib/contacts/vcard.js +179 -0
  699. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  700. package/dist/lib/crypto/composeSecurity.js +93 -0
  701. package/dist/lib/crypto/encoding.d.ts +9 -0
  702. package/dist/lib/crypto/encoding.js +32 -0
  703. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  704. package/dist/lib/crypto/escrowKeys.js +59 -0
  705. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  706. package/dist/lib/crypto/idleTimeout.js +47 -0
  707. package/dist/lib/crypto/keySession.d.ts +159 -0
  708. package/dist/lib/crypto/keySession.js +367 -0
  709. package/dist/lib/crypto/keys.d.ts +50 -0
  710. package/dist/lib/crypto/keys.js +89 -0
  711. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  712. package/dist/lib/crypto/keyvaultApi.js +350 -0
  713. package/dist/lib/crypto/masterKey.d.ts +59 -0
  714. package/dist/lib/crypto/masterKey.js +104 -0
  715. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  716. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  717. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  718. package/dist/lib/crypto/messageSecurity.js +367 -0
  719. package/dist/lib/crypto/mime.d.ts +143 -0
  720. package/dist/lib/crypto/mime.js +504 -0
  721. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  722. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  723. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  724. package/dist/lib/crypto/passwordUnlock.js +73 -0
  725. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  726. package/dist/lib/crypto/recoveryCode.js +86 -0
  727. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  728. package/dist/lib/crypto/signingProviderApi.js +60 -0
  729. package/dist/lib/crypto/smime.d.ts +128 -0
  730. package/dist/lib/crypto/smime.js +402 -0
  731. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  732. package/dist/lib/crypto/smimeMessage.js +341 -0
  733. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  734. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  735. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  736. package/dist/lib/crypto/verificationSeal.js +220 -0
  737. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  738. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  739. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  740. package/dist/lib/mail/compose/emojiData.js +18 -0
  741. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  742. package/dist/lib/mail/compose/giphyApi.js +20 -0
  743. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  744. package/dist/lib/mail/conversationsApi.js +44 -0
  745. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  746. package/dist/lib/mail/dataExportApi.js +40 -0
  747. package/dist/lib/mail/directoryApi.d.ts +49 -0
  748. package/dist/lib/mail/directoryApi.js +101 -0
  749. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  750. package/dist/lib/mail/erasureRequestApi.js +43 -0
  751. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  752. package/dist/lib/mail/flaggedMessages.js +73 -0
  753. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  754. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  755. package/dist/lib/mail/labelsApi.d.ts +47 -0
  756. package/dist/lib/mail/labelsApi.js +38 -0
  757. package/dist/lib/mail/mailAddress.d.ts +27 -0
  758. package/dist/lib/mail/mailAddress.js +54 -0
  759. package/dist/lib/mail/mailApi.d.ts +850 -0
  760. package/dist/lib/mail/mailApi.js +602 -0
  761. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  762. package/dist/lib/mail/mailDetailHooks.js +53 -0
  763. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  764. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  765. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  766. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  767. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  768. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  769. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  770. package/dist/lib/mail/mailboxImportApi.js +51 -0
  771. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  772. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  773. package/dist/lib/mail/pushClient.d.ts +106 -0
  774. package/dist/lib/mail/pushClient.js +317 -0
  775. package/dist/lib/mail/sendEvents.d.ts +33 -0
  776. package/dist/lib/mail/sendEvents.js +44 -0
  777. package/dist/lib/mail/sendFailure.d.ts +16 -0
  778. package/dist/lib/mail/sendFailure.js +86 -0
  779. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  780. package/dist/lib/mail/senderListsApi.js +126 -0
  781. package/dist/lib/search/queryGrammar.d.ts +51 -0
  782. package/dist/lib/search/queryGrammar.js +111 -0
  783. package/dist/lib/search/searchApi.d.ts +100 -0
  784. package/dist/lib/search/searchApi.js +107 -0
  785. package/dist/lib/search/searchScoring.d.ts +34 -0
  786. package/dist/lib/search/searchScoring.js +44 -0
  787. package/dist/lib/search/searchTier3.d.ts +64 -0
  788. package/dist/lib/search/searchTier3.js +335 -0
  789. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  790. package/dist/lib/tasks/tasksApi.js +55 -0
  791. package/dist/lib/util/api.d.ts +160 -0
  792. package/dist/lib/util/api.js +251 -0
  793. package/dist/lib/util/apiClientContext.d.ts +7 -0
  794. package/dist/lib/util/apiClientContext.js +32 -0
  795. package/dist/lib/util/apiQuery.d.ts +32 -0
  796. package/dist/lib/util/apiQuery.js +42 -0
  797. package/dist/lib/util/clipboard.d.ts +7 -0
  798. package/dist/lib/util/clipboard.js +56 -0
  799. package/dist/lib/util/dateInput.d.ts +6 -0
  800. package/dist/lib/util/dateInput.js +14 -0
  801. package/dist/lib/util/timeZone.d.ts +12 -0
  802. package/dist/lib/util/timeZone.js +32 -0
  803. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  804. package/dist/lib/util/useCopyToClipboard.js +34 -0
  805. package/dist/lib/util/useIsMobile.d.ts +10 -0
  806. package/dist/lib/util/useIsMobile.js +29 -0
  807. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  808. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  809. package/lib/admin/auditLogApi.ts +48 -0
  810. package/lib/admin/distributionListsApi.ts +77 -0
  811. package/lib/admin/domainsApi.ts +113 -0
  812. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  813. package/lib/admin/escrowAuditLogApi.ts +101 -0
  814. package/lib/admin/escrowScopesApi.ts +112 -0
  815. package/lib/admin/leftoverMailboxApi.ts +120 -0
  816. package/lib/admin/mailboxPolicyApi.ts +38 -0
  817. package/lib/admin/matterExportApi.ts +62 -0
  818. package/lib/admin/matterSearchApi.ts +35 -0
  819. package/lib/admin/mattersApi.ts +86 -0
  820. package/lib/admin/pluginsApi.ts +339 -0
  821. package/lib/admin/retentionPolicyApi.ts +46 -0
  822. package/lib/admin/setupApi.ts +40 -0
  823. package/lib/admin/transportRulesApi.ts +106 -0
  824. package/lib/appearance/preferencesApi.ts +335 -0
  825. package/lib/auth/profileApi.ts +77 -0
  826. package/lib/auth/session.ts +238 -0
  827. package/lib/branding/brandingApi.ts +97 -0
  828. package/lib/branding/useBranding.ts +81 -0
  829. package/lib/calendar/calendarApi.ts +309 -0
  830. package/lib/calendar/calendarColors.ts +45 -0
  831. package/lib/calendar/calendarDragIds.ts +112 -0
  832. package/lib/calendar/calendarMutations.ts +236 -0
  833. package/lib/calendar/eventDescription.ts +211 -0
  834. package/lib/calendar/freeBusyApi.ts +197 -0
  835. package/lib/calendar/inviteApi.ts +154 -0
  836. package/lib/calendar/recurrence.ts +285 -0
  837. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  838. package/lib/components/buttons/Button.tsx +40 -0
  839. package/lib/components/buttons/CopyButton.tsx +45 -0
  840. package/lib/components/feedback/Alert.tsx +13 -0
  841. package/lib/components/feedback/Skeleton.tsx +38 -0
  842. package/lib/components/forms/FormField.tsx +23 -0
  843. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  844. package/lib/components/overlays/Drawer.tsx +72 -0
  845. package/lib/components/overlays/Modal.tsx +64 -0
  846. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  847. package/lib/components/overlays/overlayStack.ts +165 -0
  848. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  849. package/lib/contacts/contactsApi.ts +289 -0
  850. package/lib/contacts/vcard.ts +197 -0
  851. package/lib/crypto/composeSecurity.ts +140 -0
  852. package/lib/crypto/encoding.ts +36 -0
  853. package/lib/crypto/escrowKeys.ts +74 -0
  854. package/lib/crypto/idleTimeout.ts +49 -0
  855. package/lib/crypto/keySession.ts +483 -0
  856. package/lib/crypto/keys.ts +105 -0
  857. package/lib/crypto/keyvaultApi.ts +674 -0
  858. package/lib/crypto/masterKey.ts +134 -0
  859. package/lib/crypto/masterKeyWraps.ts +269 -0
  860. package/lib/crypto/messageSecurity.ts +600 -0
  861. package/lib/crypto/mime.ts +568 -0
  862. package/lib/crypto/passkeyUnlock.ts +103 -0
  863. package/lib/crypto/passwordUnlock.ts +98 -0
  864. package/lib/crypto/recoveryCode.ts +93 -0
  865. package/lib/crypto/signingProviderApi.ts +135 -0
  866. package/lib/crypto/smime.ts +459 -0
  867. package/lib/crypto/smimeMessage.ts +491 -0
  868. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  869. package/lib/crypto/verificationSeal.ts +288 -0
  870. package/lib/mail/compose/composeQuoting.ts +243 -0
  871. package/lib/mail/compose/emojiData.ts +34 -0
  872. package/lib/mail/compose/giphyApi.ts +31 -0
  873. package/lib/mail/conversationsApi.ts +123 -0
  874. package/lib/mail/dataExportApi.ts +73 -0
  875. package/lib/mail/directoryApi.ts +161 -0
  876. package/lib/mail/erasureRequestApi.ts +71 -0
  877. package/lib/mail/flaggedMessages.ts +80 -0
  878. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  879. package/lib/mail/labelsApi.ts +71 -0
  880. package/lib/mail/mailAddress.ts +76 -0
  881. package/lib/mail/mailApi.ts +1286 -0
  882. package/lib/mail/mailDetailHooks.ts +57 -0
  883. package/lib/mail/mailFilterRulesApi.ts +116 -0
  884. package/lib/mail/mailSignaturesApi.ts +84 -0
  885. package/lib/mail/mailboxAccessApi.ts +106 -0
  886. package/lib/mail/mailboxImportApi.ts +87 -0
  887. package/lib/mail/messageBodySanitizer.ts +129 -0
  888. package/lib/mail/pushClient.ts +375 -0
  889. package/lib/mail/sendEvents.ts +72 -0
  890. package/lib/mail/sendFailure.ts +98 -0
  891. package/lib/mail/senderListsApi.ts +161 -0
  892. package/lib/search/queryGrammar.ts +179 -0
  893. package/lib/search/searchApi.ts +199 -0
  894. package/lib/search/searchScoring.ts +46 -0
  895. package/lib/search/searchTier3.ts +406 -0
  896. package/lib/tasks/tasksApi.ts +128 -0
  897. package/lib/util/api.ts +318 -0
  898. package/lib/util/apiClientContext.ts +35 -0
  899. package/lib/util/apiQuery.ts +61 -0
  900. package/lib/util/clipboard.ts +55 -0
  901. package/lib/util/dateInput.ts +15 -0
  902. package/lib/util/timeZone.ts +33 -0
  903. package/lib/util/useCopyToClipboard.ts +48 -0
  904. package/lib/util/useIsMobile.ts +34 -0
  905. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  906. package/package.json +10 -4
@@ -1,461 +1,465 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- import "../../styles/app.css";
6
- import React, { PropsWithChildren, useEffect, useLayoutEffect, useRef, useState } from "react";
7
- import type { IconType } from "react-icons";
8
- import {
9
- HiOutlineCalendarDays,
10
- HiOutlineClipboardDocumentList,
11
- HiOutlineEnvelope,
12
- HiOutlinePuzzlePiece,
13
- HiOutlineUsers,
14
- } from "react-icons/hi2";
15
- import { useRouter } from "@rapidrest/react/client";
16
- import { useRedirectIfUnauthenticated } from "@rapidmx/react-shared/auth/session.js";
17
- import { getSetupStatus } from "@rapidmx/react-shared/admin/setupApi.js";
18
- import { stopImpersonating } from "@rapidmx/react-shared/mail/mailApi.js";
19
- import useBranding from "@rapidmx/react-shared/branding/useBranding.js";
20
- import { useIdleKeyTimeout } from "@rapidmx/react-shared/crypto/useIdleKeyTimeout.js";
21
- import ComposeProvider from "../mail/compose/ComposeContext.js";
22
- import { flushComposeDrafts, markSigningOut } from "../mail/compose/composeFlushRegistry.js";
23
- import BottomTabBar, { NavItem } from "@rapidmx/react-shared/components/navigation/BottomTabBar.js";
24
- import { FrameBrandingFooter, FrameBrandingHeader, useBrandingHtml } from "./BrandingChrome.js";
25
- import RailIcon from "./RailIcon.js";
26
- import AppearanceProvider from "../../appearance/AppearanceProvider.js";
27
- import { clearAppearanceCache } from "../../appearance/appearanceCache.js";
28
- import type { Branding } from "@rapidmx/react-shared/branding/brandingApi.js";
29
- import UserMenu from "./UserMenu.js";
30
- import { UnlockPromptProvider } from "./UnlockPromptProvider.js";
31
- import { SIGN_OUT_CHANNEL, destroyAllLocalIndexes } from "../../search/localIndexRpcClient.js";
32
- import { authApiFetch, setApiUnauthorizedObserver } from "@rapidmx/react-shared/util/api.js";
33
- import { destroyUnlockedKeys } from "@rapidmx/react-shared/crypto/keySession.js";
34
- import { clearPinnedSignerCache } from "../mail/pinnedSigners.js";
35
- import { mergePluginNavItems, PluginNav, PluginNavProps } from "../../plugins/pluginNav.js";
36
- import { useInAppFrame } from "../../navigation/frameContext.js";
37
- import { APP_HREFS } from "../../navigation/appHrefs.js";
38
- import { useNavigate } from "../../navigation/index.js";
39
- import { GlobalShortcuts } from "../../keyboard/GlobalShortcuts.js";
40
- import { ShortcutProvider } from "../../keyboard/ShortcutProvider.js";
41
- import ShortcutsDialog from "../../keyboard/ShortcutsDialog.js";
42
- import { inboxUnreadTotal } from "../../mail/folderCounts.js";
43
- import { MailConnectionContext, useMailConnection } from "../../mail/useMailConnection.js";
44
- import { useUnreadTitle } from "../../mail/useUnreadTitle.js";
45
- import UnlockBridge from "../../mail/outbox/UnlockBridge.js";
46
- import NotificationCenter from "../../notifications/NotificationCenter.js";
47
- import { useHeaderHeightRef } from "../../notifications/headerOffset.js";
48
- import NotificationHistoryDialog from "../../notifications/NotificationHistoryDialog.js";
49
- import { notifySessionExpired, setSignInUrl } from "../../notifications/apiErrors.js";
50
- import { useUnseenErrorCount } from "../../notifications/useNotifications.js";
51
- import { useSigningEnrollmentWatcher } from "../../signing/useSigningEnrollmentWatcher.js";
52
- import { useCalendarReminders } from "../../calendar/useCalendarReminders.js";
53
-
54
- /** How long sign-out waits for auth-server's logout before navigating anyway. */
55
- export const LOGOUT_TIMEOUT_MS = 3_000;
56
-
57
- /**
58
- * Calls auth-server's logout (`POST /api/auth/logout`, cross-origin with credentials), which clears the auth
59
- * cookie and invalidates the session's refresh token. Bounded by `LOGOUT_TIMEOUT_MS` and never rejects - a
60
- * failure (unreachable server, CORS) must not keep the user from leaving.
61
- */
62
- async function logOutOfAuthServer(authServerUrl: string | undefined): Promise<void> {
63
- if (!authServerUrl) {
64
- return;
65
- }
66
- const controller = new AbortController();
67
- const timer = setTimeout(() => controller.abort(), LOGOUT_TIMEOUT_MS);
68
- try {
69
- await authApiFetch(authServerUrl, "/auth/logout", { method: "POST", signal: controller.signal });
70
- } catch {
71
- // Navigate anyway - see this function's doc comment.
72
- } finally {
73
- clearTimeout(timer);
74
- }
75
- }
76
-
77
- export type AppShellApp = "mail" | "calendar" | "contacts" | "tasks";
78
-
79
- /** `"settings"` is a valid `active` value but deliberately has no entry in `APPS` below — Settings is
80
- * reached via a `UserMenu` item, not a 5th rail icon (see `SettingsShell.tsx`), so it highlights no
81
- * rail/tab icon at all; only the header title needs to account for it. Any other string is a plugin's
82
- * `appRail` item id (see `PluginNav`). */
83
- export type AppShellActive = AppShellApp | "settings" | (string & {});
84
-
85
- export interface AppShellProps extends PluginNavProps {
86
- /** Which icon in the rail is highlighted as the current app — `"settings"` highlights none, and a plugin
87
- * app page passes its own `appRail` item id. */
88
- active: AppShellActive;
89
- /** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
90
- userUid?: string;
91
- /** auth-server's base URL, injected via the route's `fetchProps`. */
92
- authServerUrl?: string;
93
- /**
94
- * `true` when this session is an admin "log in as user" impersonation (a `jwt_impersonator` cookie is
95
- * present) — see `MailShell`'s original doc comment, unchanged now that this lives here. Drives the
96
- * "you are viewing as this user — stop impersonating" banner below.
97
- */
98
- impersonating?: boolean;
99
- /** Where `mailApi.ts`'s `stopImpersonating()` should call — see `MailShell`'s original doc comment. */
100
- impersonationBaseUrl?: string;
101
- /** `true` when the caller's JWT carries a trusted role — shows an "Admin Console" item in the user menu below. A token
102
- * only carries one once elevated, so a real administrator with an ordinary session is `false` here; the user menu
103
- * asks auth-server about them separately (see `UserMenu`'s `detectAdmin`). */
104
- trusted?: boolean;
105
- /** The role names the server treats as trusted (its `trusted_roles` config, injected via the route's `fetchProps`) -
106
- * what that lookup looks for. Absent means the server's own default, `["admin"]`. */
107
- trustedRoles?: string[];
108
- /** The deployment's branding as the server rendered the page (its `branding` prop). Used until the frame's own fetch answers, so a custom header is there from the first paint - and the frame knows to drop its own title bar - instead of appearing (and moving everything) once the request returns. */
109
- branding?: Branding;
110
- /** The signed-in user's stored appearance preferences as the server rendered the page (its `appearance` prop), so the first paint already wears the theme and background - see `AppearanceProvider`. */
111
- appearance?: unknown;
112
- }
113
-
114
- export interface AppDef {
115
- id: AppShellApp;
116
- href: string;
117
- label: string;
118
- icon: IconType;
119
- }
120
-
121
- export const APPS: AppDef[] = [
122
- { id: "mail", href: APP_HREFS.mail, label: "Mail", icon: HiOutlineEnvelope },
123
- { id: "calendar", href: APP_HREFS.calendar, label: "Calendar", icon: HiOutlineCalendarDays },
124
- { id: "contacts", href: APP_HREFS.contacts, label: "Contacts", icon: HiOutlineUsers },
125
- { id: "tasks", href: APP_HREFS.tasks, label: "Tasks", icon: HiOutlineClipboardDocumentList },
126
- ];
127
-
128
- /** Ids plugin `appRail` items can't take besides `APPS`' own - `"settings"` has no rail icon but is still a
129
- * core `active` value. */
130
- const RESERVED_APP_IDS = ["settings"];
131
-
132
- /** `APPS` followed by the plugins' `appRail` items (generic icon), core ids winning - see `mergePluginNavItems`. */
133
- export function appRailItems(pluginNav?: PluginNav): NavItem[] {
134
- return mergePluginNavItems<NavItem>(
135
- APPS,
136
- pluginNav?.appRail,
137
- ({ id, href, label }) => ({ id, href, label, icon: HiOutlinePuzzlePiece }),
138
- RESERVED_APP_IDS,
139
- );
140
- }
141
-
142
- /** What only the persistent frame (`apps/www/_shell.tsx`) passes to the chrome it keeps mounted. */
143
- export interface AppChromeProps extends AppShellProps {
144
- /** The router is loading the next page - the content is `aria-busy`. */
145
- busy?: boolean;
146
- /** A screen that takes over the window (`FrameTakeover`) is showing: the rail, header, banner and footer are hidden - not
147
- * unmounted, so what is inside them (and the page in the content region) keeps its state. */
148
- hideChrome?: boolean;
149
- }
150
-
151
- /**
152
- * The persistent chrome shared by every webmail app (Mail, Calendar, Contacts, Tasks): a left icon rail for
153
- * switching apps, a header with the current app's name and `UserMenu`, and the impersonation banner.
154
- * Each app's own shell (e.g. `MailShell`) renders its own contextual sidebar + content as `children`, inside
155
- * the area to the right of the icon rail and below the header.
156
- *
157
- * Rendered by the webmail's app shell (`apps/www/_shell.tsx`, the router's persistent client layout), once, for the life of the
158
- * page, so that moving between apps replaces only `children` - see `AppShell` below for what a page's own shell renders instead.
159
- */
160
- export function AppChrome({
161
- active,
162
- userUid,
163
- authServerUrl,
164
- impersonating,
165
- impersonationBaseUrl,
166
- trusted,
167
- trustedRoles,
168
- branding: initialBranding,
169
- appearance,
170
- pluginNav,
171
- busy,
172
- hideChrome,
173
- children,
174
- }: PropsWithChildren<AppChromeProps>) {
175
- const [stoppingImpersonation, setStoppingImpersonation] = useState(false);
176
- // The keyboard shortcuts dialog: opened by `?`/Ctrl+/ (`GlobalShortcuts`) and by the account menu's item.
177
- const [shortcutsOpen, setShortcutsOpen] = useState(false);
178
- // The pop-up history ("Recent notifications" in the account menu). The pop-ups themselves are drawn by `NotificationCenter`, below.
179
- const [historyOpen, setHistoryOpen] = useState(false);
180
- // Only the count is read, so the whole frame does not render again for every pop-up that comes and goes.
181
- const unseenErrors = useUnseenErrorCount();
182
- // The title bar's height for the pop-up stack, which sticks just below the header (a branding header publishes its own - `FrameBrandingHeader`).
183
- const headerRef = useHeaderHeightRef();
184
- const signingOutRef = useRef(false);
185
- const { branding: fetchedBranding, iconSrc: fetchedIconSrc } = useBranding();
186
- // The server's copy until the fetch answers, so the frame's shape (a custom header replaces the title bar) never changes after the first paint.
187
- const branding = fetchedBranding ?? initialBranding ?? null;
188
- const iconSrc = fetchedBranding ? fetchedIconSrc : initialBranding?.iconUrl || initialBranding?.logoUrl || fetchedIconSrc;
189
- // The custom header and footer, sanitized and with their `{USER_MENU}` / `{APP_TITLE}` placeholders (`undefined` until parsed, `null` when there is none).
190
- const header = useBrandingHtml(branding?.headerHtml);
191
- const footer = useBrandingHtml(branding?.footerHtml);
192
- // The mailboxes, their folders and the one push connection live here, in the frame that stays mounted as the router swaps pages - so
193
- // new-mail pop-ups, the folder counters and the tab title's unread count work in Calendar, Contacts, Tasks and Settings too, and moving
194
- // between apps never opens a second socket (the server allows ten per user). `MailShell` reads them from `MailConnectionContext`. Outside
195
- // the router (`AppShell` rendered as a page's own chrome: tests, plugin pages) it stays off, and a Mail shell owns its connection itself.
196
- const inFrame = useInAppFrame();
197
- const navigate = useNavigate();
198
- const { pathname } = useRouter();
199
- const mail = useMailConnection({ userUid, enabled: inFrame, open: navigate });
200
- // A signing certificate the user asked for is announced when it is issued (or fails), on whichever page they are - see the hook.
201
- useSigningEnrollmentWatcher({ userUid, mailboxes: mail.mailboxes, enabled: inFrame });
202
- // A meeting reminder pops up on whichever page they are - see the hook.
203
- useCalendarReminders({ userUid, enabled: inFrame });
204
-
205
- useRedirectIfUnauthenticated(userUid, authServerUrl);
206
- // Any request this app makes that the server answers with a 401 - the session ended - raises one "Your session expired" pop-up with a
207
- // Sign in action (see `notifySessionExpired()`), whichever request noticed first, a background refresh included.
208
- useEffect(() => {
209
- setSignInUrl(authServerUrl);
210
- if (!userUid) {
211
- return;
212
- }
213
- setApiUnauthorizedObserver(() => void notifySessionExpired());
214
- return () => setApiUnauthorizedObserver(undefined);
215
- }, [userUid, authServerUrl]);
216
- // Mounted here, not scoped to Mail/Settings (the only shells that actually read unlocked keys),
217
- // specifically so activity in *any* app resets the idle clock - see that hook's own doc comment.
218
- useIdleKeyTimeout();
219
-
220
- // An administrator on a server that hasn't finished first-run setup is sent to the setup wizard. The status
221
- // check is admin-only, so it's only made for a trusted caller - everyone else would just get a 403 on every
222
- // page load. Any failure is ignored.
223
- useEffect(() => {
224
- if (!userUid || impersonating || !trusted) {
225
- return;
226
- }
227
- getSetupStatus()
228
- .then((status) => {
229
- if (status.required) {
230
- window.location.href = "/admin/setup";
231
- }
232
- })
233
- .catch(() => undefined);
234
- }, [userUid, impersonating, trusted]);
235
-
236
- // Another tab signing out (this app's `handleSignOut`, or the admin/escrow consoles' `signOutOfConsole()`)
237
- // ended this session too - its auth cookie is gone - so this tab destroys its own unlocked keys and every
238
- // local search index on the device, then leaves as well. The consoles have no local-index client of their
239
- // own, so this is what actually removes the indexes after a console sign-out (the console also records a
240
- // pending deletion, retried on the next mail load, in case no mail tab is open). `destroyAllLocalIndexes()`
241
- // announces the sign-out on this same channel, which this tab then hears itself, so the ref is set first:
242
- // each tab reacts once, and the tab that started the sign-out ignores its own announcement.
243
- useEffect(() => {
244
- if (!userUid || typeof BroadcastChannel === "undefined") {
245
- return;
246
- }
247
- const channel = new BroadcastChannel(SIGN_OUT_CHANNEL);
248
- channel.addEventListener("message", (event: MessageEvent<{ type?: string }>) => {
249
- if (event.data?.type !== "sign-out" || signingOutRef.current) {
250
- return;
251
- }
252
- signingOutRef.current = true;
253
- // Compose windows must not ask "Leave site?" - that would let this forced navigation be cancelled.
254
- markSigningOut();
255
- destroyUnlockedKeys();
256
- clearPinnedSignerCache();
257
- clearAppearanceCache();
258
- // Bounded by its own timeout and never rejects - awaited so navigating doesn't kill the Worker mid-delete.
259
- void destroyAllLocalIndexes().then(() => {
260
- window.location.href = authServerUrl ?? "/";
261
- });
262
- });
263
- return () => channel.close();
264
- }, [userUid, authServerUrl]);
265
-
266
- async function handleSignOut() {
267
- signingOutRef.current = true;
268
- // Before flushing and navigating: compose windows then skip their "Leave site?" prompt, which could
269
- // otherwise cancel the sign-out's own navigation.
270
- markSigningOut();
271
- // Unlocked private keys never outlive an explicit sign-out.
272
- destroyUnlockedKeys();
273
- // Trusted signer pins read from contacts don't outlive the session either.
274
- clearPinnedSignerCache();
275
- clearAppearanceCache();
276
- // Open compose windows save edits still waiting on their autosave debounce while the session is still
277
- // valid - logout invalidates it. Bounded the same way as logout itself, and never rejects.
278
- await flushComposeDrafts(LOGOUT_TIMEOUT_MS);
279
- // The Tier 2 local index MUST be destroyed on explicit logout, the same as unlocked keys themselves
280
- // (spec §11). Destroys every index on this device (not only mailboxes opened this page load) and is
281
- // awaited before navigating - a navigation tears down the Worker mid-delete otherwise.
282
- // destroyAllLocalIndexes() is bounded by its own timeout and never rejects, so sign-out can't hang.
283
- // In parallel, auth-server's logout clears the auth cookie and invalidates the session's refresh
284
- // token - without it, "Sign Out" would only navigate away from a still-valid session.
285
- await Promise.all([destroyAllLocalIndexes(), logOutOfAuthServer(authServerUrl)]);
286
- window.location.href = authServerUrl ?? "/";
287
- }
288
-
289
- async function handleStopImpersonating() {
290
- setStoppingImpersonation(true);
291
- try {
292
- await stopImpersonating(impersonationBaseUrl ?? "");
293
- } catch {
294
- // Navigate either way: a failed call leaves the impersonator cookie (and this banner) exactly as
295
- // they were, so there's nothing else useful to show — matching this app's other network-error
296
- // handling, which surfaces via a full reload rather than an inline retry affordance.
297
- } finally {
298
- window.location.href = "/admin";
299
- }
300
- }
301
-
302
- // The tab's title in the frame is each page's own (its `title` export: rendered by the server, set again by the router on every navigation),
303
- // but `useBranding()` above sets it to the branding's title once its fetch answers - a title for a page that has none. The page's is put back:
304
- // the layout effect reads it before that hook's effect runs, this one (declared after it) writes it back.
305
- const pageTitleRef = useRef("");
306
- useLayoutEffect(() => {
307
- pageTitleRef.current = document.title;
308
- }, [fetchedBranding?.title]);
309
- useEffect(() => {
310
- if (inFrame) {
311
- document.title = pageTitleRef.current;
312
- }
313
- }, [fetchedBranding?.title, inFrame]);
314
- // The router sets the document's title to the page's own whenever the page changes, which drops the unread count this puts in front
315
- // of it, so a new pathname is what puts the count back. (A folder change is shallow and keeps the title.)
316
- useUnreadTitle(inboxUnreadTotal(mail.mailboxFolders, mail.folderCounts.counts), { enabled: inFrame, resetKey: pathname });
317
-
318
- if (!userUid) {
319
- return <div className="min-h-screen" />;
320
- }
321
-
322
- const apps = appRailItems(pluginNav);
323
- // The header title: "settings" has no rail item, everything else is labelled by its own rail item.
324
- const activeLabel = active === "settings" ? "Settings" : apps.find((app) => app.id === active)?.label;
325
- // A custom header (`Branding.headerHtml`) replaces the app's own title bar and the icon at the top of the rail: it is the top of the app, and the
326
- // account menu moves into it. While it is still being parsed it already counts, so the frame's shape doesn't change under the user.
327
- const customHeader = header !== null;
328
- // Where the one account menu lives: the header's `{USER_MENU}`, else the footer's, else a small cell at the header's right end - never lost.
329
- const menuInHeader = !!header?.hasUserMenu;
330
- const menuInFooter = customHeader && !!header && !menuInHeader && !!footer?.hasUserMenu;
331
- const renderUserMenu = (placement: "down" | "up") => (
332
- <UserMenu
333
- userUid={userUid}
334
- authServerUrl={authServerUrl}
335
- onSignOut={handleSignOut}
336
- // An impersonating administrator acts as the impersonated user, so the console link is hidden then.
337
- showAdminLink={!!trusted && !impersonating}
338
- detectAdmin={!trusted && !impersonating}
339
- trustedRoles={trustedRoles}
340
- showSettingsLink
341
- showNotificationSettings
342
- onShowShortcuts={() => setShortcutsOpen(true)}
343
- onShowNotifications={() => setHistoryOpen(true)}
344
- unseenErrors={unseenErrors}
345
- placement={placement}
346
- />
347
- );
348
-
349
- return (
350
- // Mounted here, not scoped to Mail/Settings, for the same reason as useIdleKeyTimeout() above -
351
- // ComposeWindow's sign/encrypt toggles and MessageDetailPane's encrypted-message view (both Mail)
352
- // are today's only useUnlockPrompt() callers, but this needs to be available to any app shell.
353
- // Asks the server for the user's appearance only in the persistent frame (every core page); a page that renders its own chrome (a plugin's) uses the
354
- // `appearance` it was given and what this browser cached.
355
- <AppearanceProvider userUid={userUid} initial={appearance} lookUp={inFrame}>
356
- <ShortcutProvider>
357
- <MailConnectionContext.Provider value={inFrame ? mail : null}>
358
- <GlobalShortcuts authServerUrl={authServerUrl} onToggleHelp={() => setShortcutsOpen((open) => !open)} />
359
- <ShortcutsDialog open={shortcutsOpen} onClose={() => setShortcutsOpen(false)} />
360
- <UnlockPromptProvider>
361
- <UnlockBridge />
362
- {/* An impersonating admin acts with the impersonated user's access, so their own trusted role
363
- mustn't skip the per-mailbox checks. */}
364
- <ComposeProvider userUid={userUid} trusted={!!trusted && !impersonating}>
365
- <div className="rr-frame-bg min-h-screen flex flex-col">
366
- {!hideChrome && customHeader && (
367
- <FrameBrandingHeader
368
- parsed={header}
369
- userMenu={menuInHeader ? renderUserMenu("down") : undefined}
370
- fallbackMenu={header && !menuInHeader && !menuInFooter ? renderUserMenu("down") : undefined}
371
- appTitle={activeLabel}
372
- />
373
- )}
374
- {impersonating && !hideChrome && (
375
- <div className="h-10 shrink-0 bg-warning text-warning-contrast flex items-center justify-center gap-3 text-sm font-medium px-4">
376
- <span>
377
- You are viewing as <strong>{userUid}</strong>.
378
- </span>
379
- <button
380
- type="button"
381
- onClick={handleStopImpersonating}
382
- disabled={stoppingImpersonation}
383
- className="underline hover:no-underline disabled:opacity-60"
384
- >
385
- {stoppingImpersonation ? "Returning to admin…" : "Return to admin"}
386
- </button>
387
- </div>
388
- )}
389
- <div className="flex-1 flex min-h-0">
390
- {!hideChrome && (
391
- <nav
392
- aria-label="Apps"
393
- className={[
394
- "hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center gap-1",
395
- // Under a custom header the rail starts with the app icons; without one its own icon is flush with the top of the window.
396
- customHeader ? "py-3" : "pb-3",
397
- ].join(" ")}
398
- >
399
- {!customHeader && (
400
- <RailIcon src={iconSrc} />
401
- )}
402
- {apps.map(({ id, href, label, icon: Icon }) => (
403
- <a
404
- key={id}
405
- href={href}
406
- aria-label={label}
407
- aria-current={id === active ? "page" : undefined}
408
- title={label}
409
- className={[
410
- "w-10 h-10 flex items-center justify-center rounded-sm",
411
- id === active
412
- ? "bg-primary/10 text-primary-dark"
413
- : "text-text-muted hover:bg-surface-alt hover:text-text",
414
- ].join(" ")}
415
- >
416
- <Icon size={20} aria-hidden="true" />
417
- </a>
418
- ))}
419
- </nav>
420
- )}
421
- {!hideChrome && <BottomTabBar apps={apps} active={active} />}
422
- <div className="flex-1 flex flex-col min-w-0">
423
- {!hideChrome && !customHeader && (
424
- <header ref={headerRef} className="rr-solid sticky top-0 z-30 h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6">
425
- <span className="font-display font-bold text-lg uppercase tracking-wide">{activeLabel}</span>
426
- {renderUserMenu("down")}
427
- </header>
428
- )}
429
- {/* The one pop-up stack for the whole app: right under the header row, so it never covers the account menu. */}
430
- <NotificationCenter />
431
- <div
432
- id="app-content"
433
- tabIndex={-1}
434
- aria-busy={busy || undefined}
435
- className={["flex-1 flex min-h-0 outline-none", hideChrome ? "" : "pb-14 md:pb-0"].join(" ")}
436
- >
437
- {children}
438
- </div>
439
- </div>
440
- </div>
441
- {!hideChrome && <FrameBrandingFooter parsed={footer} userMenu={menuInFooter ? renderUserMenu("up") : undefined} appTitle={activeLabel} />}
442
- </div>
443
- </ComposeProvider>
444
- </UnlockPromptProvider>
445
- <NotificationHistoryDialog open={historyOpen} onClose={() => setHistoryOpen(false)} />
446
- </MailConnectionContext.Provider>
447
- </ShortcutProvider>
448
- </AppearanceProvider>
449
- );
450
- }
451
-
452
- /**
453
- * What a page's own shell (`MailShell`, `CalendarShell`, ...) renders around its content. Outside the client-side router it
454
- * is the whole `AppChrome`, as it always was. Inside it (the router's app shell, `apps/www/_shell.tsx`) the chrome is already mounted above the
455
- * page - and stays mounted as the page is replaced - so this is only its children; everything it would have been given comes from
456
- * the shell instead (the props are the same for every page, and `active` is the route's).
457
- */
458
- export default function AppShell(props: PropsWithChildren<AppShellProps>) {
459
- const inFrame = useInAppFrame();
460
- return inFrame ? <>{props.children}</> : <AppChrome {...props} />;
461
- }
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import "../../styles/app.css";
6
+ import React, { PropsWithChildren, useEffect, useLayoutEffect, useRef, useState } from "react";
7
+ import type { IconType } from "react-icons";
8
+ import {
9
+ HiOutlineCalendarDays,
10
+ HiOutlineClipboardDocumentList,
11
+ HiOutlineEnvelope,
12
+ HiOutlinePuzzlePiece,
13
+ HiOutlineUsers,
14
+ } from "react-icons/hi2";
15
+ import { useRouter } from "@rapidrest/react/client";
16
+ import { useSessionRefresh } from "../../../../lib/auth/session.js";
17
+ import { getSetupStatus } from "../../../../lib/admin/setupApi.js";
18
+ import { stopImpersonating } from "../../../../lib/mail/mailApi.js";
19
+ import { useApiClient } from "../../../../lib/util/apiClientContext.js";
20
+ import useBranding from "../../../../lib/branding/useBranding.js";
21
+ import { useIdleKeyTimeout } from "../../../../lib/crypto/useIdleKeyTimeout.js";
22
+ import ComposeProvider from "../mail/compose/ComposeContext.js";
23
+ import { flushComposeDrafts, markSigningOut } from "../mail/compose/composeFlushRegistry.js";
24
+ import BottomTabBar, { NavItem } from "../../../../lib/components/navigation/BottomTabBar.js";
25
+ import { FrameBrandingFooter, FrameBrandingHeader, useBrandingHtml } from "./BrandingChrome.js";
26
+ import RailIcon from "./RailIcon.js";
27
+ import AppearanceProvider from "../../appearance/AppearanceProvider.js";
28
+ import { clearAppearanceCache } from "../../appearance/appearanceCache.js";
29
+ import type { Branding } from "../../../../lib/branding/brandingApi.js";
30
+ import UserMenu from "./UserMenu.js";
31
+ import { UnlockPromptProvider } from "./UnlockPromptProvider.js";
32
+ import { SIGN_OUT_CHANNEL, destroyAllLocalIndexes } from "../../search/localIndexRpcClient.js";
33
+ import { authApiFetch, setApiUnauthorizedObserver } from "../../../../lib/util/api.js";
34
+ import { destroyUnlockedKeys } from "../../../../lib/crypto/keySession.js";
35
+ import { clearPinnedSignerCache } from "../mail/pinnedSigners.js";
36
+ import { mergePluginNavItems, PluginNav, PluginNavProps } from "../../plugins/pluginNav.js";
37
+ import { useInAppFrame } from "../../navigation/frameContext.js";
38
+ import { APP_HREFS } from "../../navigation/appHrefs.js";
39
+ import { useNavigate } from "../../navigation/index.js";
40
+ import { GlobalShortcuts } from "../../keyboard/GlobalShortcuts.js";
41
+ import { ShortcutProvider } from "../../keyboard/ShortcutProvider.js";
42
+ import ShortcutsDialog from "../../keyboard/ShortcutsDialog.js";
43
+ import { inboxUnreadTotal } from "../../mail/folderCounts.js";
44
+ import { MailConnectionContext, useMailConnection } from "../../mail/useMailConnection.js";
45
+ import { useUnreadTitle } from "../../mail/useUnreadTitle.js";
46
+ import UnlockBridge from "../../mail/outbox/UnlockBridge.js";
47
+ import NotificationCenter from "../../notifications/NotificationCenter.js";
48
+ import { useHeaderHeightRef } from "../../notifications/headerOffset.js";
49
+ import NotificationHistoryDialog from "../../notifications/NotificationHistoryDialog.js";
50
+ import { notifySessionExpired, setSignInUrl } from "../../notifications/apiErrors.js";
51
+ import { useUnseenErrorCount } from "../../notifications/useNotifications.js";
52
+ import { useSigningEnrollmentWatcher } from "../../signing/useSigningEnrollmentWatcher.js";
53
+ import { useCalendarReminders } from "../../calendar/useCalendarReminders.js";
54
+
55
+ /** How long sign-out waits for auth-server's logout before navigating anyway. */
56
+ export const LOGOUT_TIMEOUT_MS = 3_000;
57
+
58
+ /**
59
+ * Calls auth-server's logout (`POST /api/auth/logout`, cross-origin with credentials), which clears the auth
60
+ * cookie and invalidates the session's refresh token. Bounded by `LOGOUT_TIMEOUT_MS` and never rejects - a
61
+ * failure (unreachable server, CORS) must not keep the user from leaving.
62
+ */
63
+ async function logOutOfAuthServer(authServerUrl: string | undefined): Promise<void> {
64
+ if (!authServerUrl) {
65
+ return;
66
+ }
67
+ const controller = new AbortController();
68
+ const timer = setTimeout(() => controller.abort(), LOGOUT_TIMEOUT_MS);
69
+ try {
70
+ await authApiFetch(authServerUrl, "/auth/logout", { method: "POST", signal: controller.signal });
71
+ } catch {
72
+ // Navigate anyway - see this function's doc comment.
73
+ } finally {
74
+ clearTimeout(timer);
75
+ }
76
+ }
77
+
78
+ export type AppShellApp = "mail" | "calendar" | "contacts" | "tasks";
79
+
80
+ /** `"settings"` is a valid `active` value but deliberately has no entry in `APPS` below — Settings is
81
+ * reached via a `UserMenu` item, not a 5th rail icon (see `SettingsShell.tsx`), so it highlights no
82
+ * rail/tab icon at all; only the header title needs to account for it. Any other string is a plugin's
83
+ * `appRail` item id (see `PluginNav`). */
84
+ export type AppShellActive = AppShellApp | "settings" | (string & {});
85
+
86
+ export interface AppShellProps extends PluginNavProps {
87
+ /** Which icon in the rail is highlighted as the current app — `"settings"` highlights none, and a plugin
88
+ * app page passes its own `appRail` item id. */
89
+ active: AppShellActive;
90
+ /** Populated automatically by the framework from an authenticated request (e.g. a valid `jwt` cookie). */
91
+ userUid?: string;
92
+ /** auth-server's base URL, injected via the route's `fetchProps`. */
93
+ authServerUrl?: string;
94
+ /**
95
+ * `true` when this session is an admin "log in as user" impersonation (a `jwt_impersonator` cookie is
96
+ * present) — see `MailShell`'s original doc comment, unchanged now that this lives here. Drives the
97
+ * "you are viewing as this user — stop impersonating" banner below.
98
+ */
99
+ impersonating?: boolean;
100
+ /** Where `mailApi.ts`'s `stopImpersonating()` should call — see `MailShell`'s original doc comment. */
101
+ impersonationBaseUrl?: string;
102
+ /** `true` when the caller's JWT carries a trusted role — shows an "Admin Console" item in the user menu below. A token
103
+ * only carries one once elevated, so a real administrator with an ordinary session is `false` here; the user menu
104
+ * asks auth-server about them separately (see `UserMenu`'s `detectAdmin`). */
105
+ trusted?: boolean;
106
+ /** The role names the server treats as trusted (its `trusted_roles` config, injected via the route's `fetchProps`) -
107
+ * what that lookup looks for. Absent means the server's own default, `["admin"]`. */
108
+ trustedRoles?: string[];
109
+ /** The deployment's branding as the server rendered the page (its `branding` prop). Used until the frame's own fetch answers, so a custom header is there from the first paint - and the frame knows to drop its own title bar - instead of appearing (and moving everything) once the request returns. */
110
+ branding?: Branding;
111
+ /** The signed-in user's stored appearance preferences as the server rendered the page (its `appearance` prop), so the first paint already wears the theme and background - see `AppearanceProvider`. */
112
+ appearance?: unknown;
113
+ }
114
+
115
+ export interface AppDef {
116
+ id: AppShellApp;
117
+ href: string;
118
+ label: string;
119
+ icon: IconType;
120
+ }
121
+
122
+ export const APPS: AppDef[] = [
123
+ { id: "mail", href: APP_HREFS.mail, label: "Mail", icon: HiOutlineEnvelope },
124
+ { id: "calendar", href: APP_HREFS.calendar, label: "Calendar", icon: HiOutlineCalendarDays },
125
+ { id: "contacts", href: APP_HREFS.contacts, label: "Contacts", icon: HiOutlineUsers },
126
+ { id: "tasks", href: APP_HREFS.tasks, label: "Tasks", icon: HiOutlineClipboardDocumentList },
127
+ ];
128
+
129
+ /** Ids plugin `appRail` items can't take besides `APPS`' own - `"settings"` has no rail icon but is still a
130
+ * core `active` value. */
131
+ const RESERVED_APP_IDS = ["settings"];
132
+
133
+ /** `APPS` followed by the plugins' `appRail` items (generic icon), core ids winning - see `mergePluginNavItems`. */
134
+ export function appRailItems(pluginNav?: PluginNav): NavItem[] {
135
+ return mergePluginNavItems<NavItem>(
136
+ APPS,
137
+ pluginNav?.appRail,
138
+ ({ id, href, label }) => ({ id, href, label, icon: HiOutlinePuzzlePiece }),
139
+ RESERVED_APP_IDS,
140
+ );
141
+ }
142
+
143
+ /** What only the persistent frame (`apps/www/_shell.tsx`) passes to the chrome it keeps mounted. */
144
+ export interface AppChromeProps extends AppShellProps {
145
+ /** The router is loading the next page - the content is `aria-busy`. */
146
+ busy?: boolean;
147
+ /** A screen that takes over the window (`FrameTakeover`) is showing: the rail, header, banner and footer are hidden - not
148
+ * unmounted, so what is inside them (and the page in the content region) keeps its state. */
149
+ hideChrome?: boolean;
150
+ }
151
+
152
+ /**
153
+ * The persistent chrome shared by every webmail app (Mail, Calendar, Contacts, Tasks): a left icon rail for
154
+ * switching apps, a header with the current app's name and `UserMenu`, and the impersonation banner.
155
+ * Each app's own shell (e.g. `MailShell`) renders its own contextual sidebar + content as `children`, inside
156
+ * the area to the right of the icon rail and below the header.
157
+ *
158
+ * Rendered by the webmail's app shell (`apps/www/_shell.tsx`, the router's persistent client layout), once, for the life of the
159
+ * page, so that moving between apps replaces only `children` - see `AppShell` below for what a page's own shell renders instead.
160
+ */
161
+ export function AppChrome({
162
+ active,
163
+ userUid,
164
+ authServerUrl,
165
+ impersonating,
166
+ impersonationBaseUrl,
167
+ trusted,
168
+ trustedRoles,
169
+ branding: initialBranding,
170
+ appearance,
171
+ pluginNav,
172
+ busy,
173
+ hideChrome,
174
+ children,
175
+ }: PropsWithChildren<AppChromeProps>) {
176
+ const client = useApiClient();
177
+ const [stoppingImpersonation, setStoppingImpersonation] = useState(false);
178
+ // The keyboard shortcuts dialog: opened by `?`/Ctrl+/ (`GlobalShortcuts`) and by the account menu's item.
179
+ const [shortcutsOpen, setShortcutsOpen] = useState(false);
180
+ // The pop-up history ("Recent notifications" in the account menu). The pop-ups themselves are drawn by `NotificationCenter`, below.
181
+ const [historyOpen, setHistoryOpen] = useState(false);
182
+ // Only the count is read, so the whole frame does not render again for every pop-up that comes and goes.
183
+ const unseenErrors = useUnseenErrorCount();
184
+ // The title bar's height for the pop-up stack, which sticks just below the header (a branding header publishes its own - `FrameBrandingHeader`).
185
+ const headerRef = useHeaderHeightRef();
186
+ const signingOutRef = useRef(false);
187
+ const { branding: fetchedBranding, iconSrc: fetchedIconSrc } = useBranding();
188
+ // The server's copy until the fetch answers, so the frame's shape (a custom header replaces the title bar) never changes after the first paint.
189
+ const branding = fetchedBranding ?? initialBranding ?? null;
190
+ const iconSrc = fetchedBranding ? fetchedIconSrc : initialBranding?.iconUrl || initialBranding?.logoUrl || fetchedIconSrc;
191
+ // The custom header and footer, sanitized and with their `{USER_MENU}` / `{APP_TITLE}` placeholders (`undefined` until parsed, `null` when there is none).
192
+ const header = useBrandingHtml(branding?.headerHtml);
193
+ const footer = useBrandingHtml(branding?.footerHtml);
194
+ // The mailboxes, their folders and the one push connection live here, in the frame that stays mounted as the router swaps pages - so
195
+ // new-mail pop-ups, the folder counters and the tab title's unread count work in Calendar, Contacts, Tasks and Settings too, and moving
196
+ // between apps never opens a second socket (the server allows ten per user). `MailShell` reads them from `MailConnectionContext`. Outside
197
+ // the router (`AppShell` rendered as a page's own chrome: tests, plugin pages) it stays off, and a Mail shell owns its connection itself.
198
+ const inFrame = useInAppFrame();
199
+ const navigate = useNavigate();
200
+ const { pathname } = useRouter();
201
+ const mail = useMailConnection({ userUid, enabled: inFrame, open: navigate });
202
+ // A signing certificate the user asked for is announced when it is issued (or fails), on whichever page they are - see the hook.
203
+ useSigningEnrollmentWatcher({ userUid, mailboxes: mail.mailboxes, enabled: inFrame });
204
+ // A meeting reminder pops up on whichever page they are - see the hook.
205
+ useCalendarReminders({ userUid, enabled: inFrame });
206
+
207
+ // A refused refresh means the sign-in has ended: open compose windows save what they hold (the access token still works for a while) and the
208
+ // browser goes to sign-in. Not while viewing as another user - the refresh cookie is the admin's own, and refreshing would end the impersonation.
209
+ useSessionRefresh(userUid, authServerUrl, { paused: impersonating, beforeRedirect: () => flushComposeDrafts(LOGOUT_TIMEOUT_MS) });
210
+ // Any request this app makes that the server answers with a 401 - the session ended - raises one "Your session expired" pop-up with a
211
+ // Sign in action (see `notifySessionExpired()`), whichever request noticed first, a background refresh included.
212
+ useEffect(() => {
213
+ setSignInUrl(authServerUrl);
214
+ if (!userUid) {
215
+ return;
216
+ }
217
+ setApiUnauthorizedObserver(() => void notifySessionExpired());
218
+ return () => setApiUnauthorizedObserver(undefined);
219
+ }, [userUid, authServerUrl]);
220
+ // Mounted here, not scoped to Mail/Settings (the only shells that actually read unlocked keys),
221
+ // specifically so activity in *any* app resets the idle clock - see that hook's own doc comment.
222
+ useIdleKeyTimeout();
223
+
224
+ // An administrator on a server that hasn't finished first-run setup is sent to the setup wizard. The status
225
+ // check is admin-only, so it's only made for a trusted caller - everyone else would just get a 403 on every
226
+ // page load. Any failure is ignored.
227
+ useEffect(() => {
228
+ if (!userUid || impersonating || !trusted) {
229
+ return;
230
+ }
231
+ getSetupStatus(client)
232
+ .then((status) => {
233
+ if (status.required) {
234
+ window.location.href = "/admin/setup";
235
+ }
236
+ })
237
+ .catch(() => undefined);
238
+ }, [userUid, impersonating, trusted]);
239
+
240
+ // Another tab signing out (this app's `handleSignOut`, or the admin/escrow consoles' `signOutOfConsole()`)
241
+ // ended this session too - its auth cookie is gone - so this tab destroys its own unlocked keys and every
242
+ // local search index on the device, then leaves as well. The consoles have no local-index client of their
243
+ // own, so this is what actually removes the indexes after a console sign-out (the console also records a
244
+ // pending deletion, retried on the next mail load, in case no mail tab is open). `destroyAllLocalIndexes()`
245
+ // announces the sign-out on this same channel, which this tab then hears itself, so the ref is set first:
246
+ // each tab reacts once, and the tab that started the sign-out ignores its own announcement.
247
+ useEffect(() => {
248
+ if (!userUid || typeof BroadcastChannel === "undefined") {
249
+ return;
250
+ }
251
+ const channel = new BroadcastChannel(SIGN_OUT_CHANNEL);
252
+ channel.addEventListener("message", (event: MessageEvent<{ type?: string }>) => {
253
+ if (event.data?.type !== "sign-out" || signingOutRef.current) {
254
+ return;
255
+ }
256
+ signingOutRef.current = true;
257
+ // Compose windows must not ask "Leave site?" - that would let this forced navigation be cancelled.
258
+ markSigningOut();
259
+ destroyUnlockedKeys();
260
+ clearPinnedSignerCache();
261
+ clearAppearanceCache();
262
+ // Bounded by its own timeout and never rejects - awaited so navigating doesn't kill the Worker mid-delete.
263
+ void destroyAllLocalIndexes().then(() => {
264
+ window.location.href = authServerUrl ?? "/";
265
+ });
266
+ });
267
+ return () => channel.close();
268
+ }, [userUid, authServerUrl]);
269
+
270
+ async function handleSignOut() {
271
+ signingOutRef.current = true;
272
+ // Before flushing and navigating: compose windows then skip their "Leave site?" prompt, which could
273
+ // otherwise cancel the sign-out's own navigation.
274
+ markSigningOut();
275
+ // Unlocked private keys never outlive an explicit sign-out.
276
+ destroyUnlockedKeys();
277
+ // Trusted signer pins read from contacts don't outlive the session either.
278
+ clearPinnedSignerCache();
279
+ clearAppearanceCache();
280
+ // Open compose windows save edits still waiting on their autosave debounce while the session is still
281
+ // valid - logout invalidates it. Bounded the same way as logout itself, and never rejects.
282
+ await flushComposeDrafts(LOGOUT_TIMEOUT_MS);
283
+ // The Tier 2 local index MUST be destroyed on explicit logout, the same as unlocked keys themselves
284
+ // (spec §11). Destroys every index on this device (not only mailboxes opened this page load) and is
285
+ // awaited before navigating - a navigation tears down the Worker mid-delete otherwise.
286
+ // destroyAllLocalIndexes() is bounded by its own timeout and never rejects, so sign-out can't hang.
287
+ // In parallel, auth-server's logout clears the auth cookie and invalidates the session's refresh
288
+ // token - without it, "Sign Out" would only navigate away from a still-valid session.
289
+ await Promise.all([destroyAllLocalIndexes(), logOutOfAuthServer(authServerUrl)]);
290
+ window.location.href = authServerUrl ?? "/";
291
+ }
292
+
293
+ async function handleStopImpersonating() {
294
+ setStoppingImpersonation(true);
295
+ try {
296
+ await stopImpersonating(impersonationBaseUrl ?? "");
297
+ } catch {
298
+ // Navigate either way: a failed call leaves the impersonator cookie (and this banner) exactly as
299
+ // they were, so there's nothing else useful to show — matching this app's other network-error
300
+ // handling, which surfaces via a full reload rather than an inline retry affordance.
301
+ } finally {
302
+ window.location.href = "/admin";
303
+ }
304
+ }
305
+
306
+ // The tab's title in the frame is each page's own (its `title` export: rendered by the server, set again by the router on every navigation),
307
+ // but `useBranding()` above sets it to the branding's title once its fetch answers - a title for a page that has none. The page's is put back:
308
+ // the layout effect reads it before that hook's effect runs, this one (declared after it) writes it back.
309
+ const pageTitleRef = useRef("");
310
+ useLayoutEffect(() => {
311
+ pageTitleRef.current = document.title;
312
+ }, [fetchedBranding?.title]);
313
+ useEffect(() => {
314
+ if (inFrame) {
315
+ document.title = pageTitleRef.current;
316
+ }
317
+ }, [fetchedBranding?.title, inFrame]);
318
+ // The router sets the document's title to the page's own whenever the page changes, which drops the unread count this puts in front
319
+ // of it, so a new pathname is what puts the count back. (A folder change is shallow and keeps the title.)
320
+ useUnreadTitle(inboxUnreadTotal(mail.mailboxFolders, mail.folderCounts.counts), { enabled: inFrame, resetKey: pathname });
321
+
322
+ if (!userUid) {
323
+ return <div className="min-h-screen" />;
324
+ }
325
+
326
+ const apps = appRailItems(pluginNav);
327
+ // The header title: "settings" has no rail item, everything else is labelled by its own rail item.
328
+ const activeLabel = active === "settings" ? "Settings" : apps.find((app) => app.id === active)?.label;
329
+ // A custom header (`Branding.headerHtml`) replaces the app's own title bar and the icon at the top of the rail: it is the top of the app, and the
330
+ // account menu moves into it. While it is still being parsed it already counts, so the frame's shape doesn't change under the user.
331
+ const customHeader = header !== null;
332
+ // Where the one account menu lives: the header's `{USER_MENU}`, else the footer's, else a small cell at the header's right end - never lost.
333
+ const menuInHeader = !!header?.hasUserMenu;
334
+ const menuInFooter = customHeader && !!header && !menuInHeader && !!footer?.hasUserMenu;
335
+ const renderUserMenu = (placement: "down" | "up") => (
336
+ <UserMenu
337
+ userUid={userUid}
338
+ authServerUrl={authServerUrl}
339
+ onSignOut={handleSignOut}
340
+ // An impersonating administrator acts as the impersonated user, so the console link is hidden then.
341
+ showAdminLink={!!trusted && !impersonating}
342
+ detectAdmin={!trusted && !impersonating}
343
+ trustedRoles={trustedRoles}
344
+ showSettingsLink
345
+ showNotificationSettings
346
+ onShowShortcuts={() => setShortcutsOpen(true)}
347
+ onShowNotifications={() => setHistoryOpen(true)}
348
+ unseenErrors={unseenErrors}
349
+ placement={placement}
350
+ />
351
+ );
352
+
353
+ return (
354
+ // Mounted here, not scoped to Mail/Settings, for the same reason as useIdleKeyTimeout() above -
355
+ // ComposeWindow's sign/encrypt toggles and MessageDetailPane's encrypted-message view (both Mail)
356
+ // are today's only useUnlockPrompt() callers, but this needs to be available to any app shell.
357
+ // Asks the server for the user's appearance only in the persistent frame (every core page); a page that renders its own chrome (a plugin's) uses the
358
+ // `appearance` it was given and what this browser cached.
359
+ <AppearanceProvider userUid={userUid} initial={appearance} lookUp={inFrame}>
360
+ <ShortcutProvider>
361
+ <MailConnectionContext.Provider value={inFrame ? mail : null}>
362
+ <GlobalShortcuts authServerUrl={authServerUrl} onToggleHelp={() => setShortcutsOpen((open) => !open)} />
363
+ <ShortcutsDialog open={shortcutsOpen} onClose={() => setShortcutsOpen(false)} />
364
+ <UnlockPromptProvider>
365
+ <UnlockBridge />
366
+ {/* An impersonating admin acts with the impersonated user's access, so their own trusted role
367
+ mustn't skip the per-mailbox checks. */}
368
+ <ComposeProvider userUid={userUid} trusted={!!trusted && !impersonating}>
369
+ <div className="rr-frame-bg min-h-screen flex flex-col">
370
+ {!hideChrome && customHeader && (
371
+ <FrameBrandingHeader
372
+ parsed={header}
373
+ userMenu={menuInHeader ? renderUserMenu("down") : undefined}
374
+ fallbackMenu={header && !menuInHeader && !menuInFooter ? renderUserMenu("down") : undefined}
375
+ appTitle={activeLabel}
376
+ />
377
+ )}
378
+ {impersonating && !hideChrome && (
379
+ <div className="h-10 shrink-0 bg-warning text-warning-contrast flex items-center justify-center gap-3 text-sm font-medium px-4">
380
+ <span>
381
+ You are viewing as <strong>{userUid}</strong>.
382
+ </span>
383
+ <button
384
+ type="button"
385
+ onClick={handleStopImpersonating}
386
+ disabled={stoppingImpersonation}
387
+ className="underline hover:no-underline disabled:opacity-60"
388
+ >
389
+ {stoppingImpersonation ? "Returning to admin…" : "Return to admin"}
390
+ </button>
391
+ </div>
392
+ )}
393
+ <div className="flex-1 flex min-h-0">
394
+ {!hideChrome && (
395
+ <nav
396
+ aria-label="Apps"
397
+ className={[
398
+ "hidden md:flex w-16 shrink-0 bg-surface border-r border-border flex-col items-center gap-1",
399
+ // Under a custom header the rail starts with the app icons; without one its own icon is flush with the top of the window.
400
+ customHeader ? "py-3" : "pb-3",
401
+ ].join(" ")}
402
+ >
403
+ {!customHeader && (
404
+ <RailIcon src={iconSrc} />
405
+ )}
406
+ {apps.map(({ id, href, label, icon: Icon }) => (
407
+ <a
408
+ key={id}
409
+ href={href}
410
+ aria-label={label}
411
+ aria-current={id === active ? "page" : undefined}
412
+ title={label}
413
+ className={[
414
+ "w-10 h-10 flex items-center justify-center rounded-sm",
415
+ id === active
416
+ ? "bg-primary/10 text-primary-dark"
417
+ : "text-text-muted hover:bg-surface-alt hover:text-text",
418
+ ].join(" ")}
419
+ >
420
+ <Icon size={20} aria-hidden="true" />
421
+ </a>
422
+ ))}
423
+ </nav>
424
+ )}
425
+ {!hideChrome && <BottomTabBar apps={apps} active={active} />}
426
+ <div className="flex-1 flex flex-col min-w-0">
427
+ {!hideChrome && !customHeader && (
428
+ <header ref={headerRef} className="rr-solid sticky top-0 z-30 h-16 shrink-0 bg-surface border-b border-border flex items-center justify-between gap-4 px-6">
429
+ <span className="font-display font-bold text-lg uppercase tracking-wide">{activeLabel}</span>
430
+ {renderUserMenu("down")}
431
+ </header>
432
+ )}
433
+ {/* The one pop-up stack for the whole app: right under the header row, so it never covers the account menu. */}
434
+ <NotificationCenter />
435
+ <div
436
+ id="app-content"
437
+ tabIndex={-1}
438
+ aria-busy={busy || undefined}
439
+ className={["flex-1 flex min-h-0 outline-none", hideChrome ? "" : "pb-14 md:pb-0"].join(" ")}
440
+ >
441
+ {children}
442
+ </div>
443
+ </div>
444
+ </div>
445
+ {!hideChrome && <FrameBrandingFooter parsed={footer} userMenu={menuInFooter ? renderUserMenu("up") : undefined} appTitle={activeLabel} />}
446
+ </div>
447
+ </ComposeProvider>
448
+ </UnlockPromptProvider>
449
+ <NotificationHistoryDialog open={historyOpen} onClose={() => setHistoryOpen(false)} />
450
+ </MailConnectionContext.Provider>
451
+ </ShortcutProvider>
452
+ </AppearanceProvider>
453
+ );
454
+ }
455
+
456
+ /**
457
+ * What a page's own shell (`MailShell`, `CalendarShell`, ...) renders around its content. Outside the client-side router it
458
+ * is the whole `AppChrome`, as it always was. Inside it (the router's app shell, `apps/www/_shell.tsx`) the chrome is already mounted above the
459
+ * page - and stays mounted as the page is replaced - so this is only its children; everything it would have been given comes from
460
+ * the shell instead (the props are the same for every page, and `active` is the route's).
461
+ */
462
+ export default function AppShell(props: PropsWithChildren<AppShellProps>) {
463
+ const inFrame = useInAppFrame();
464
+ return inFrame ? <>{props.children}</> : <AppChrome {...props} />;
465
+ }