@remit/web-client 0.0.1

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 (298) hide show
  1. package/harness/build.mjs +84 -0
  2. package/harness/index.html +37 -0
  3. package/harness/vite-preset.ts +29 -0
  4. package/index.html +47 -0
  5. package/package.json +147 -0
  6. package/public/config.js +5 -0
  7. package/public/locales/de/common.json +1 -0
  8. package/public/locales/de/errors.json +1 -0
  9. package/public/locales/de/mail.json +13 -0
  10. package/public/locales/de/settings.json +1 -0
  11. package/public/locales/en/common.json +26 -0
  12. package/public/locales/en/errors.json +1 -0
  13. package/public/locales/en/mail.json +69 -0
  14. package/public/locales/en/settings.json +1 -0
  15. package/public/locales/fr/common.json +1 -0
  16. package/public/locales/fr/errors.json +1 -0
  17. package/public/locales/fr/mail.json +13 -0
  18. package/public/locales/fr/settings.json +1 -0
  19. package/public/locales/nl/common.json +1 -0
  20. package/public/locales/nl/errors.json +1 -0
  21. package/public/locales/nl/mail.json +13 -0
  22. package/public/locales/nl/settings.json +1 -0
  23. package/src/auth/AccountMenu.tsx +58 -0
  24. package/src/auth/AccountSession.tsx +19 -0
  25. package/src/auth/BetterAuthShell.tsx +198 -0
  26. package/src/auth/account-menu-mode.test.ts +64 -0
  27. package/src/auth/account-menu-mode.ts +28 -0
  28. package/src/auth/amplify-config.test.ts +158 -0
  29. package/src/auth/amplify-config.ts +60 -0
  30. package/src/auth/auth-error.test.ts +109 -0
  31. package/src/auth/auth-error.ts +107 -0
  32. package/src/auth/auth-interceptor.test.ts +86 -0
  33. package/src/auth/auth-interceptor.ts +23 -0
  34. package/src/auth/auth.css +334 -0
  35. package/src/auth/better-auth/BetterAuthAccount.tsx +25 -0
  36. package/src/auth/better-auth-config.ts +85 -0
  37. package/src/auth/better-auth-provider.tsx +20 -0
  38. package/src/auth/cognito/CognitoAccount.tsx +48 -0
  39. package/src/auth/cognito/CognitoShell.tsx +266 -0
  40. package/src/auth/cognito/cognito-token.test.ts +110 -0
  41. package/src/auth/cognito/cognito-token.ts +47 -0
  42. package/src/auth/cognito/cognito.css +21 -0
  43. package/src/auth/cognito-provider.tsx +22 -0
  44. package/src/auth/combined-provider.tsx +42 -0
  45. package/src/auth/composition.test.ts +136 -0
  46. package/src/auth/provider.tsx +63 -0
  47. package/src/auth/sign-out-visibility.test.ts +40 -0
  48. package/src/auth/sign-out-visibility.ts +10 -0
  49. package/src/auth/sign-up-disabled.test.ts +23 -0
  50. package/src/components/compose/AddressField.tsx +237 -0
  51. package/src/components/compose/ComposeBody.tsx +36 -0
  52. package/src/components/compose/ComposeForm.tsx +676 -0
  53. package/src/components/compose/ComposeProvider.tsx +133 -0
  54. package/src/components/compose/ComposeSmtpMissingBanner.tsx +49 -0
  55. package/src/components/compose/FromSelector.tsx +67 -0
  56. package/src/components/compose/FullCompose.tsx +82 -0
  57. package/src/components/compose/InlineCompose.tsx +29 -0
  58. package/src/components/compose/MobileComposeSheet.tsx +152 -0
  59. package/src/components/compose/PlateEditor.tsx +86 -0
  60. package/src/components/compose/PlateToolbar.tsx +110 -0
  61. package/src/components/compose/SubjectField.tsx +19 -0
  62. package/src/components/compose/draft-discard-banner.test.ts +76 -0
  63. package/src/components/compose/index.ts +6 -0
  64. package/src/components/compose/sanitize-quote-html.ts +23 -0
  65. package/src/components/layout/AppShellSkeleton.tsx +53 -0
  66. package/src/components/layout/ComposeFab.tsx +86 -0
  67. package/src/components/layout/Drawer.tsx +105 -0
  68. package/src/components/mail/AutoMovedIndicator.tsx +48 -0
  69. package/src/components/mail/BlockedImagesNotice.render.test.ts +65 -0
  70. package/src/components/mail/BlockedImagesNotice.tsx +41 -0
  71. package/src/components/mail/BriefPane.tsx +289 -0
  72. package/src/components/mail/ConversationView.tsx +462 -0
  73. package/src/components/mail/DailyBrief.tsx +451 -0
  74. package/src/components/mail/DraftsView.tsx +279 -0
  75. package/src/components/mail/FlaggedList.tsx +170 -0
  76. package/src/components/mail/FlaggedPane.tsx +217 -0
  77. package/src/components/mail/IntelligencePane.test.ts +123 -0
  78. package/src/components/mail/IntelligencePane.tsx +441 -0
  79. package/src/components/mail/MailListHeader.tsx +187 -0
  80. package/src/components/mail/MailNav.tsx +70 -0
  81. package/src/components/mail/MailSidebarAdapter.tsx +351 -0
  82. package/src/components/mail/MailViewChrome.tsx +95 -0
  83. package/src/components/mail/MailboxPane.tsx +1219 -0
  84. package/src/components/mail/MessageActionMenu.tsx +368 -0
  85. package/src/components/mail/MessageBody.tsx +207 -0
  86. package/src/components/mail/MessageBodyErrorBanner.render.test.ts +85 -0
  87. package/src/components/mail/MessageBodyErrorBanner.test.ts +86 -0
  88. package/src/components/mail/MessageBodyErrorBanner.tsx +90 -0
  89. package/src/components/mail/MessageCard.tsx +401 -0
  90. package/src/components/mail/MessageDetail.tsx +153 -0
  91. package/src/components/mail/MessageList.tsx +804 -0
  92. package/src/components/mail/MessageListItem.tsx +267 -0
  93. package/src/components/mail/MessageToolbar.render.test.ts +57 -0
  94. package/src/components/mail/MessageToolbar.tsx +166 -0
  95. package/src/components/mail/MobileMessageBar.tsx +142 -0
  96. package/src/components/mail/MoveToTrigger.tsx +250 -0
  97. package/src/components/mail/NavMenuButton.tsx +25 -0
  98. package/src/components/mail/OutboxPane.tsx +531 -0
  99. package/src/components/mail/PullToRefresh.tsx +24 -0
  100. package/src/components/mail/RawMessageView.tsx +84 -0
  101. package/src/components/mail/ReadingPaneEmpty.render.test.ts +42 -0
  102. package/src/components/mail/SelectionToolbar.tsx +139 -0
  103. package/src/components/mail/SpamRescue.render.test.ts +35 -0
  104. package/src/components/mail/SpamRescue.tsx +138 -0
  105. package/src/components/mail/SwipeableMessageRow.tsx +148 -0
  106. package/src/components/mail/message-body-error-banner-content.ts +87 -0
  107. package/src/components/mail/organize/OrganizeDialog.render.test.ts +37 -0
  108. package/src/components/mail/organize/OrganizeDialog.tsx +83 -0
  109. package/src/components/mail/organize/OrganizePanel.render.test.ts +102 -0
  110. package/src/components/mail/organize/OrganizePanel.tsx +344 -0
  111. package/src/components/onboarding/OnboardingWizard.tsx +1547 -0
  112. package/src/components/settings/AccountFormPanel.test.ts +72 -0
  113. package/src/components/settings/AccountFormPanel.tsx +942 -0
  114. package/src/components/settings/DangerZone.tsx +30 -0
  115. package/src/components/settings/DeleteAccountDialog.render.test.ts +47 -0
  116. package/src/components/settings/DeleteAccountDialog.tsx +224 -0
  117. package/src/components/settings/FiltersList.render.test.ts +71 -0
  118. package/src/components/settings/FiltersList.tsx +93 -0
  119. package/src/components/settings/account-form-helpers.test.ts +172 -0
  120. package/src/components/settings/account-form-helpers.ts +105 -0
  121. package/src/components/ui/AppVersion.tsx +50 -0
  122. package/src/components/ui/BugReportButton.tsx +45 -0
  123. package/src/components/ui/ConfirmDialog.tsx +125 -0
  124. package/src/components/ui/DropdownMenu.tsx +105 -0
  125. package/src/components/ui/EmptyState.tsx +11 -0
  126. package/src/components/ui/ErrorBanner.tsx +88 -0
  127. package/src/components/ui/ErrorBannerProvider.tsx +77 -0
  128. package/src/components/ui/ErrorBannerStack.tsx +34 -0
  129. package/src/components/ui/ErrorState.tsx +80 -0
  130. package/src/components/ui/FatalErrorOverlay.render.test.ts +66 -0
  131. package/src/components/ui/FatalErrorOverlay.tsx +151 -0
  132. package/src/components/ui/KeyboardShortcutsModal.tsx +120 -0
  133. package/src/components/ui/SlidePanel.tsx +72 -0
  134. package/src/components/ui/error-banners.test.ts +207 -0
  135. package/src/components/ui/error-banners.ts +111 -0
  136. package/src/hooks/queries/keys.ts +38 -0
  137. package/src/hooks/useArchiveMailbox.ts +104 -0
  138. package/src/hooks/useAutoMovedBadge.ts +77 -0
  139. package/src/hooks/useCurrentMailboxName.ts +99 -0
  140. package/src/hooks/useDebouncedValue.ts +12 -0
  141. package/src/hooks/useDeleteMessages.test.ts +103 -0
  142. package/src/hooks/useDeleteMessages.ts +226 -0
  143. package/src/hooks/useFilters.ts +112 -0
  144. package/src/hooks/useIntelligenceData.test.ts +269 -0
  145. package/src/hooks/useIntelligenceData.ts +284 -0
  146. package/src/hooks/useIsDark.ts +27 -0
  147. package/src/hooks/useKeyboardNavigation.ts +192 -0
  148. package/src/hooks/useLayoutTier.test.ts +47 -0
  149. package/src/hooks/useLayoutTier.ts +44 -0
  150. package/src/hooks/useLongPress.test.ts +291 -0
  151. package/src/hooks/useLongPress.ts +87 -0
  152. package/src/hooks/useMailboxAccount.ts +62 -0
  153. package/src/hooks/useMailboxNameIndex.ts +32 -0
  154. package/src/hooks/useMarkAsRead.test.ts +165 -0
  155. package/src/hooks/useMarkAsRead.ts +319 -0
  156. package/src/hooks/useMediaQuery.ts +39 -0
  157. package/src/hooks/useMessageBodyContent.test.ts +370 -0
  158. package/src/hooks/useMessageBodyContent.ts +380 -0
  159. package/src/hooks/useMoveMessages.test.ts +63 -0
  160. package/src/hooks/useMoveMessages.ts +221 -0
  161. package/src/hooks/useOrganizeJob.ts +90 -0
  162. package/src/hooks/useOrganizePreview.ts +40 -0
  163. package/src/hooks/useRescueCandidates.test.ts +217 -0
  164. package/src/hooks/useRescueCandidates.ts +48 -0
  165. package/src/hooks/useSaveDraft.ts +118 -0
  166. package/src/hooks/useSelection.test.ts +75 -0
  167. package/src/hooks/useSelection.ts +211 -0
  168. package/src/hooks/useSemanticSearch.ts +94 -0
  169. package/src/hooks/useSignature.ts +69 -0
  170. package/src/hooks/useStaleAccountSync.test.ts +211 -0
  171. package/src/hooks/useStaleAccountSync.ts +246 -0
  172. package/src/hooks/useSwipeNavigation.test.ts +35 -0
  173. package/src/hooks/useSwipeNavigation.ts +99 -0
  174. package/src/hooks/useToggleStar.ts +187 -0
  175. package/src/hooks/useToggleTrusted.test.ts +102 -0
  176. package/src/hooks/useToggleTrusted.ts +161 -0
  177. package/src/hooks/useTriageKeyboard.ts +103 -0
  178. package/src/hooks/useTriggerSync.test.ts +24 -0
  179. package/src/hooks/useTriggerSync.ts +58 -0
  180. package/src/hooks/useUpdateAddressFlags.ts +125 -0
  181. package/src/hooks/useVisualViewport.test.ts +127 -0
  182. package/src/hooks/useVisualViewport.ts +51 -0
  183. package/src/index.css +19 -0
  184. package/src/lib/account-order.test.ts +84 -0
  185. package/src/lib/account-order.ts +13 -0
  186. package/src/lib/adjacent-message.test.ts +43 -0
  187. package/src/lib/adjacent-message.ts +20 -0
  188. package/src/lib/api.ts +82 -0
  189. package/src/lib/app-info.ts +15 -0
  190. package/src/lib/auto-moved.test.ts +140 -0
  191. package/src/lib/auto-moved.ts +68 -0
  192. package/src/lib/autodiscovery.test.ts +94 -0
  193. package/src/lib/autodiscovery.ts +354 -0
  194. package/src/lib/brief.test.ts +366 -0
  195. package/src/lib/brief.ts +180 -0
  196. package/src/lib/bug-report.test.ts +213 -0
  197. package/src/lib/bug-report.ts +176 -0
  198. package/src/lib/client.ts +36 -0
  199. package/src/lib/console-errors.test.ts +64 -0
  200. package/src/lib/console-errors.ts +92 -0
  201. package/src/lib/conversation-target.test.ts +68 -0
  202. package/src/lib/conversation-target.ts +59 -0
  203. package/src/lib/display-category.ts +20 -0
  204. package/src/lib/drafts.test.ts +241 -0
  205. package/src/lib/drafts.ts +121 -0
  206. package/src/lib/error-classifier.test.ts +133 -0
  207. package/src/lib/error-classifier.ts +96 -0
  208. package/src/lib/fatal-error.test.ts +97 -0
  209. package/src/lib/fatal-error.ts +165 -0
  210. package/src/lib/folder-roles.test.ts +113 -0
  211. package/src/lib/folder-roles.ts +112 -0
  212. package/src/lib/format.test.ts +115 -0
  213. package/src/lib/format.ts +198 -0
  214. package/src/lib/i18n.ts +36 -0
  215. package/src/lib/intelligence-pref.test.ts +57 -0
  216. package/src/lib/intelligence-pref.ts +25 -0
  217. package/src/lib/keymap-dispatch.test.ts +178 -0
  218. package/src/lib/keymap-dispatch.ts +187 -0
  219. package/src/lib/keymap.test.ts +49 -0
  220. package/src/lib/keymap.ts +178 -0
  221. package/src/lib/mail-context.ts +64 -0
  222. package/src/lib/mail-route.test.ts +80 -0
  223. package/src/lib/mail-route.ts +44 -0
  224. package/src/lib/message-body-source.test.ts +98 -0
  225. package/src/lib/message-body-source.ts +48 -0
  226. package/src/lib/move-targets.test.ts +195 -0
  227. package/src/lib/move-targets.ts +74 -0
  228. package/src/lib/onboarding-completion.test.ts +64 -0
  229. package/src/lib/onboarding-completion.ts +23 -0
  230. package/src/lib/organize/filter-status.test.ts +99 -0
  231. package/src/lib/organize/filter-status.ts +86 -0
  232. package/src/lib/organize/organize-copy.test.ts +93 -0
  233. package/src/lib/organize/organize-copy.ts +61 -0
  234. package/src/lib/organize/organize-model.test.ts +104 -0
  235. package/src/lib/organize/organize-model.ts +105 -0
  236. package/src/lib/organize/organize-poll.test.ts +37 -0
  237. package/src/lib/organize/organize-poll.ts +28 -0
  238. package/src/lib/outbox-status.test.ts +64 -0
  239. package/src/lib/outbox-status.ts +75 -0
  240. package/src/lib/plate-serializer.ts +164 -0
  241. package/src/lib/provider-presets.test.ts +96 -0
  242. package/src/lib/provider-presets.ts +81 -0
  243. package/src/lib/query-error-handler.test.ts +144 -0
  244. package/src/lib/query-error-handler.ts +34 -0
  245. package/src/lib/query-escalation.integration.test.ts +138 -0
  246. package/src/lib/recent-searches.test.ts +53 -0
  247. package/src/lib/recent-searches.ts +34 -0
  248. package/src/lib/rescue-candidates.test.ts +94 -0
  249. package/src/lib/rescue-candidates.ts +35 -0
  250. package/src/lib/rescue-telemetry.test.ts +75 -0
  251. package/src/lib/rescue-telemetry.ts +55 -0
  252. package/src/lib/rum-adapter.test.ts +299 -0
  253. package/src/lib/rum-adapter.ts +107 -0
  254. package/src/lib/saved-searches.test.ts +80 -0
  255. package/src/lib/saved-searches.ts +52 -0
  256. package/src/lib/search-pending.test.ts +61 -0
  257. package/src/lib/search-pending.ts +24 -0
  258. package/src/lib/search-query.test.ts +35 -0
  259. package/src/lib/search-query.ts +11 -0
  260. package/src/lib/search-result.test.ts +160 -0
  261. package/src/lib/search-result.ts +102 -0
  262. package/src/lib/search-schemas.ts +34 -0
  263. package/src/lib/search-token-index.test.ts +112 -0
  264. package/src/lib/search-token-index.ts +56 -0
  265. package/src/lib/search-tokens.test.ts +218 -0
  266. package/src/lib/search-tokens.ts +133 -0
  267. package/src/lib/telemetry-context.tsx +9 -0
  268. package/src/lib/telemetry.ts +17 -0
  269. package/src/lib/theme-preference.ts +46 -0
  270. package/src/lib/theme.ts +33 -0
  271. package/src/lib/utils.ts +4 -0
  272. package/src/main.tsx +10 -0
  273. package/src/routeTree.gen.ts +392 -0
  274. package/src/router.test.ts +81 -0
  275. package/src/router.tsx +31 -0
  276. package/src/routes/__root.tsx +69 -0
  277. package/src/routes/index.tsx +7 -0
  278. package/src/routes/mail/$mailboxId.tsx +27 -0
  279. package/src/routes/mail/flagged.tsx +37 -0
  280. package/src/routes/mail/index.tsx +41 -0
  281. package/src/routes/mail/outbox.tsx +13 -0
  282. package/src/routes/mail.tsx +413 -0
  283. package/src/routes/onboarding.tsx +35 -0
  284. package/src/routes/settings/-accounts.test.ts +65 -0
  285. package/src/routes/settings/accounts.tsx +542 -0
  286. package/src/routes/settings/advanced.tsx +55 -0
  287. package/src/routes/settings/appearance.tsx +140 -0
  288. package/src/routes/settings/filters.tsx +141 -0
  289. package/src/routes/settings/folders.tsx +216 -0
  290. package/src/routes/settings/index.tsx +5 -0
  291. package/src/routes/settings/senders.tsx +466 -0
  292. package/src/routes/settings/suggested-vips.tsx +10 -0
  293. package/src/routes/settings.tsx +66 -0
  294. package/src/runtime-config.ts +75 -0
  295. package/src/shell/index.tsx +98 -0
  296. package/src/types/index.ts +73 -0
  297. package/src/vite-env.d.ts +4 -0
  298. package/vite.base.ts +43 -0
@@ -0,0 +1,392 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as IndexRouteImport } from './routes/index'
13
+ import { Route as MailRouteImport } from './routes/mail'
14
+ import { Route as OnboardingRouteImport } from './routes/onboarding'
15
+ import { Route as SettingsRouteImport } from './routes/settings'
16
+ import { Route as MailIndexRouteImport } from './routes/mail/index'
17
+ import { Route as MailMailboxIdRouteImport } from './routes/mail/$mailboxId'
18
+ import { Route as MailFlaggedRouteImport } from './routes/mail/flagged'
19
+ import { Route as MailOutboxRouteImport } from './routes/mail/outbox'
20
+ import { Route as SettingsIndexRouteImport } from './routes/settings/index'
21
+ import { Route as SettingsAccountsRouteImport } from './routes/settings/accounts'
22
+ import { Route as SettingsAdvancedRouteImport } from './routes/settings/advanced'
23
+ import { Route as SettingsAppearanceRouteImport } from './routes/settings/appearance'
24
+ import { Route as SettingsFiltersRouteImport } from './routes/settings/filters'
25
+ import { Route as SettingsFoldersRouteImport } from './routes/settings/folders'
26
+ import { Route as SettingsSendersRouteImport } from './routes/settings/senders'
27
+ import { Route as SettingsSuggestedVipsRouteImport } from './routes/settings/suggested-vips'
28
+
29
+ const IndexRoute = IndexRouteImport.update({
30
+ id: '/',
31
+ path: '/',
32
+ getParentRoute: () => rootRouteImport,
33
+ } as any)
34
+ const MailRoute = MailRouteImport.update({
35
+ id: '/mail',
36
+ path: '/mail',
37
+ getParentRoute: () => rootRouteImport,
38
+ } as any)
39
+ const OnboardingRoute = OnboardingRouteImport.update({
40
+ id: '/onboarding',
41
+ path: '/onboarding',
42
+ getParentRoute: () => rootRouteImport,
43
+ } as any)
44
+ const SettingsRoute = SettingsRouteImport.update({
45
+ id: '/settings',
46
+ path: '/settings',
47
+ getParentRoute: () => rootRouteImport,
48
+ } as any)
49
+ const MailIndexRoute = MailIndexRouteImport.update({
50
+ id: '/',
51
+ path: '/',
52
+ getParentRoute: () => MailRoute,
53
+ } as any)
54
+ const MailMailboxIdRoute = MailMailboxIdRouteImport.update({
55
+ id: '/$mailboxId',
56
+ path: '/$mailboxId',
57
+ getParentRoute: () => MailRoute,
58
+ } as any)
59
+ const MailFlaggedRoute = MailFlaggedRouteImport.update({
60
+ id: '/flagged',
61
+ path: '/flagged',
62
+ getParentRoute: () => MailRoute,
63
+ } as any)
64
+ const MailOutboxRoute = MailOutboxRouteImport.update({
65
+ id: '/outbox',
66
+ path: '/outbox',
67
+ getParentRoute: () => MailRoute,
68
+ } as any)
69
+ const SettingsIndexRoute = SettingsIndexRouteImport.update({
70
+ id: '/',
71
+ path: '/',
72
+ getParentRoute: () => SettingsRoute,
73
+ } as any)
74
+ const SettingsAccountsRoute = SettingsAccountsRouteImport.update({
75
+ id: '/accounts',
76
+ path: '/accounts',
77
+ getParentRoute: () => SettingsRoute,
78
+ } as any)
79
+ const SettingsAdvancedRoute = SettingsAdvancedRouteImport.update({
80
+ id: '/advanced',
81
+ path: '/advanced',
82
+ getParentRoute: () => SettingsRoute,
83
+ } as any)
84
+ const SettingsAppearanceRoute = SettingsAppearanceRouteImport.update({
85
+ id: '/appearance',
86
+ path: '/appearance',
87
+ getParentRoute: () => SettingsRoute,
88
+ } as any)
89
+ const SettingsFiltersRoute = SettingsFiltersRouteImport.update({
90
+ id: '/filters',
91
+ path: '/filters',
92
+ getParentRoute: () => SettingsRoute,
93
+ } as any)
94
+ const SettingsFoldersRoute = SettingsFoldersRouteImport.update({
95
+ id: '/folders',
96
+ path: '/folders',
97
+ getParentRoute: () => SettingsRoute,
98
+ } as any)
99
+ const SettingsSendersRoute = SettingsSendersRouteImport.update({
100
+ id: '/senders',
101
+ path: '/senders',
102
+ getParentRoute: () => SettingsRoute,
103
+ } as any)
104
+ const SettingsSuggestedVipsRoute = SettingsSuggestedVipsRouteImport.update({
105
+ id: '/suggested-vips',
106
+ path: '/suggested-vips',
107
+ getParentRoute: () => SettingsRoute,
108
+ } as any)
109
+
110
+ export interface FileRoutesByFullPath {
111
+ '/': typeof IndexRoute
112
+ '/mail': typeof MailRouteWithChildren
113
+ '/onboarding': typeof OnboardingRoute
114
+ '/settings': typeof SettingsRouteWithChildren
115
+ '/mail/$mailboxId': typeof MailMailboxIdRoute
116
+ '/mail/flagged': typeof MailFlaggedRoute
117
+ '/mail/outbox': typeof MailOutboxRoute
118
+ '/settings/accounts': typeof SettingsAccountsRoute
119
+ '/settings/advanced': typeof SettingsAdvancedRoute
120
+ '/settings/appearance': typeof SettingsAppearanceRoute
121
+ '/settings/filters': typeof SettingsFiltersRoute
122
+ '/settings/folders': typeof SettingsFoldersRoute
123
+ '/settings/senders': typeof SettingsSendersRoute
124
+ '/settings/suggested-vips': typeof SettingsSuggestedVipsRoute
125
+ '/mail/': typeof MailIndexRoute
126
+ '/settings/': typeof SettingsIndexRoute
127
+ }
128
+ export interface FileRoutesByTo {
129
+ '/': typeof IndexRoute
130
+ '/onboarding': typeof OnboardingRoute
131
+ '/mail/$mailboxId': typeof MailMailboxIdRoute
132
+ '/mail/flagged': typeof MailFlaggedRoute
133
+ '/mail/outbox': typeof MailOutboxRoute
134
+ '/settings/accounts': typeof SettingsAccountsRoute
135
+ '/settings/advanced': typeof SettingsAdvancedRoute
136
+ '/settings/appearance': typeof SettingsAppearanceRoute
137
+ '/settings/filters': typeof SettingsFiltersRoute
138
+ '/settings/folders': typeof SettingsFoldersRoute
139
+ '/settings/senders': typeof SettingsSendersRoute
140
+ '/settings/suggested-vips': typeof SettingsSuggestedVipsRoute
141
+ '/mail': typeof MailIndexRoute
142
+ '/settings': typeof SettingsIndexRoute
143
+ }
144
+ export interface FileRoutesById {
145
+ __root__: typeof rootRouteImport
146
+ '/': typeof IndexRoute
147
+ '/mail': typeof MailRouteWithChildren
148
+ '/onboarding': typeof OnboardingRoute
149
+ '/settings': typeof SettingsRouteWithChildren
150
+ '/mail/$mailboxId': typeof MailMailboxIdRoute
151
+ '/mail/flagged': typeof MailFlaggedRoute
152
+ '/mail/outbox': typeof MailOutboxRoute
153
+ '/settings/accounts': typeof SettingsAccountsRoute
154
+ '/settings/advanced': typeof SettingsAdvancedRoute
155
+ '/settings/appearance': typeof SettingsAppearanceRoute
156
+ '/settings/filters': typeof SettingsFiltersRoute
157
+ '/settings/folders': typeof SettingsFoldersRoute
158
+ '/settings/senders': typeof SettingsSendersRoute
159
+ '/settings/suggested-vips': typeof SettingsSuggestedVipsRoute
160
+ '/mail/': typeof MailIndexRoute
161
+ '/settings/': typeof SettingsIndexRoute
162
+ }
163
+ export interface FileRouteTypes {
164
+ fileRoutesByFullPath: FileRoutesByFullPath
165
+ fullPaths:
166
+ | '/'
167
+ | '/mail'
168
+ | '/onboarding'
169
+ | '/settings'
170
+ | '/mail/$mailboxId'
171
+ | '/mail/flagged'
172
+ | '/mail/outbox'
173
+ | '/settings/accounts'
174
+ | '/settings/advanced'
175
+ | '/settings/appearance'
176
+ | '/settings/filters'
177
+ | '/settings/folders'
178
+ | '/settings/senders'
179
+ | '/settings/suggested-vips'
180
+ | '/mail/'
181
+ | '/settings/'
182
+ fileRoutesByTo: FileRoutesByTo
183
+ to:
184
+ | '/'
185
+ | '/onboarding'
186
+ | '/mail/$mailboxId'
187
+ | '/mail/flagged'
188
+ | '/mail/outbox'
189
+ | '/settings/accounts'
190
+ | '/settings/advanced'
191
+ | '/settings/appearance'
192
+ | '/settings/filters'
193
+ | '/settings/folders'
194
+ | '/settings/senders'
195
+ | '/settings/suggested-vips'
196
+ | '/mail'
197
+ | '/settings'
198
+ id:
199
+ | '__root__'
200
+ | '/'
201
+ | '/mail'
202
+ | '/onboarding'
203
+ | '/settings'
204
+ | '/mail/$mailboxId'
205
+ | '/mail/flagged'
206
+ | '/mail/outbox'
207
+ | '/settings/accounts'
208
+ | '/settings/advanced'
209
+ | '/settings/appearance'
210
+ | '/settings/filters'
211
+ | '/settings/folders'
212
+ | '/settings/senders'
213
+ | '/settings/suggested-vips'
214
+ | '/mail/'
215
+ | '/settings/'
216
+ fileRoutesById: FileRoutesById
217
+ }
218
+ export interface RootRouteChildren {
219
+ IndexRoute: typeof IndexRoute
220
+ MailRoute: typeof MailRouteWithChildren
221
+ OnboardingRoute: typeof OnboardingRoute
222
+ SettingsRoute: typeof SettingsRouteWithChildren
223
+ }
224
+
225
+ declare module '@tanstack/react-router' {
226
+ interface FileRoutesByPath {
227
+ '/': {
228
+ id: '/'
229
+ path: '/'
230
+ fullPath: '/'
231
+ preLoaderRoute: typeof IndexRouteImport
232
+ parentRoute: typeof rootRouteImport
233
+ }
234
+ '/mail': {
235
+ id: '/mail'
236
+ path: '/mail'
237
+ fullPath: '/mail'
238
+ preLoaderRoute: typeof MailRouteImport
239
+ parentRoute: typeof rootRouteImport
240
+ }
241
+ '/onboarding': {
242
+ id: '/onboarding'
243
+ path: '/onboarding'
244
+ fullPath: '/onboarding'
245
+ preLoaderRoute: typeof OnboardingRouteImport
246
+ parentRoute: typeof rootRouteImport
247
+ }
248
+ '/settings': {
249
+ id: '/settings'
250
+ path: '/settings'
251
+ fullPath: '/settings'
252
+ preLoaderRoute: typeof SettingsRouteImport
253
+ parentRoute: typeof rootRouteImport
254
+ }
255
+ '/mail/': {
256
+ id: '/mail/'
257
+ path: '/'
258
+ fullPath: '/mail/'
259
+ preLoaderRoute: typeof MailIndexRouteImport
260
+ parentRoute: typeof MailRoute
261
+ }
262
+ '/mail/$mailboxId': {
263
+ id: '/mail/$mailboxId'
264
+ path: '/$mailboxId'
265
+ fullPath: '/mail/$mailboxId'
266
+ preLoaderRoute: typeof MailMailboxIdRouteImport
267
+ parentRoute: typeof MailRoute
268
+ }
269
+ '/mail/flagged': {
270
+ id: '/mail/flagged'
271
+ path: '/flagged'
272
+ fullPath: '/mail/flagged'
273
+ preLoaderRoute: typeof MailFlaggedRouteImport
274
+ parentRoute: typeof MailRoute
275
+ }
276
+ '/mail/outbox': {
277
+ id: '/mail/outbox'
278
+ path: '/outbox'
279
+ fullPath: '/mail/outbox'
280
+ preLoaderRoute: typeof MailOutboxRouteImport
281
+ parentRoute: typeof MailRoute
282
+ }
283
+ '/settings/': {
284
+ id: '/settings/'
285
+ path: '/'
286
+ fullPath: '/settings/'
287
+ preLoaderRoute: typeof SettingsIndexRouteImport
288
+ parentRoute: typeof SettingsRoute
289
+ }
290
+ '/settings/accounts': {
291
+ id: '/settings/accounts'
292
+ path: '/accounts'
293
+ fullPath: '/settings/accounts'
294
+ preLoaderRoute: typeof SettingsAccountsRouteImport
295
+ parentRoute: typeof SettingsRoute
296
+ }
297
+ '/settings/advanced': {
298
+ id: '/settings/advanced'
299
+ path: '/advanced'
300
+ fullPath: '/settings/advanced'
301
+ preLoaderRoute: typeof SettingsAdvancedRouteImport
302
+ parentRoute: typeof SettingsRoute
303
+ }
304
+ '/settings/appearance': {
305
+ id: '/settings/appearance'
306
+ path: '/appearance'
307
+ fullPath: '/settings/appearance'
308
+ preLoaderRoute: typeof SettingsAppearanceRouteImport
309
+ parentRoute: typeof SettingsRoute
310
+ }
311
+ '/settings/filters': {
312
+ id: '/settings/filters'
313
+ path: '/filters'
314
+ fullPath: '/settings/filters'
315
+ preLoaderRoute: typeof SettingsFiltersRouteImport
316
+ parentRoute: typeof SettingsRoute
317
+ }
318
+ '/settings/folders': {
319
+ id: '/settings/folders'
320
+ path: '/folders'
321
+ fullPath: '/settings/folders'
322
+ preLoaderRoute: typeof SettingsFoldersRouteImport
323
+ parentRoute: typeof SettingsRoute
324
+ }
325
+ '/settings/senders': {
326
+ id: '/settings/senders'
327
+ path: '/senders'
328
+ fullPath: '/settings/senders'
329
+ preLoaderRoute: typeof SettingsSendersRouteImport
330
+ parentRoute: typeof SettingsRoute
331
+ }
332
+ '/settings/suggested-vips': {
333
+ id: '/settings/suggested-vips'
334
+ path: '/suggested-vips'
335
+ fullPath: '/settings/suggested-vips'
336
+ preLoaderRoute: typeof SettingsSuggestedVipsRouteImport
337
+ parentRoute: typeof SettingsRoute
338
+ }
339
+ }
340
+ }
341
+
342
+ interface MailRouteChildren {
343
+ MailMailboxIdRoute: typeof MailMailboxIdRoute
344
+ MailFlaggedRoute: typeof MailFlaggedRoute
345
+ MailOutboxRoute: typeof MailOutboxRoute
346
+ MailIndexRoute: typeof MailIndexRoute
347
+ }
348
+
349
+ const MailRouteChildren: MailRouteChildren = {
350
+ MailMailboxIdRoute: MailMailboxIdRoute,
351
+ MailFlaggedRoute: MailFlaggedRoute,
352
+ MailOutboxRoute: MailOutboxRoute,
353
+ MailIndexRoute: MailIndexRoute,
354
+ }
355
+
356
+ const MailRouteWithChildren = MailRoute._addFileChildren(MailRouteChildren)
357
+
358
+ interface SettingsRouteChildren {
359
+ SettingsAccountsRoute: typeof SettingsAccountsRoute
360
+ SettingsAdvancedRoute: typeof SettingsAdvancedRoute
361
+ SettingsAppearanceRoute: typeof SettingsAppearanceRoute
362
+ SettingsFiltersRoute: typeof SettingsFiltersRoute
363
+ SettingsFoldersRoute: typeof SettingsFoldersRoute
364
+ SettingsSendersRoute: typeof SettingsSendersRoute
365
+ SettingsSuggestedVipsRoute: typeof SettingsSuggestedVipsRoute
366
+ SettingsIndexRoute: typeof SettingsIndexRoute
367
+ }
368
+
369
+ const SettingsRouteChildren: SettingsRouteChildren = {
370
+ SettingsAccountsRoute: SettingsAccountsRoute,
371
+ SettingsAdvancedRoute: SettingsAdvancedRoute,
372
+ SettingsAppearanceRoute: SettingsAppearanceRoute,
373
+ SettingsFiltersRoute: SettingsFiltersRoute,
374
+ SettingsFoldersRoute: SettingsFoldersRoute,
375
+ SettingsSendersRoute: SettingsSendersRoute,
376
+ SettingsSuggestedVipsRoute: SettingsSuggestedVipsRoute,
377
+ SettingsIndexRoute: SettingsIndexRoute,
378
+ }
379
+
380
+ const SettingsRouteWithChildren = SettingsRoute._addFileChildren(
381
+ SettingsRouteChildren,
382
+ )
383
+
384
+ const rootRouteChildren: RootRouteChildren = {
385
+ IndexRoute: IndexRoute,
386
+ MailRoute: MailRouteWithChildren,
387
+ OnboardingRoute: OnboardingRoute,
388
+ SettingsRoute: SettingsRouteWithChildren,
389
+ }
390
+ export const routeTree = rootRouteImport
391
+ ._addFileChildren(rootRouteChildren)
392
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,81 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import type { Telemetry } from "./lib/telemetry";
4
+
5
+ interface SubscribeCall {
6
+ eventType: string;
7
+ fn: (event: { toLocation: { pathname: string } }) => void;
8
+ }
9
+
10
+ interface RouterStub {
11
+ subscribeCalls: SubscribeCall[];
12
+ subscribe(
13
+ eventType: string,
14
+ fn: (event: { toLocation: { pathname: string } }) => void,
15
+ ): () => void;
16
+ }
17
+
18
+ function makeRouterStub(): RouterStub {
19
+ const stub: RouterStub = {
20
+ subscribeCalls: [],
21
+ subscribe(eventType, fn) {
22
+ stub.subscribeCalls.push({ eventType, fn });
23
+ return () => undefined;
24
+ },
25
+ };
26
+ return stub;
27
+ }
28
+
29
+ function makeTelemetryStub(): Telemetry & { pageViews: string[] } {
30
+ const stub = {
31
+ pageViews: [] as string[],
32
+ recordPageView(path: string) {
33
+ stub.pageViews.push(path);
34
+ },
35
+ recordError: () => undefined,
36
+ recordEvent: () => undefined,
37
+ recordTiming: () => undefined,
38
+ };
39
+ return stub;
40
+ }
41
+
42
+ function wirePageViews(router: RouterStub, telemetry: Telemetry): void {
43
+ router.subscribe("onResolved", (event) => {
44
+ telemetry.recordPageView(event.toLocation.pathname);
45
+ });
46
+ }
47
+
48
+ describe("router page-view telemetry wiring", () => {
49
+ it("subscribes to onResolved", () => {
50
+ const router = makeRouterStub();
51
+ const telemetry = makeTelemetryStub();
52
+ wirePageViews(router, telemetry);
53
+ assert.equal(router.subscribeCalls.length, 1);
54
+ assert.equal(router.subscribeCalls[0]?.eventType, "onResolved");
55
+ });
56
+
57
+ it("calls recordPageView with pathname on each navigation", () => {
58
+ const router = makeRouterStub();
59
+ const telemetry = makeTelemetryStub();
60
+ wirePageViews(router, telemetry);
61
+
62
+ const handler = router.subscribeCalls[0]?.fn;
63
+ assert.ok(handler, "Expected a subscribe handler");
64
+
65
+ handler({ toLocation: { pathname: "/mail" } });
66
+ handler({ toLocation: { pathname: "/settings" } });
67
+
68
+ assert.deepEqual(telemetry.pageViews, ["/mail", "/settings"]);
69
+ });
70
+
71
+ it("is a no-op when telemetry is noopTelemetry (does not throw)", () => {
72
+ const router = makeRouterStub();
73
+ const telemetry = makeTelemetryStub();
74
+ telemetry.recordPageView = () => undefined;
75
+ wirePageViews(router, telemetry);
76
+
77
+ const handler = router.subscribeCalls[0]?.fn;
78
+ assert.ok(handler);
79
+ assert.doesNotThrow(() => handler({ toLocation: { pathname: "/mail" } }));
80
+ });
81
+ });
package/src/router.tsx ADDED
@@ -0,0 +1,31 @@
1
+ import type { QueryClient } from "@tanstack/react-query";
2
+ import { createRouter } from "@tanstack/react-router";
3
+ import type { Telemetry } from "./lib/telemetry";
4
+ import { routeTree } from "./routeTree.gen";
5
+
6
+ export interface RouterContext {
7
+ queryClient: QueryClient;
8
+ }
9
+
10
+ export const createAppRouter = (
11
+ queryClient: QueryClient,
12
+ telemetry: Telemetry,
13
+ ) => {
14
+ const router = createRouter({
15
+ routeTree,
16
+ context: { queryClient },
17
+ defaultPreload: "intent",
18
+ });
19
+
20
+ router.subscribe("onResolved", (event) => {
21
+ telemetry.recordPageView(event.toLocation.pathname);
22
+ });
23
+
24
+ return router;
25
+ };
26
+
27
+ declare module "@tanstack/react-router" {
28
+ interface Register {
29
+ router: ReturnType<typeof createAppRouter>;
30
+ }
31
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ createRootRouteWithContext,
3
+ type ErrorComponentProps,
4
+ Outlet,
5
+ } from "@tanstack/react-router";
6
+ import { Suspense } from "react";
7
+ import { useTranslation } from "react-i18next";
8
+ import { ComposeProvider } from "@/components/compose/ComposeProvider";
9
+ import { AppShellSkeleton } from "@/components/layout/AppShellSkeleton";
10
+ import { ErrorBannerProvider } from "@/components/ui/ErrorBannerProvider";
11
+ import {
12
+ FatalErrorOverlay,
13
+ FatalErrorScreen,
14
+ } from "@/components/ui/FatalErrorOverlay";
15
+ import { isServerError } from "@/lib/error-classifier";
16
+ import { reportFatalError } from "@/lib/fatal-error";
17
+ import type { RouterContext } from "@/router";
18
+
19
+ const RootErrorComponent = ({ error, reset, info }: ErrorComponentProps) => {
20
+ // Everything that bubbles to the route boundary escalates to the loud
21
+ // full-screen fatal page — never the soft grey "Something went wrong" that
22
+ // implied recovery. A bubbled 5xx is transient (Retry re-runs the loader via
23
+ // reset); a caught render exception is deterministic — retry re-crashes, so
24
+ // it is fatal with no Retry, offering a safe route out instead (issue #1231).
25
+ const recoverable = isServerError(error);
26
+ const fatal = reportFatalError(error, {
27
+ recoverable,
28
+ componentStack: info?.componentStack,
29
+ });
30
+ return (
31
+ <FatalErrorScreen fatal={fatal} onRetry={recoverable ? reset : undefined} />
32
+ );
33
+ };
34
+
35
+ export const Route = createRootRouteWithContext<RouterContext>()({
36
+ component: RootLayout,
37
+ errorComponent: RootErrorComponent,
38
+ });
39
+
40
+ const SkipLink = () => {
41
+ // Provide a string fallback so Suspense doesn't render the raw i18n key
42
+ // while the common namespace is still loading over HTTP.
43
+ const { t } = useTranslation("common", { useSuspense: false });
44
+
45
+ return (
46
+ <a
47
+ href="#main-content"
48
+ className="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:p-4 focus:bg-canvas focus:text-fg focus:rounded-md"
49
+ >
50
+ {t("accessibility.skipToContent", "Skip to main content")}
51
+ </a>
52
+ );
53
+ };
54
+
55
+ function RootLayout() {
56
+ return (
57
+ <ErrorBannerProvider>
58
+ <ComposeProvider>
59
+ <SkipLink />
60
+ <main id="main-content" className="h-dvh overflow-hidden">
61
+ <Suspense fallback={<AppShellSkeleton />}>
62
+ <Outlet />
63
+ </Suspense>
64
+ </main>
65
+ </ComposeProvider>
66
+ <FatalErrorOverlay />
67
+ </ErrorBannerProvider>
68
+ );
69
+ }
@@ -0,0 +1,7 @@
1
+ import { createFileRoute, redirect } from "@tanstack/react-router";
2
+
3
+ export const Route = createFileRoute("/")({
4
+ beforeLoad: () => {
5
+ throw redirect({ to: "/mail" });
6
+ },
7
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * /mail/$mailboxId route — shell activation only.
3
+ *
4
+ * The component returns null because rendering is lifted to `mail.tsx`
5
+ * which reads the active mailboxId via `useRouterState` and mounts
6
+ * `<MailboxPane>` directly into `<AppShellSlotted>` slots. TanStack Router
7
+ * still needs this file to:
8
+ * - Register the route so URL matching works
9
+ * - Validate the search params so selectedMessageId is properly typed
10
+ */
11
+ import { createFileRoute } from "@tanstack/react-router";
12
+ import { z } from "zod";
13
+
14
+ // Search schema includes q from parent route for proper inheritance
15
+ const mailboxSearchSchema = z.object({
16
+ selectedMessageId: z.string().optional(),
17
+ // A tapped semantic "Related" hit can point at a message outside the loaded
18
+ // list; carrying its thread lets the mailbox open it directly (the mailbox is
19
+ // the route param). See `buildConversationTarget`.
20
+ selectedThreadId: z.string().optional(),
21
+ q: z.string().optional(),
22
+ });
23
+
24
+ export const Route = createFileRoute("/mail/$mailboxId")({
25
+ component: () => null,
26
+ validateSearch: mailboxSearchSchema,
27
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * /mail/flagged route (Flagged virtual mailbox) — shell activation only.
3
+ *
4
+ * The component returns null because rendering is lifted to `mail.tsx`, which
5
+ * detects the flagged route via `useRouterState` and mounts `<FlaggedPane>`
6
+ * directly into `<AppShellSlotted>` slots — the same pattern the brief uses.
7
+ */
8
+ import {
9
+ createFileRoute,
10
+ type ErrorComponentProps,
11
+ } from "@tanstack/react-router";
12
+ import { z } from "zod";
13
+ import { ErrorState } from "@/components/ui/ErrorState";
14
+
15
+ const FlaggedError = ({ error, reset }: ErrorComponentProps) => (
16
+ <div className="flex h-full items-center justify-center bg-canvas p-4">
17
+ <ErrorState
18
+ title="Couldn't load your flagged mail"
19
+ error={error}
20
+ onRetry={reset}
21
+ />
22
+ </div>
23
+ );
24
+
25
+ // `q` is inherited from the parent /mail route; re-declared here so it survives
26
+ // this route's own search validation and isn't dropped when navigating with a
27
+ // functional search updater.
28
+ const flaggedSearchSchema = z.object({
29
+ selectedMessageId: z.string().optional(),
30
+ q: z.string().optional(),
31
+ });
32
+
33
+ export const Route = createFileRoute("/mail/flagged")({
34
+ component: () => null,
35
+ validateSearch: flaggedSearchSchema,
36
+ errorComponent: FlaggedError,
37
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * /mail/ route (Daily Brief) — shell activation only.
3
+ *
4
+ * The component returns null because rendering is lifted to `mail.tsx`
5
+ * which detects the brief route via `useRouterState` and mounts
6
+ * `<BriefPane>` directly into `<AppShellSlotted>` slots.
7
+ */
8
+ import {
9
+ createFileRoute,
10
+ type ErrorComponentProps,
11
+ } from "@tanstack/react-router";
12
+ import { z } from "zod";
13
+ import { ErrorState } from "@/components/ui/ErrorState";
14
+
15
+ const MailIndexError = ({ error, reset }: ErrorComponentProps) => (
16
+ <div className="flex h-full items-center justify-center bg-canvas p-4">
17
+ <ErrorState
18
+ title="Couldn't load your mailboxes"
19
+ error={error}
20
+ onRetry={reset}
21
+ />
22
+ </div>
23
+ );
24
+
25
+ // `q` is inherited from the parent /mail route; re-declared here so it
26
+ // survives this route's own search validation and isn't dropped when
27
+ // navigating with a functional search updater.
28
+ const briefSearchSchema = z.object({
29
+ selectedMessageId: z.string().optional(),
30
+ // A tapped semantic "Related" hit can point at a message outside the loaded
31
+ // brief list; carrying its thread + mailbox lets the brief open it directly.
32
+ selectedThreadId: z.string().optional(),
33
+ selectedMailboxId: z.string().optional(),
34
+ q: z.string().optional(),
35
+ });
36
+
37
+ export const Route = createFileRoute("/mail/")({
38
+ component: () => null,
39
+ validateSearch: briefSearchSchema,
40
+ errorComponent: MailIndexError,
41
+ });