@arbidocs/react 0.5.110 → 0.5.112

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 (1011) hide show
  1. package/dist/PdfViewerImpl-QUSDLNNM.cjs +380 -0
  2. package/dist/PdfViewerImpl-QUSDLNNM.cjs.map +1 -0
  3. package/dist/{PdfViewer.d-bQH9yxFg.d.cts → PdfViewerImpl.d-B28hLEuj.d.ts} +8 -1
  4. package/dist/account/client.js +96 -0
  5. package/dist/account/client.js.map +7 -0
  6. package/dist/account/delete.js +70 -0
  7. package/dist/account/delete.js.map +7 -0
  8. package/dist/account/didit.js +34 -0
  9. package/dist/account/didit.js.map +7 -0
  10. package/dist/account/dismissed-tips.js +33 -0
  11. package/dist/account/dismissed-tips.js.map +7 -0
  12. package/dist/account/email.js +84 -0
  13. package/dist/account/email.js.map +7 -0
  14. package/dist/account/entitlements.js +47 -0
  15. package/dist/account/entitlements.js.map +7 -0
  16. package/dist/account/identity.js +270 -0
  17. package/dist/account/identity.js.map +7 -0
  18. package/dist/account/index.js +93 -0
  19. package/dist/account/index.js.map +7 -0
  20. package/dist/account/notifications.js +59 -0
  21. package/dist/account/notifications.js.map +7 -0
  22. package/dist/account/password.js +103 -0
  23. package/dist/account/password.js.map +7 -0
  24. package/dist/account/profile.js +116 -0
  25. package/dist/account/profile.js.map +7 -0
  26. package/dist/account/recovery.js +13 -0
  27. package/dist/account/recovery.js.map +7 -0
  28. package/dist/account/relevance.js +82 -0
  29. package/dist/account/relevance.js.map +7 -0
  30. package/dist/account/sessions.js +37 -0
  31. package/dist/account/sessions.js.map +7 -0
  32. package/dist/account/settings.js +39 -0
  33. package/dist/account/settings.js.map +7 -0
  34. package/dist/account/table-views.js +16 -0
  35. package/dist/account/table-views.js.map +7 -0
  36. package/dist/ai/AiAnswer.js +83 -0
  37. package/dist/ai/AiAnswer.js.map +7 -0
  38. package/dist/ai/AiMarkdown.js +40 -0
  39. package/dist/ai/AiMarkdown.js.map +7 -0
  40. package/dist/ai/ArtifactCanvas.js +193 -0
  41. package/dist/ai/ArtifactCanvas.js.map +7 -0
  42. package/dist/ai/CitationRenderer.js +413 -0
  43. package/dist/ai/CitationRenderer.js.map +7 -0
  44. package/dist/{chunk-WL2Y2T47.js → ai/MarkdownRenderer.js} +35 -101
  45. package/dist/ai/MarkdownRenderer.js.map +7 -0
  46. package/dist/ai/MessageArtifactCard.js +75 -0
  47. package/dist/ai/MessageArtifactCard.js.map +7 -0
  48. package/dist/ai/PassageCard.js +79 -0
  49. package/dist/ai/PassageCard.js.map +7 -0
  50. package/dist/ai/engine.js +110 -0
  51. package/dist/ai/engine.js.map +7 -0
  52. package/dist/ai/index.js +63 -0
  53. package/dist/ai/index.js.map +7 -0
  54. package/dist/ai/markdown-base-components.js +37 -0
  55. package/dist/ai/markdown-base-components.js.map +7 -0
  56. package/dist/ai/markdown-streaming-context.js +6 -0
  57. package/dist/ai/markdown-streaming-context.js.map +7 -0
  58. package/dist/{chunk-VSCU7K4V.js → ai/normalize-citations.js} +12 -19
  59. package/dist/ai/normalize-citations.js.map +7 -0
  60. package/dist/ai/redline.js +41 -0
  61. package/dist/ai/redline.js.map +7 -0
  62. package/dist/ai/split-markdown-blocks.js +40 -0
  63. package/dist/ai/split-markdown-blocks.js.map +7 -0
  64. package/dist/{chunk-UW2SGTI5.js → ai/stored-markdown.js} +41 -93
  65. package/dist/ai/stored-markdown.js.map +7 -0
  66. package/dist/annotations/AnnotationPageOverlay.js +807 -0
  67. package/dist/annotations/AnnotationPageOverlay.js.map +7 -0
  68. package/dist/annotations/AnnotationToolbar.js +398 -0
  69. package/dist/annotations/AnnotationToolbar.js.map +7 -0
  70. package/dist/annotations/FreezeDialog.js +170 -0
  71. package/dist/annotations/FreezeDialog.js.map +7 -0
  72. package/dist/annotations/ProductionDialog.js +354 -0
  73. package/dist/annotations/ProductionDialog.js.map +7 -0
  74. package/dist/annotations/controller.js +49 -0
  75. package/dist/annotations/controller.js.map +7 -0
  76. package/dist/annotations/freeze/browser.js +21 -0
  77. package/dist/annotations/freeze/browser.js.map +7 -0
  78. package/dist/annotations/freeze/bundle.js +201 -0
  79. package/dist/annotations/freeze/bundle.js.map +7 -0
  80. package/dist/annotations/freeze/coords.js +37 -0
  81. package/dist/annotations/freeze/coords.js.map +7 -0
  82. package/dist/annotations/freeze/freeze.js +261 -0
  83. package/dist/annotations/freeze/freeze.js.map +7 -0
  84. package/dist/annotations/freeze/hash.js +28 -0
  85. package/dist/annotations/freeze/hash.js.map +7 -0
  86. package/dist/annotations/freeze/index-page.js +78 -0
  87. package/dist/annotations/freeze/index-page.js.map +7 -0
  88. package/dist/annotations/freeze/index.d.ts +1 -1
  89. package/dist/annotations/freeze/index.js +28 -22
  90. package/dist/annotations/freeze/index.js.map +7 -1
  91. package/dist/annotations/freeze/raster.js +208 -0
  92. package/dist/annotations/freeze/raster.js.map +7 -0
  93. package/dist/annotations/freeze/stamp.js +117 -0
  94. package/dist/annotations/freeze/stamp.js.map +7 -0
  95. package/dist/annotations/freeze/types.js +14 -0
  96. package/dist/annotations/freeze/types.js.map +7 -0
  97. package/dist/annotations/freeze/vector.js +283 -0
  98. package/dist/annotations/freeze/vector.js.map +7 -0
  99. package/dist/annotations/geometry.js +81 -0
  100. package/dist/annotations/geometry.js.map +7 -0
  101. package/dist/annotations/index.js +125 -6
  102. package/dist/annotations/index.js.map +7 -1
  103. package/dist/annotations/merge.js +53 -0
  104. package/dist/annotations/merge.js.map +7 -0
  105. package/dist/{chunk-T3USOSVN.js → annotations/model.js} +23 -16
  106. package/dist/annotations/model.js.map +7 -0
  107. package/dist/annotations/overlay-geometry.js +41 -0
  108. package/dist/annotations/overlay-geometry.js.map +7 -0
  109. package/dist/annotations/redaction-log.js +33 -0
  110. package/dist/annotations/redaction-log.js.map +7 -0
  111. package/dist/annotations/selection-marks.js +122 -0
  112. package/dist/annotations/selection-marks.js.map +7 -0
  113. package/dist/annotations/state.js +140 -0
  114. package/dist/annotations/state.js.map +7 -0
  115. package/dist/annotations/store.js +49 -0
  116. package/dist/annotations/store.js.map +7 -0
  117. package/dist/annotations/useAnnotationLayer.js +306 -0
  118. package/dist/annotations/useAnnotationLayer.js.map +7 -0
  119. package/dist/annotations/useAnnotationShortcuts.js +31 -0
  120. package/dist/annotations/useAnnotationShortcuts.js.map +7 -0
  121. package/dist/annotations/useTextSelectionMarks.js +100 -0
  122. package/dist/annotations/useTextSelectionMarks.js.map +7 -0
  123. package/dist/artifacts/artifact-kind.js +19 -0
  124. package/dist/artifacts/artifact-kind.js.map +7 -0
  125. package/dist/artifacts/index.cjs +13 -13
  126. package/dist/artifacts/index.js +89 -571
  127. package/dist/artifacts/index.js.map +7 -1
  128. package/dist/{chunk-WUTL62MC.js → artifacts/normalize-markdown.js} +12 -14
  129. package/dist/artifacts/normalize-markdown.js.map +7 -0
  130. package/dist/artifacts/redline-core.js +12 -3
  131. package/dist/artifacts/redline-core.js.map +7 -1
  132. package/dist/{chunk-74N436VK.js → artifacts/redline-diff.js} +10 -9
  133. package/dist/artifacts/redline-diff.js.map +7 -0
  134. package/dist/artifacts/redline-engine.js +14 -0
  135. package/dist/artifacts/redline-engine.js.map +7 -0
  136. package/dist/artifacts/renderArtifactMarkdown.js +9 -0
  137. package/dist/artifacts/renderArtifactMarkdown.js.map +7 -0
  138. package/dist/artifacts/useArtifactEditSession.js +183 -0
  139. package/dist/artifacts/useArtifactEditSession.js.map +7 -0
  140. package/dist/artifacts/useArtifactSession.js +173 -0
  141. package/dist/artifacts/useArtifactSession.js.map +7 -0
  142. package/dist/artifacts/useArtifactSource.js +16 -0
  143. package/dist/artifacts/useArtifactSource.js.map +7 -0
  144. package/dist/artifacts/useArtifactText.js +78 -0
  145. package/dist/artifacts/useArtifactText.js.map +7 -0
  146. package/dist/artifacts/useRedline.js +65 -0
  147. package/dist/artifacts/useRedline.js.map +7 -0
  148. package/dist/artifacts/useStreamedArtifact.js +74 -0
  149. package/dist/artifacts/useStreamedArtifact.js.map +7 -0
  150. package/dist/artifacts/versions.js +68 -0
  151. package/dist/artifacts/versions.js.map +7 -0
  152. package/dist/billing.js +200 -0
  153. package/dist/billing.js.map +7 -0
  154. package/dist/boot-timeout.js +19 -0
  155. package/dist/boot-timeout.js.map +7 -0
  156. package/dist/calendar/accounts.js +213 -0
  157. package/dist/calendar/accounts.js.map +7 -0
  158. package/dist/calendar/hooks.js +409 -0
  159. package/dist/calendar/hooks.js.map +7 -0
  160. package/dist/calendar/index.js +31 -0
  161. package/dist/calendar/index.js.map +7 -0
  162. package/dist/calendar/keys.js +5 -0
  163. package/dist/calendar/keys.js.map +7 -0
  164. package/dist/calendar/offline/outbox.js +231 -0
  165. package/dist/calendar/offline/outbox.js.map +7 -0
  166. package/dist/calendar/offline/runtime.js +109 -0
  167. package/dist/calendar/offline/runtime.js.map +7 -0
  168. package/dist/calendar/offline/support.js +41 -0
  169. package/dist/calendar/offline/support.js.map +7 -0
  170. package/dist/calendar/support.js +34 -0
  171. package/dist/calendar/support.js.map +7 -0
  172. package/dist/calls/actions.js +226 -0
  173. package/dist/calls/actions.js.map +7 -0
  174. package/dist/calls/events.js +138 -0
  175. package/dist/calls/events.js.map +7 -0
  176. package/dist/calls/gate.js +19 -0
  177. package/dist/calls/gate.js.map +7 -0
  178. package/dist/calls/hooks.js +115 -0
  179. package/dist/calls/hooks.js.map +7 -0
  180. package/dist/calls/in-call-chat.js +132 -0
  181. package/dist/calls/in-call-chat.js.map +7 -0
  182. package/dist/calls/index.js +24 -0
  183. package/dist/calls/index.js.map +7 -0
  184. package/dist/calls/keys.js +17 -0
  185. package/dist/calls/keys.js.map +7 -0
  186. package/dist/calls/livekit/index.cjs +48 -48
  187. package/dist/calls/livekit/index.js +6 -958
  188. package/dist/calls/livekit/index.js.map +7 -1
  189. package/dist/calls/livekit/room.js +98 -0
  190. package/dist/calls/livekit/room.js.map +7 -0
  191. package/dist/calls/livekit/useCallRecorder.js +219 -0
  192. package/dist/calls/livekit/useCallRecorder.js.map +7 -0
  193. package/dist/calls/livekit/useCallTranscriber.js +309 -0
  194. package/dist/calls/livekit/useCallTranscriber.js.map +7 -0
  195. package/dist/calls/livekit/useHostMuteNotice.js +29 -0
  196. package/dist/calls/livekit/useHostMuteNotice.js.map +7 -0
  197. package/dist/calls/livekit/useInCallChat.js +161 -0
  198. package/dist/calls/livekit/useInCallChat.js.map +7 -0
  199. package/dist/calls/livekit/useWebinar.js +239 -0
  200. package/dist/calls/livekit/useWebinar.js.map +7 -0
  201. package/dist/calls/meeting-chat.js +149 -0
  202. package/dist/calls/meeting-chat.js.map +7 -0
  203. package/dist/calls/moderation.js +50 -0
  204. package/dist/calls/moderation.js.map +7 -0
  205. package/dist/calls/open-meeting-chat.js +29 -0
  206. package/dist/calls/open-meeting-chat.js.map +7 -0
  207. package/dist/calls/read-only.js +24 -0
  208. package/dist/calls/read-only.js.map +7 -0
  209. package/dist/calls/recording-media.js +218 -0
  210. package/dist/calls/recording-media.js.map +7 -0
  211. package/dist/calls/recording-playback.js +260 -0
  212. package/dist/calls/recording-playback.js.map +7 -0
  213. package/dist/calls/recording.js +268 -0
  214. package/dist/calls/recording.js.map +7 -0
  215. package/dist/calls/save-to-thread.js +80 -0
  216. package/dist/calls/save-to-thread.js.map +7 -0
  217. package/dist/calls/session.js +134 -0
  218. package/dist/calls/session.js.map +7 -0
  219. package/dist/calls/transcript-format.js +147 -0
  220. package/dist/calls/transcript-format.js.map +7 -0
  221. package/dist/calls/transcription.js +101 -0
  222. package/dist/calls/transcription.js.map +7 -0
  223. package/dist/calls/webinar.js +71 -0
  224. package/dist/calls/webinar.js.map +7 -0
  225. package/dist/charge-watch.js +71 -0
  226. package/dist/charge-watch.js.map +7 -0
  227. package/dist/chat/ChatBubble.js +146 -0
  228. package/dist/chat/ChatBubble.js.map +7 -0
  229. package/dist/chat/ConversationScope.js +30 -0
  230. package/dist/chat/ConversationScope.js.map +7 -0
  231. package/dist/chat/active-config.js +20 -0
  232. package/dist/chat/active-config.js.map +7 -0
  233. package/dist/chat/agent-chat-owner.js +25 -0
  234. package/dist/chat/agent-chat-owner.js.map +7 -0
  235. package/dist/chat/chat-artifacts.js +64 -0
  236. package/dist/chat/chat-artifacts.js.map +7 -0
  237. package/dist/chat/citations.js +32 -0
  238. package/dist/chat/citations.js.map +7 -0
  239. package/dist/chat/config-model.js +10 -0
  240. package/dist/chat/config-model.js.map +7 -0
  241. package/dist/chat/conversation-cache.js +676 -0
  242. package/dist/chat/conversation-cache.js.map +7 -0
  243. package/dist/chat/conversation-events.js +57 -0
  244. package/dist/chat/conversation-events.js.map +7 -0
  245. package/dist/chat/conversation-mentions.js +58 -0
  246. package/dist/chat/conversation-mentions.js.map +7 -0
  247. package/dist/chat/conversation-workspace.js +58 -0
  248. package/dist/chat/conversation-workspace.js.map +7 -0
  249. package/dist/chat/mentions.js +116 -0
  250. package/dist/chat/mentions.js.map +7 -0
  251. package/dist/chat/open-document.js +106 -0
  252. package/dist/chat/open-document.js.map +7 -0
  253. package/dist/chat/orphaned.js +44 -0
  254. package/dist/chat/orphaned.js.map +7 -0
  255. package/dist/chat/pendingFirstTurn.js +30 -0
  256. package/dist/chat/pendingFirstTurn.js.map +7 -0
  257. package/dist/chat/reply-target.js +12 -0
  258. package/dist/chat/reply-target.js.map +7 -0
  259. package/dist/chat/run-finished.js +28 -0
  260. package/dist/chat/run-finished.js.map +7 -0
  261. package/dist/chat/run-state.js +91 -0
  262. package/dist/chat/run-state.js.map +7 -0
  263. package/dist/chat/stream-handlers.js +419 -0
  264. package/dist/chat/stream-handlers.js.map +7 -0
  265. package/dist/chat/thread-binding.js +76 -0
  266. package/dist/chat/thread-binding.js.map +7 -0
  267. package/dist/chat/thread-document.js +53 -0
  268. package/dist/chat/thread-document.js.map +7 -0
  269. package/dist/chat/turn-control.js +79 -0
  270. package/dist/chat/turn-control.js.map +7 -0
  271. package/dist/chat/useAgentRunControl.js +36 -0
  272. package/dist/chat/useAgentRunControl.js.map +7 -0
  273. package/dist/chat/useAnswerAgentQuestion.js +19 -0
  274. package/dist/chat/useAnswerAgentQuestion.js.map +7 -0
  275. package/dist/chat/useAskAgain.js +32 -0
  276. package/dist/chat/useAskAgain.js.map +7 -0
  277. package/dist/chat/useAutoSendDraft.js +17 -0
  278. package/dist/chat/useAutoSendDraft.js.map +7 -0
  279. package/dist/chat/useBranchNavigation.js +97 -0
  280. package/dist/chat/useBranchNavigation.js.map +7 -0
  281. package/dist/chat/useConnectRunStream.js +169 -0
  282. package/dist/chat/useConnectRunStream.js.map +7 -0
  283. package/dist/chat/useConversation.js +431 -0
  284. package/dist/chat/useConversation.js.map +7 -0
  285. package/dist/chat/useConversationActions.js +662 -0
  286. package/dist/chat/useConversationActions.js.map +7 -0
  287. package/dist/chat/useConversationData.js +143 -0
  288. package/dist/chat/useConversationData.js.map +7 -0
  289. package/dist/chat/useConversationDocuments.js +41 -0
  290. package/dist/chat/useConversationDocuments.js.map +7 -0
  291. package/dist/chat/useConversationSettings.js +78 -0
  292. package/dist/chat/useConversationSettings.js.map +7 -0
  293. package/dist/chat/useConversationTurn.js +31 -0
  294. package/dist/chat/useConversationTurn.js.map +7 -0
  295. package/dist/chat/useConversationWorkspace.js +12 -0
  296. package/dist/chat/useConversationWorkspace.js.map +7 -0
  297. package/dist/chat/useDeleteMessage.js +15 -0
  298. package/dist/chat/useDeleteMessage.js.map +7 -0
  299. package/dist/chat/useDocumentAutoAdd.js +73 -0
  300. package/dist/chat/useDocumentAutoAdd.js.map +7 -0
  301. package/dist/chat/useMessageTimer.js +39 -0
  302. package/dist/chat/useMessageTimer.js.map +7 -0
  303. package/dist/chat/useThinkingContent.js +67 -0
  304. package/dist/chat/useThinkingContent.js.map +7 -0
  305. package/dist/chat/useThreadDocument.js +61 -0
  306. package/dist/chat/useThreadDocument.js.map +7 -0
  307. package/dist/chat/workflow-run-titles.js +50 -0
  308. package/dist/chat/workflow-run-titles.js.map +7 -0
  309. package/dist/{chunk-W2FATOTX.cjs → chunk-3JAEX57E.cjs} +4 -4
  310. package/dist/{chunk-W2FATOTX.cjs.map → chunk-3JAEX57E.cjs.map} +1 -1
  311. package/dist/{chunk-HD5H2PPP.cjs → chunk-5TRUFWKT.cjs} +66 -66
  312. package/dist/{chunk-HD5H2PPP.cjs.map → chunk-5TRUFWKT.cjs.map} +1 -1
  313. package/dist/{chunk-Z3W4XFWB.cjs → chunk-C72FSQUE.cjs} +3 -3
  314. package/dist/{chunk-Z3W4XFWB.cjs.map → chunk-C72FSQUE.cjs.map} +1 -1
  315. package/dist/{chunk-LBAG7KL7.cjs → chunk-FQVNZCTD.cjs} +3 -3
  316. package/dist/{chunk-LBAG7KL7.cjs.map → chunk-FQVNZCTD.cjs.map} +1 -1
  317. package/dist/{chunk-RVUGSM65.cjs → chunk-GKGAPU6T.cjs} +44 -44
  318. package/dist/{chunk-RVUGSM65.cjs.map → chunk-GKGAPU6T.cjs.map} +1 -1
  319. package/dist/{chunk-7LIMHY56.cjs → chunk-O32T7XRD.cjs} +3 -3
  320. package/dist/{chunk-7LIMHY56.cjs.map → chunk-O32T7XRD.cjs.map} +1 -1
  321. package/dist/chunk-PWGSIM3F.cjs +291 -0
  322. package/dist/chunk-PWGSIM3F.cjs.map +1 -0
  323. package/dist/chunk-QP3WQSAA.cjs +23 -0
  324. package/dist/chunk-QP3WQSAA.cjs.map +1 -0
  325. package/dist/chunk-T5FZICXN.cjs +50 -0
  326. package/dist/chunk-T5FZICXN.cjs.map +1 -0
  327. package/dist/{chunk-QF2BW6HZ.cjs → chunk-UDDJIUHJ.cjs} +38 -38
  328. package/dist/{chunk-QF2BW6HZ.cjs.map → chunk-UDDJIUHJ.cjs.map} +1 -1
  329. package/dist/{chunk-5HDKL6XB.cjs → chunk-YMB6XUR2.cjs} +57 -57
  330. package/dist/{chunk-5HDKL6XB.cjs.map → chunk-YMB6XUR2.cjs.map} +1 -1
  331. package/dist/config/config-hooks.js +209 -0
  332. package/dist/config/config-hooks.js.map +7 -0
  333. package/dist/connectivity.js +72 -0
  334. package/dist/connectivity.js.map +7 -0
  335. package/dist/connectors/hooks.js +263 -0
  336. package/dist/connectors/hooks.js.map +7 -0
  337. package/dist/connectors/index.js +28 -0
  338. package/dist/connectors/index.js.map +7 -0
  339. package/dist/contacts.js +100 -0
  340. package/dist/contacts.js.map +7 -0
  341. package/dist/conversation-list.js +65 -0
  342. package/dist/conversation-list.js.map +7 -0
  343. package/dist/databases.js +195 -0
  344. package/dist/databases.js.map +7 -0
  345. package/dist/datasets/aggregate.js +281 -0
  346. package/dist/datasets/aggregate.js.map +7 -0
  347. package/dist/datasets/index.js +122 -0
  348. package/dist/datasets/index.js.map +7 -0
  349. package/dist/datasets/members.js +27 -0
  350. package/dist/datasets/members.js.map +7 -0
  351. package/dist/datasets/panel.js +44 -0
  352. package/dist/datasets/panel.js.map +7 -0
  353. package/dist/datasets/registry.js +47 -0
  354. package/dist/datasets/registry.js.map +7 -0
  355. package/dist/datasets/rows-source.js +44 -0
  356. package/dist/datasets/rows-source.js.map +7 -0
  357. package/dist/datasets/sources/activity.js +83 -0
  358. package/dist/datasets/sources/activity.js.map +7 -0
  359. package/dist/datasets/sources/conversations.js +74 -0
  360. package/dist/datasets/sources/conversations.js.map +7 -0
  361. package/dist/datasets/sources/database.js +209 -0
  362. package/dist/datasets/sources/database.js.map +7 -0
  363. package/dist/datasets/sources/documents.js +93 -0
  364. package/dist/datasets/sources/documents.js.map +7 -0
  365. package/dist/datasets/sources/facts.js +56 -0
  366. package/dist/datasets/sources/facts.js.map +7 -0
  367. package/dist/datasets/sources/projects.js +117 -0
  368. package/dist/datasets/sources/projects.js.map +7 -0
  369. package/dist/datasets/sources/tags.js +67 -0
  370. package/dist/datasets/sources/tags.js.map +7 -0
  371. package/dist/datasets/sources/usage.js +268 -0
  372. package/dist/datasets/sources/usage.js.map +7 -0
  373. package/dist/datasets/sources/workflows.js +91 -0
  374. package/dist/datasets/sources/workflows.js.map +7 -0
  375. package/dist/datasets/sources/workspaces.js +67 -0
  376. package/dist/datasets/sources/workspaces.js.map +7 -0
  377. package/dist/datasets/store.js +194 -0
  378. package/dist/datasets/store.js.map +7 -0
  379. package/dist/datasets/types.js +14 -0
  380. package/dist/datasets/types.js.map +7 -0
  381. package/dist/directmessages/ConversationList.js +148 -0
  382. package/dist/directmessages/ConversationList.js.map +7 -0
  383. package/dist/directmessages/DateSeparator.js +8 -0
  384. package/dist/directmessages/DateSeparator.js.map +7 -0
  385. package/dist/directmessages/DmAvatar.js +29 -0
  386. package/dist/directmessages/DmAvatar.js.map +7 -0
  387. package/dist/directmessages/DmComposer.js +201 -0
  388. package/dist/directmessages/DmComposer.js.map +7 -0
  389. package/dist/directmessages/arrivals.js +29 -0
  390. package/dist/directmessages/arrivals.js.map +7 -0
  391. package/dist/directmessages/chat-cache.js +192 -0
  392. package/dist/directmessages/chat-cache.js.map +7 -0
  393. package/dist/directmessages/grouping.js +26 -0
  394. package/dist/directmessages/grouping.js.map +7 -0
  395. package/dist/directmessages/index.js +81 -0
  396. package/dist/directmessages/index.js.map +7 -0
  397. package/dist/directmessages/useChats.js +266 -0
  398. package/dist/directmessages/useChats.js.map +7 -0
  399. package/dist/directmessages/utils.js +23 -0
  400. package/dist/directmessages/utils.js.map +7 -0
  401. package/dist/document-tools/can-annotate.js +7 -0
  402. package/dist/document-tools/can-annotate.js.map +7 -0
  403. package/dist/document-tools/core.cjs +99 -99
  404. package/dist/document-tools/core.d.ts +2 -2
  405. package/dist/document-tools/core.js +218 -12
  406. package/dist/document-tools/core.js.map +7 -1
  407. package/dist/document-tools/document-pdf.js +76 -0
  408. package/dist/document-tools/document-pdf.js.map +7 -0
  409. package/dist/document-tools/environment.js +136 -0
  410. package/dist/document-tools/environment.js.map +7 -0
  411. package/dist/document-tools/file-shares.js +112 -0
  412. package/dist/document-tools/file-shares.js.map +7 -0
  413. package/dist/document-tools/frozen-copy.js +27 -0
  414. package/dist/document-tools/frozen-copy.js.map +7 -0
  415. package/dist/document-tools/index.cjs +101 -101
  416. package/dist/document-tools/index.d.ts +1 -1
  417. package/dist/document-tools/index.js +26 -148
  418. package/dist/document-tools/index.js.map +7 -1
  419. package/dist/document-tools/markup-download.js +51 -0
  420. package/dist/document-tools/markup-download.js.map +7 -0
  421. package/dist/document-tools/markup-store.js +138 -0
  422. package/dist/document-tools/markup-store.js.map +7 -0
  423. package/dist/document-tools/markup-transfer.js +88 -0
  424. package/dist/document-tools/markup-transfer.js.map +7 -0
  425. package/dist/document-tools/production.js +147 -0
  426. package/dist/document-tools/production.js.map +7 -0
  427. package/dist/document-tools/saved-signature.js +90 -0
  428. package/dist/document-tools/saved-signature.js.map +7 -0
  429. package/dist/document-tools/signature-requests.js +310 -0
  430. package/dist/document-tools/signature-requests.js.map +7 -0
  431. package/dist/document-tools/signing.js +127 -0
  432. package/dist/document-tools/signing.js.map +7 -0
  433. package/dist/document-tools/useDocumentBytes.js +38 -0
  434. package/dist/document-tools/useDocumentBytes.js.map +7 -0
  435. package/dist/document-tools/useDocumentMarkup.js +154 -0
  436. package/dist/document-tools/useDocumentMarkup.js.map +7 -0
  437. package/dist/document-tools/useIsOnline.js +13 -0
  438. package/dist/document-tools/useIsOnline.js.map +7 -0
  439. package/dist/document-tools/verification.js +60 -0
  440. package/dist/document-tools/verification.js.map +7 -0
  441. package/dist/documents/DocumentReview.js +220 -0
  442. package/dist/documents/DocumentReview.js.map +7 -0
  443. package/dist/documents/DraftTitleControls.js +41 -0
  444. package/dist/documents/DraftTitleControls.js.map +7 -0
  445. package/dist/documents/InlineMicButton.js +90 -0
  446. package/dist/documents/InlineMicButton.js.map +7 -0
  447. package/dist/documents/MicRecordingOverlay.js +29 -0
  448. package/dist/documents/MicRecordingOverlay.js.map +7 -0
  449. package/dist/documents/MicWaveformIndicator.js +68 -0
  450. package/dist/documents/MicWaveformIndicator.js.map +7 -0
  451. package/dist/documents/PhotoCapture.js +120 -0
  452. package/dist/documents/PhotoCapture.js.map +7 -0
  453. package/dist/documents/VideoCapture.js +129 -0
  454. package/dist/documents/VideoCapture.js.map +7 -0
  455. package/dist/documents/VoiceNoteRecorder.js +166 -0
  456. package/dist/documents/VoiceNoteRecorder.js.map +7 -0
  457. package/dist/documents/audio-segmenter.js +63 -0
  458. package/dist/documents/audio-segmenter.js.map +7 -0
  459. package/dist/documents/capture-frame.js +21 -0
  460. package/dist/documents/capture-frame.js.map +7 -0
  461. package/dist/documents/chunked-recorder.js +190 -0
  462. package/dist/documents/chunked-recorder.js.map +7 -0
  463. package/dist/documents/document-download.js +53 -0
  464. package/dist/documents/document-download.js.map +7 -0
  465. package/dist/documents/document-fields.js +109 -0
  466. package/dist/documents/document-fields.js.map +7 -0
  467. package/dist/documents/documents-cache.js +110 -0
  468. package/dist/documents/documents-cache.js.map +7 -0
  469. package/dist/documents/documents-data.js +128 -0
  470. package/dist/documents/documents-data.js.map +7 -0
  471. package/dist/documents/documents-loader.js +113 -0
  472. package/dist/documents/documents-loader.js.map +7 -0
  473. package/dist/documents/documents-writes.js +448 -0
  474. package/dist/documents/documents-writes.js.map +7 -0
  475. package/dist/documents/format-elapsed.js +10 -0
  476. package/dist/documents/format-elapsed.js.map +7 -0
  477. package/dist/documents/has-video-track.js +26 -0
  478. package/dist/documents/has-video-track.js.map +7 -0
  479. package/dist/documents/index.js +266 -0
  480. package/dist/documents/index.js.map +7 -0
  481. package/dist/documents/media-error.js +18 -0
  482. package/dist/documents/media-error.js.map +7 -0
  483. package/dist/documents/offline-documents.js +160 -0
  484. package/dist/documents/offline-documents.js.map +7 -0
  485. package/dist/documents/pending-sync.js +210 -0
  486. package/dist/documents/pending-sync.js.map +7 -0
  487. package/dist/documents/processing-live.js +122 -0
  488. package/dist/documents/processing-live.js.map +7 -0
  489. package/dist/documents/processing-order.js +59 -0
  490. package/dist/documents/processing-order.js.map +7 -0
  491. package/dist/documents/processing-settle.js +21 -0
  492. package/dist/documents/processing-settle.js.map +7 -0
  493. package/dist/documents/related-documents.js +133 -0
  494. package/dist/documents/related-documents.js.map +7 -0
  495. package/dist/documents/support.js +29 -0
  496. package/dist/documents/support.js.map +7 -0
  497. package/dist/documents/upload-files.js +169 -0
  498. package/dist/documents/upload-files.js.map +7 -0
  499. package/dist/documents/useAudioRecorder.js +315 -0
  500. package/dist/documents/useAudioRecorder.js.map +7 -0
  501. package/dist/documents/useCameraStream.js +35 -0
  502. package/dist/documents/useCameraStream.js.map +7 -0
  503. package/dist/documents/useDocumentThumbnails.js +71 -0
  504. package/dist/documents/useDocumentThumbnails.js.map +7 -0
  505. package/dist/documents/useDownloadDocuments.js +90 -0
  506. package/dist/documents/useDownloadDocuments.js.map +7 -0
  507. package/dist/documents/usePassageQuotes.js +37 -0
  508. package/dist/documents/usePassageQuotes.js.map +7 -0
  509. package/dist/documents/useUploadFiles.js +165 -0
  510. package/dist/documents/useUploadFiles.js.map +7 -0
  511. package/dist/documents/useUploadFromUrls.js +63 -0
  512. package/dist/documents/useUploadFromUrls.js.map +7 -0
  513. package/dist/documents/useVideoRecorder.js +62 -0
  514. package/dist/documents/useVideoRecorder.js.map +7 -0
  515. package/dist/entities/AgentMarkers.js +61 -0
  516. package/dist/entities/AgentMarkers.js.map +7 -0
  517. package/dist/entities/DocumentEntity.js +125 -0
  518. package/dist/entities/DocumentEntity.js.map +7 -0
  519. package/dist/entities/EntityPill.js +250 -0
  520. package/dist/entities/EntityPill.js.map +7 -0
  521. package/dist/entities/ProjectEntity.js +61 -0
  522. package/dist/entities/ProjectEntity.js.map +7 -0
  523. package/dist/entities/ShieldIcon.js +49 -0
  524. package/dist/entities/ShieldIcon.js.map +7 -0
  525. package/dist/entities/TagEntity.js +79 -0
  526. package/dist/entities/TagEntity.js.map +7 -0
  527. package/dist/entities/ThreadVisibilityIcon.js +15 -0
  528. package/dist/entities/ThreadVisibilityIcon.js.map +7 -0
  529. package/dist/entities/UserEntity.js +211 -0
  530. package/dist/entities/UserEntity.js.map +7 -0
  531. package/dist/entities/VerifiedBadge.js +19 -0
  532. package/dist/entities/VerifiedBadge.js.map +7 -0
  533. package/dist/entities/agent-avatar-colors.js +26 -0
  534. package/dist/entities/agent-avatar-colors.js.map +7 -0
  535. package/dist/entities/colors.js +64 -0
  536. package/dist/entities/colors.js.map +7 -0
  537. package/dist/entities/file-type.js +151 -0
  538. package/dist/entities/file-type.js.map +7 -0
  539. package/dist/entities/index.js +100 -0
  540. package/dist/entities/index.js.map +7 -0
  541. package/dist/entities/media-type.js +61 -0
  542. package/dist/entities/media-type.js.map +7 -0
  543. package/dist/entities/registry.js +385 -0
  544. package/dist/entities/registry.js.map +7 -0
  545. package/dist/entities/status.js +43 -0
  546. package/dist/entities/status.js.map +7 -0
  547. package/dist/entities/tag-kinds.js +23 -0
  548. package/dist/entities/tag-kinds.js.map +7 -0
  549. package/dist/entities/tag-types.js +44 -0
  550. package/dist/entities/tag-types.js.map +7 -0
  551. package/dist/entities/vocabulary.js +12 -0
  552. package/dist/entities/vocabulary.js.map +7 -0
  553. package/dist/entity-extraction.js +25 -0
  554. package/dist/entity-extraction.js.map +7 -0
  555. package/dist/facts-search.js +57 -0
  556. package/dist/facts-search.js.map +7 -0
  557. package/dist/facts.js +69 -0
  558. package/dist/facts.js.map +7 -0
  559. package/dist/hooks.js +320 -0
  560. package/dist/hooks.js.map +7 -0
  561. package/dist/host/ArbiHost.js +24 -0
  562. package/dist/host/ArbiHost.js.map +7 -0
  563. package/dist/host/assistant-copy.js +54 -0
  564. package/dist/host/assistant-copy.js.map +7 -0
  565. package/dist/host/config.js +9 -0
  566. package/dist/host/config.js.map +7 -0
  567. package/dist/host/context.js +10 -0
  568. package/dist/host/context.js.map +7 -0
  569. package/dist/host/features.js +144 -0
  570. package/dist/host/features.js.map +7 -0
  571. package/dist/host/index.js +12 -0
  572. package/dist/host/index.js.map +7 -0
  573. package/dist/{chunk-DPYBZDJB.js → host/merge.js} +7 -7
  574. package/dist/host/merge.js.map +7 -0
  575. package/dist/house-template.js +40 -0
  576. package/dist/house-template.js.map +7 -0
  577. package/dist/icons/AppleIcon.js +25 -0
  578. package/dist/icons/AppleIcon.js.map +7 -0
  579. package/dist/icons/ArbiBotHeadIcon.js +46 -0
  580. package/dist/icons/ArbiBotHeadIcon.js.map +7 -0
  581. package/dist/icons/ArbiIcon.js +24 -0
  582. package/dist/icons/ArbiIcon.js.map +7 -0
  583. package/dist/icons/AudIcon.js +56 -0
  584. package/dist/icons/AudIcon.js.map +7 -0
  585. package/dist/icons/ClaudeIcon.js +28 -0
  586. package/dist/icons/ClaudeIcon.js.map +7 -0
  587. package/dist/icons/DocIcon.js +56 -0
  588. package/dist/icons/DocIcon.js.map +7 -0
  589. package/dist/icons/EmlIcon.js +57 -0
  590. package/dist/icons/EmlIcon.js.map +7 -0
  591. package/dist/icons/FireworksIcon.js +47 -0
  592. package/dist/icons/FireworksIcon.js.map +7 -0
  593. package/dist/icons/GeminiIcon.js +37 -0
  594. package/dist/icons/GeminiIcon.js.map +7 -0
  595. package/dist/icons/GitHubIcon.js +32 -0
  596. package/dist/icons/GitHubIcon.js.map +7 -0
  597. package/dist/icons/GoogleIcon.js +53 -0
  598. package/dist/icons/GoogleIcon.js.map +7 -0
  599. package/dist/icons/HtmlIcon.js +56 -0
  600. package/dist/icons/HtmlIcon.js.map +7 -0
  601. package/dist/icons/ImgIcon.js +56 -0
  602. package/dist/icons/ImgIcon.js.map +7 -0
  603. package/dist/icons/LinkedInIcon.js +25 -0
  604. package/dist/icons/LinkedInIcon.js.map +7 -0
  605. package/dist/icons/OpenAIIcon.js +28 -0
  606. package/dist/icons/OpenAIIcon.js.map +7 -0
  607. package/dist/icons/PdfIcon.js +56 -0
  608. package/dist/icons/PdfIcon.js.map +7 -0
  609. package/dist/icons/ProviderIcon.js +42 -0
  610. package/dist/icons/ProviderIcon.js.map +7 -0
  611. package/dist/icons/TxtIcon.js +56 -0
  612. package/dist/icons/TxtIcon.js.map +7 -0
  613. package/dist/icons/VidIcon.js +56 -0
  614. package/dist/icons/VidIcon.js.map +7 -0
  615. package/dist/icons/XlsIcon.js +56 -0
  616. package/dist/icons/XlsIcon.js.map +7 -0
  617. package/dist/icons/arbi-mark.js +5 -0
  618. package/dist/icons/arbi-mark.js.map +7 -0
  619. package/dist/icons/colors.js +31 -0
  620. package/dist/icons/colors.js.map +7 -0
  621. package/dist/icons/file-type.js +105 -0
  622. package/dist/icons/file-type.js.map +7 -0
  623. package/dist/icons/index.js +55 -0
  624. package/dist/icons/index.js.map +7 -0
  625. package/dist/imagecrop/ImageCropModal.js +56 -0
  626. package/dist/imagecrop/ImageCropModal.js.map +7 -0
  627. package/dist/imagecrop/ImageCropper.js +100 -0
  628. package/dist/imagecrop/ImageCropper.js.map +7 -0
  629. package/dist/imagecrop/cropImage.js +43 -0
  630. package/dist/imagecrop/cropImage.js.map +7 -0
  631. package/dist/imagecrop/index.js +13 -0
  632. package/dist/imagecrop/index.js.map +7 -0
  633. package/dist/index.cjs +1689 -1619
  634. package/dist/index.cjs.map +1 -1
  635. package/dist/{index.d-BMhTH98a.d.ts → index.d-CYXcTKYt.d.ts} +1 -0
  636. package/dist/index.d.ts +38 -8
  637. package/dist/index.js +702 -17060
  638. package/dist/index.js.map +7 -1
  639. package/dist/lazy-chunk.d-B6YDLMKI.d.ts +31 -0
  640. package/dist/lazy-chunk.js +46 -0
  641. package/dist/lazy-chunk.js.map +7 -0
  642. package/dist/lib/collision-padding.js +13 -0
  643. package/dist/lib/collision-padding.js.map +7 -0
  644. package/dist/lib/format-friendly-date.js +55 -0
  645. package/dist/lib/format-friendly-date.js.map +7 -0
  646. package/dist/lib/format-relative-time.js +52 -0
  647. package/dist/lib/format-relative-time.js.map +7 -0
  648. package/dist/lib/markdown-to-text.js +35 -0
  649. package/dist/lib/markdown-to-text.js.map +7 -0
  650. package/dist/lib/toast-interaction.js +9 -0
  651. package/dist/lib/toast-interaction.js.map +7 -0
  652. package/dist/lib/utils.js +9 -0
  653. package/dist/lib/utils.js.map +7 -0
  654. package/dist/mail/client.js +103 -0
  655. package/dist/mail/client.js.map +7 -0
  656. package/dist/mail/document-attachment.js +38 -0
  657. package/dist/mail/document-attachment.js.map +7 -0
  658. package/dist/mail/filing.js +132 -0
  659. package/dist/mail/filing.js.map +7 -0
  660. package/dist/mail/hooks.js +393 -0
  661. package/dist/mail/hooks.js.map +7 -0
  662. package/dist/mail/index.js +120 -0
  663. package/dist/mail/index.js.map +7 -0
  664. package/dist/mail/offline/changes.js +258 -0
  665. package/dist/mail/offline/changes.js.map +7 -0
  666. package/dist/mail/offline/confirm-sign-out.js +37 -0
  667. package/dist/mail/offline/confirm-sign-out.js.map +7 -0
  668. package/dist/mail/offline/crypto.js +26 -0
  669. package/dist/mail/offline/crypto.js.map +7 -0
  670. package/dist/mail/offline/drafts.js +69 -0
  671. package/dist/mail/offline/drafts.js.map +7 -0
  672. package/dist/mail/offline/hooks.js +104 -0
  673. package/dist/mail/offline/hooks.js.map +7 -0
  674. package/dist/mail/offline/index.js +48 -0
  675. package/dist/mail/offline/index.js.map +7 -0
  676. package/dist/mail/offline/live.js +233 -0
  677. package/dist/mail/offline/live.js.map +7 -0
  678. package/dist/mail/offline/mailbox.js +191 -0
  679. package/dist/mail/offline/mailbox.js.map +7 -0
  680. package/dist/mail/offline/outbox.js +167 -0
  681. package/dist/mail/offline/outbox.js.map +7 -0
  682. package/dist/mail/offline/reads.js +159 -0
  683. package/dist/mail/offline/reads.js.map +7 -0
  684. package/dist/mail/offline/runtime.js +100 -0
  685. package/dist/mail/offline/runtime.js.map +7 -0
  686. package/dist/mail/offline/settings.js +84 -0
  687. package/dist/mail/offline/settings.js.map +7 -0
  688. package/dist/mail/offline/store.js +29 -0
  689. package/dist/mail/offline/store.js.map +7 -0
  690. package/dist/mail/offline/sync.js +251 -0
  691. package/dist/mail/offline/sync.js.map +7 -0
  692. package/dist/mail/offline/wipe.js +19 -0
  693. package/dist/mail/offline/wipe.js.map +7 -0
  694. package/dist/mail/thread-keys.js +53 -0
  695. package/dist/mail/thread-keys.js.map +7 -0
  696. package/dist/mail/threads.js +71 -0
  697. package/dist/mail/threads.js.map +7 -0
  698. package/dist/mail/useMailClient.js +42 -0
  699. package/dist/mail/useMailClient.js.map +7 -0
  700. package/dist/model-connections.js +58 -0
  701. package/dist/model-connections.js.map +7 -0
  702. package/dist/notifications/ActivityFeed.js +70 -0
  703. package/dist/notifications/ActivityFeed.js.map +7 -0
  704. package/dist/notifications/NotificationBell.js +200 -0
  705. package/dist/notifications/NotificationBell.js.map +7 -0
  706. package/dist/notifications/Toaster.js +24 -0
  707. package/dist/notifications/Toaster.js.map +7 -0
  708. package/dist/notifications/activity-types.js +65 -0
  709. package/dist/notifications/activity-types.js.map +7 -0
  710. package/dist/notifications/bell.js +24 -0
  711. package/dist/notifications/bell.js.map +7 -0
  712. package/dist/notifications/calendar-notifications.js +79 -0
  713. package/dist/notifications/calendar-notifications.js.map +7 -0
  714. package/dist/notifications/index.js +108 -0
  715. package/dist/notifications/index.js.map +7 -0
  716. package/dist/notifications/live.js +28 -0
  717. package/dist/notifications/live.js.map +7 -0
  718. package/dist/notifications/notification-types.js +127 -0
  719. package/dist/notifications/notification-types.js.map +7 -0
  720. package/dist/{chunk-BF5KW5ZI.js → notifications/notify-store.js} +24 -74
  721. package/dist/notifications/notify-store.js.map +7 -0
  722. package/dist/notifications/server-notifications.js +452 -0
  723. package/dist/notifications/server-notifications.js.map +7 -0
  724. package/dist/notifications/useNotifications.js +34 -0
  725. package/dist/notifications/useNotifications.js.map +7 -0
  726. package/dist/offline-store/network.js +11 -0
  727. package/dist/offline-store/network.js.map +7 -0
  728. package/dist/offline-store/store.js +136 -0
  729. package/dist/offline-store/store.js.map +7 -0
  730. package/dist/offline-store/wipe.js +8 -0
  731. package/dist/offline-store/wipe.js.map +7 -0
  732. package/dist/offline.js +58 -0
  733. package/dist/offline.js.map +7 -0
  734. package/dist/people.js +122 -0
  735. package/dist/people.js.map +7 -0
  736. package/dist/projects.js +30 -0
  737. package/dist/projects.js.map +7 -0
  738. package/dist/query-client.js +63 -0
  739. package/dist/query-client.js.map +7 -0
  740. package/dist/{query-keys.d-ChgpJLPF.d.ts → query-keys.d-B1Am9tIF.d.ts} +1 -1
  741. package/dist/{chunk-6H3O63TQ.js → query-keys.js} +19 -32
  742. package/dist/query-keys.js.map +7 -0
  743. package/dist/query-persist.js +66 -0
  744. package/dist/query-persist.js.map +7 -0
  745. package/dist/queued-writes.js +59 -0
  746. package/dist/queued-writes.js.map +7 -0
  747. package/dist/remote-control.js +67 -0
  748. package/dist/remote-control.js.map +7 -0
  749. package/dist/search/index.js +23 -0
  750. package/dist/search/index.js.map +7 -0
  751. package/dist/search/session.js +58 -0
  752. package/dist/search/session.js.map +7 -0
  753. package/dist/search/useSemanticSearch.js +82 -0
  754. package/dist/search/useSemanticSearch.js.map +7 -0
  755. package/dist/session/chat.js +512 -0
  756. package/dist/session/chat.js.map +7 -0
  757. package/dist/session/documents.js +196 -0
  758. package/dist/session/documents.js.map +7 -0
  759. package/dist/session/drafts.js +103 -0
  760. package/dist/session/drafts.js.map +7 -0
  761. package/dist/session/index.js +123 -0
  762. package/dist/session/index.js.map +7 -0
  763. package/dist/session/layout.js +339 -0
  764. package/dist/session/layout.js.map +7 -0
  765. package/dist/session/persist.js +106 -0
  766. package/dist/session/persist.js.map +7 -0
  767. package/dist/session/session-hooks.js +53 -0
  768. package/dist/session/session-hooks.js.map +7 -0
  769. package/dist/session/signed-in.js +36 -0
  770. package/dist/session/signed-in.js.map +7 -0
  771. package/dist/session/store.js +166 -0
  772. package/dist/session/store.js.map +7 -0
  773. package/dist/session/thread-tabs.js +121 -0
  774. package/dist/session/thread-tabs.js.map +7 -0
  775. package/dist/session/ui-state-storage.js +76 -0
  776. package/dist/session/ui-state-storage.js.map +7 -0
  777. package/dist/session/workspace.js +77 -0
  778. package/dist/session/workspace.js.map +7 -0
  779. package/dist/shell/index.cjs +37 -37
  780. package/dist/shell/index.js +43 -10
  781. package/dist/shell/index.js.map +7 -1
  782. package/dist/skill-pack.js +111 -0
  783. package/dist/skill-pack.js.map +7 -0
  784. package/dist/storage.js +186 -0
  785. package/dist/storage.js.map +7 -0
  786. package/dist/support/index.js +35 -0
  787. package/dist/support/index.js.map +7 -0
  788. package/dist/tags.js +204 -0
  789. package/dist/tags.js.map +7 -0
  790. package/dist/ui/DebouncedInput.js +109 -0
  791. package/dist/ui/DebouncedInput.js.map +7 -0
  792. package/dist/ui/DictateContext.js +8 -0
  793. package/dist/ui/DictateContext.js.map +7 -0
  794. package/dist/ui/IconToggle.js +79 -0
  795. package/dist/ui/IconToggle.js.map +7 -0
  796. package/dist/ui/VerifiedToggle.js +38 -0
  797. package/dist/ui/VerifiedToggle.js.map +7 -0
  798. package/dist/ui/VisibilityToggle.js +103 -0
  799. package/dist/ui/VisibilityToggle.js.map +7 -0
  800. package/dist/ui/avatar.js +40 -0
  801. package/dist/ui/avatar.js.map +7 -0
  802. package/dist/ui/badge.js +28 -0
  803. package/dist/ui/badge.js.map +7 -0
  804. package/dist/ui/button.js +48 -0
  805. package/dist/ui/button.js.map +7 -0
  806. package/dist/ui/card.js +90 -0
  807. package/dist/ui/card.js.map +7 -0
  808. package/dist/ui/checkbox.js +22 -0
  809. package/dist/ui/checkbox.js.map +7 -0
  810. package/dist/ui/collapsible.js +10 -0
  811. package/dist/ui/collapsible.js.map +7 -0
  812. package/dist/ui/command.js +103 -0
  813. package/dist/ui/command.js.map +7 -0
  814. package/dist/{chunk-YNNAJJ5Z.js → ui/dialog.js} +30 -68
  815. package/dist/ui/dialog.js.map +7 -0
  816. package/dist/{chunk-47CCGUWR.js → ui/dropdown-menu.js} +40 -45
  817. package/dist/ui/dropdown-menu.js.map +7 -0
  818. package/dist/ui/hover-card.js +36 -0
  819. package/dist/ui/hover-card.js.map +7 -0
  820. package/dist/ui/index.cjs +167 -154
  821. package/dist/ui/index.d.ts +2 -1
  822. package/dist/ui/index.js +290 -9
  823. package/dist/ui/index.js.map +7 -1
  824. package/dist/ui/input.js +65 -0
  825. package/dist/ui/input.js.map +7 -0
  826. package/dist/ui/label.js +16 -0
  827. package/dist/ui/label.js.map +7 -0
  828. package/dist/ui/mark-menu.js +300 -0
  829. package/dist/ui/mark-menu.js.map +7 -0
  830. package/dist/ui/popover.js +40 -0
  831. package/dist/ui/popover.js.map +7 -0
  832. package/dist/ui/portal-container.js +17 -0
  833. package/dist/ui/portal-container.js.map +7 -0
  834. package/dist/ui/progress.js +24 -0
  835. package/dist/ui/progress.js.map +7 -0
  836. package/dist/ui/radio-group.js +29 -0
  837. package/dist/ui/radio-group.js.map +7 -0
  838. package/dist/ui/scroll-area.js +39 -0
  839. package/dist/ui/scroll-area.js.map +7 -0
  840. package/dist/ui/select.js +130 -0
  841. package/dist/ui/select.js.map +7 -0
  842. package/dist/ui/separator.js +23 -0
  843. package/dist/ui/separator.js.map +7 -0
  844. package/dist/ui/slider.js +23 -0
  845. package/dist/ui/slider.js.map +7 -0
  846. package/dist/ui/switch.js +28 -0
  847. package/dist/ui/switch.js.map +7 -0
  848. package/dist/ui/table.js +75 -0
  849. package/dist/ui/table.js.map +7 -0
  850. package/dist/ui/tabs.js +48 -0
  851. package/dist/ui/tabs.js.map +7 -0
  852. package/dist/ui/textarea.js +63 -0
  853. package/dist/ui/textarea.js.map +7 -0
  854. package/dist/ui/toggle-group.js +43 -0
  855. package/dist/ui/toggle-group.js.map +7 -0
  856. package/dist/ui/toggle.js +40 -0
  857. package/dist/ui/toggle.js.map +7 -0
  858. package/dist/ui/tooltip.js +215 -0
  859. package/dist/ui/tooltip.js.map +7 -0
  860. package/dist/usage-metered.js +8 -0
  861. package/dist/usage-metered.js.map +7 -0
  862. package/dist/useArbi.js +16 -0
  863. package/dist/useArbi.js.map +7 -0
  864. package/dist/useInstallSkillPack.js +81 -0
  865. package/dist/useInstallSkillPack.js.map +7 -0
  866. package/dist/usermenu/UserMenu.js +188 -0
  867. package/dist/usermenu/UserMenu.js.map +7 -0
  868. package/dist/usermenu/index.js +5 -0
  869. package/dist/usermenu/index.js.map +7 -0
  870. package/dist/users/index.js +15 -0
  871. package/dist/users/index.js.map +7 -0
  872. package/dist/users/user-display.js +60 -0
  873. package/dist/users/user-display.js.map +7 -0
  874. package/dist/viewers/AudioViewer.js +23 -0
  875. package/dist/viewers/AudioViewer.js.map +7 -0
  876. package/dist/viewers/DocxViewer.js +81 -0
  877. package/dist/viewers/DocxViewer.js.map +7 -0
  878. package/dist/viewers/ImageViewer.js +31 -0
  879. package/dist/viewers/ImageViewer.js.map +7 -0
  880. package/dist/viewers/PdfViewer.cjs +17 -0
  881. package/dist/viewers/PdfViewer.cjs.map +1 -0
  882. package/dist/viewers/PdfViewer.d.ts +9 -0
  883. package/dist/viewers/PdfViewer.js +20 -0
  884. package/dist/viewers/PdfViewer.js.map +7 -0
  885. package/dist/viewers/PdfViewerImpl.js +384 -0
  886. package/dist/viewers/PdfViewerImpl.js.map +7 -0
  887. package/dist/viewers/VideoViewer.js +31 -0
  888. package/dist/viewers/VideoViewer.js.map +7 -0
  889. package/dist/viewers/XlsxViewer.js +102 -0
  890. package/dist/viewers/XlsxViewer.js.map +7 -0
  891. package/dist/viewers/index.cjs +79 -653
  892. package/dist/viewers/index.cjs.map +1 -1
  893. package/dist/viewers/index.d.ts +3 -1
  894. package/dist/viewers/index.js +60 -903
  895. package/dist/viewers/index.js.map +7 -1
  896. package/dist/viewers/page-scroll.js +59 -0
  897. package/dist/viewers/page-scroll.js.map +7 -0
  898. package/dist/viewers/pdf-viewer-constants.js +9 -0
  899. package/dist/viewers/pdf-viewer-constants.js.map +7 -0
  900. package/dist/{chunk-PTOP7OHG.js → viewers/pdfjs-polyfills.js} +5 -6
  901. package/dist/viewers/pdfjs-polyfills.js.map +7 -0
  902. package/dist/viewers/selection-runs.js +38 -0
  903. package/dist/viewers/selection-runs.js.map +7 -0
  904. package/dist/viewers/usePageIntersectionObserver.js +68 -0
  905. package/dist/viewers/usePageIntersectionObserver.js.map +7 -0
  906. package/dist/viewers/useProcessedChunks.js +58 -0
  907. package/dist/viewers/useProcessedChunks.js.map +7 -0
  908. package/dist/viewers/viewer-zoom.js +163 -0
  909. package/dist/viewers/viewer-zoom.js.map +7 -0
  910. package/dist/websocket/ArbiWebSocketProvider.js +207 -0
  911. package/dist/websocket/ArbiWebSocketProvider.js.map +7 -0
  912. package/dist/websocket/context.js +14 -0
  913. package/dist/websocket/context.js.map +7 -0
  914. package/dist/websocket/hooks.js +30 -0
  915. package/dist/websocket/hooks.js.map +7 -0
  916. package/dist/websocket/index.js +22 -0
  917. package/dist/websocket/index.js.map +7 -0
  918. package/dist/websocket/live-updates.js +38 -0
  919. package/dist/websocket/live-updates.js.map +7 -0
  920. package/dist/websocket/reconnect.js +13 -0
  921. package/dist/websocket/reconnect.js.map +7 -0
  922. package/dist/websocket/store.js +58 -0
  923. package/dist/websocket/store.js.map +7 -0
  924. package/dist/workforce.js +607 -0
  925. package/dist/workforce.js.map +7 -0
  926. package/dist/workspaces/access-request.js +35 -0
  927. package/dist/workspaces/access-request.js.map +7 -0
  928. package/dist/workspaces/archive.js +59 -0
  929. package/dist/workspaces/archive.js.map +7 -0
  930. package/dist/workspaces/current.js +20 -0
  931. package/dist/workspaces/current.js.map +7 -0
  932. package/dist/workspaces/delete-workspaces.js +15 -0
  933. package/dist/workspaces/delete-workspaces.js.map +7 -0
  934. package/dist/workspaces/export-archive.js +40 -0
  935. package/dist/workspaces/export-archive.js.map +7 -0
  936. package/dist/workspaces/index.js +99 -0
  937. package/dist/workspaces/index.js.map +7 -0
  938. package/dist/workspaces/settings.js +45 -0
  939. package/dist/workspaces/settings.js.map +7 -0
  940. package/dist/workspaces/shell-workspaces.js +49 -0
  941. package/dist/workspaces/shell-workspaces.js.map +7 -0
  942. package/dist/workspaces/switch.js +234 -0
  943. package/dist/workspaces/switch.js.map +7 -0
  944. package/dist/workspaces/transfer.js +170 -0
  945. package/dist/workspaces/transfer.js.map +7 -0
  946. package/dist/workspaces/useShellWorkspaces.js +86 -0
  947. package/dist/workspaces/useShellWorkspaces.js.map +7 -0
  948. package/dist/workspaces/workspace-users.js +65 -0
  949. package/dist/workspaces/workspace-users.js.map +7 -0
  950. package/package.json +12 -4
  951. package/dist/PdfViewer.d-bQH9yxFg.d.ts +0 -55
  952. package/dist/ProductionDialog.d-CpzslFn0.d.cts +0 -51
  953. package/dist/annotations/freeze/index.d.cts +0 -205
  954. package/dist/annotations/index.d.cts +0 -360
  955. package/dist/artifacts/index.d.cts +0 -476
  956. package/dist/artifacts/redline-core.d.cts +0 -39
  957. package/dist/bundle-YNOX7VIL.js +0 -5
  958. package/dist/bundle-YNOX7VIL.js.map +0 -1
  959. package/dist/bundle.d-BnCSbHy2.d.cts +0 -85
  960. package/dist/calls/livekit/index.d.cts +0 -280
  961. package/dist/chunk-47CCGUWR.js.map +0 -1
  962. package/dist/chunk-6H3O63TQ.js.map +0 -1
  963. package/dist/chunk-74N436VK.js.map +0 -1
  964. package/dist/chunk-APZZQZ6P.js +0 -147
  965. package/dist/chunk-APZZQZ6P.js.map +0 -1
  966. package/dist/chunk-BF5KW5ZI.js.map +0 -1
  967. package/dist/chunk-CPQ3CU4B.js +0 -246
  968. package/dist/chunk-CPQ3CU4B.js.map +0 -1
  969. package/dist/chunk-DPYBZDJB.js.map +0 -1
  970. package/dist/chunk-DQALOQPI.js +0 -2766
  971. package/dist/chunk-DQALOQPI.js.map +0 -1
  972. package/dist/chunk-FZJMA32S.js +0 -1011
  973. package/dist/chunk-FZJMA32S.js.map +0 -1
  974. package/dist/chunk-H5M7FXLB.js +0 -2057
  975. package/dist/chunk-H5M7FXLB.js.map +0 -1
  976. package/dist/chunk-HDCRPPBW.js +0 -75
  977. package/dist/chunk-HDCRPPBW.js.map +0 -1
  978. package/dist/chunk-NIKPG4PW.js +0 -1128
  979. package/dist/chunk-NIKPG4PW.js.map +0 -1
  980. package/dist/chunk-NNAJLY6B.js +0 -4974
  981. package/dist/chunk-NNAJLY6B.js.map +0 -1
  982. package/dist/chunk-OYPKKHXY.js +0 -559
  983. package/dist/chunk-OYPKKHXY.js.map +0 -1
  984. package/dist/chunk-P6WLV5UY.js +0 -644
  985. package/dist/chunk-P6WLV5UY.js.map +0 -1
  986. package/dist/chunk-PTOP7OHG.js.map +0 -1
  987. package/dist/chunk-SN5YV6FF.js +0 -11
  988. package/dist/chunk-SN5YV6FF.js.map +0 -1
  989. package/dist/chunk-T3USOSVN.js.map +0 -1
  990. package/dist/chunk-T4CLW3DN.js +0 -2560
  991. package/dist/chunk-T4CLW3DN.js.map +0 -1
  992. package/dist/chunk-UW2SGTI5.js.map +0 -1
  993. package/dist/chunk-VSCU7K4V.js.map +0 -1
  994. package/dist/chunk-WL2Y2T47.js.map +0 -1
  995. package/dist/chunk-WUTL62MC.js.map +0 -1
  996. package/dist/chunk-Y3MTRA7D.js +0 -97
  997. package/dist/chunk-Y3MTRA7D.js.map +0 -1
  998. package/dist/chunk-YNNAJJ5Z.js.map +0 -1
  999. package/dist/document-tools/core.d.cts +0 -883
  1000. package/dist/document-tools/index.d.cts +0 -90
  1001. package/dist/index.d-BMhTH98a.d.cts +0 -2035
  1002. package/dist/index.d-oR5FKZpJ.d.cts +0 -579
  1003. package/dist/index.d.cts +0 -15893
  1004. package/dist/model.d-CNtRWKVK.d.cts +0 -110
  1005. package/dist/query-keys.d-ChgpJLPF.d.cts +0 -337
  1006. package/dist/shell/index.d.cts +0 -6
  1007. package/dist/store.d-GADDvC7w.d.cts +0 -35
  1008. package/dist/stored-markdown.d-Bp18vrbr.d.cts +0 -125
  1009. package/dist/ui/index.d.cts +0 -25
  1010. package/dist/useAudioRecorder.d-B9rlMI_E.d.cts +0 -778
  1011. package/dist/viewers/index.d.cts +0 -260
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mail/offline/mailbox.ts"],
4
+ "sourcesContent": ["/**\n * Typed, encrypted reads and writes of mailbox data in the offline store: accounts, folders,\n * message headers (what a folder list shows), bodies (the opened message) and attachment bytes.\n *\n * Headers and bodies are separate records so a folder list decrypts only small headers; bodies are\n * decrypted when a message is opened or searched.\n */\n\nimport type { MailAccount, MailFolder, MailMessage, MailPolicy } from '../types'\nimport { blind, openBytes, openJson, sealBytes, sealJson } from './crypto'\nimport {\n deleteRecords,\n getRecord,\n hasRecord,\n putRecord,\n recordsInBucket,\n touchRecord,\n type RecordKind,\n type StoredRecord,\n} from './store'\n\nexport const keyOf = {\n accounts: () => blind('accounts'),\n policy: () => blind('policy'),\n folders: (accountId: string) => blind('folders', accountId),\n header: (accountId: string, folderId: string, messageId: string) =>\n blind('header', accountId, folderId, messageId),\n body: (accountId: string, messageId: string) => blind('body', accountId, messageId),\n attachment: (accountId: string, messageId: string, attachmentId: string) =>\n blind('attachment', accountId, messageId, attachmentId),\n}\n\nexport const bucketOf = {\n global: () => blind('bucket', 'global'),\n folder: (accountId: string, folderId: string) => blind('bucket', 'folder', accountId, folderId),\n account: (accountId: string) => blind('bucket', 'account', accountId),\n}\n\nasync function put(\n kind: RecordKind,\n key: string,\n bucket: string,\n data: Uint8Array,\n sortAt = Date.now()\n): Promise<void> {\n const now = Date.now()\n await putRecord({ key, kind, bucket, sortAt, size: data.byteLength, lastAccess: now, data })\n}\n\nasync function readJson<T>(record: StoredRecord | undefined): Promise<T | undefined> {\n if (!record) return undefined\n try {\n return await openJson<T>(record.data)\n } catch {\n // Sealed under another identity (a different account wrote it) or damaged: treat as absent.\n return undefined\n }\n}\n\nconst receivedAt = (m: MailMessage) => (m.received_at ? Date.parse(m.received_at) || 0 : 0)\n\nexport async function saveAccounts(accounts: MailAccount[]): Promise<void> {\n await put('accounts', await keyOf.accounts(), await bucketOf.global(), await sealJson(accounts))\n}\n\nexport async function loadAccounts(): Promise<MailAccount[] | undefined> {\n return readJson<MailAccount[]>(await getRecord(await keyOf.accounts()))\n}\n\nexport async function savePolicy(policy: MailPolicy): Promise<void> {\n await put('policy', await keyOf.policy(), await bucketOf.global(), await sealJson(policy))\n}\n\nexport async function loadPolicy(): Promise<MailPolicy | undefined> {\n return readJson<MailPolicy>(await getRecord(await keyOf.policy()))\n}\n\nexport async function saveFolders(accountId: string, folders: MailFolder[]): Promise<void> {\n await put(\n 'folders',\n await keyOf.folders(accountId),\n await bucketOf.account(accountId),\n await sealJson(folders)\n )\n}\n\nexport async function loadFolders(accountId: string): Promise<MailFolder[] | undefined> {\n return readJson<MailFolder[]>(await getRecord(await keyOf.folders(accountId)))\n}\n\n/** A list row: the message without its body text/html or attachment bytes. */\nexport function toHeader(message: MailMessage): MailMessage {\n return {\n ...message,\n text: '',\n html: undefined,\n delivery: undefined,\n attachments: message.attachments.map((a) => ({ ...a, content_base64: null })),\n }\n}\n\nexport interface StoredHeader {\n key: string\n sortAt: number\n message: MailMessage\n}\n\nexport async function saveHeader(\n accountId: string,\n folderId: string,\n message: MailMessage\n): Promise<void> {\n await put(\n 'header',\n await keyOf.header(accountId, folderId, message.id),\n await bucketOf.folder(accountId, folderId),\n await sealJson(toHeader(message)),\n receivedAt(message)\n )\n}\n\n/** A folder's saved headers, newest first. */\nexport async function loadHeaders(accountId: string, folderId: string): Promise<StoredHeader[]> {\n const records = await recordsInBucket(await bucketOf.folder(accountId, folderId))\n const out: StoredHeader[] = []\n for (const record of records) {\n if (record.kind !== 'header') continue\n const message = await readJson<MailMessage>(record)\n if (message) out.push({ key: record.key, sortAt: record.sortAt, message })\n }\n return out.sort((a, b) => b.sortAt - a.sortAt)\n}\n\n/** Whether a folder has ever been saved on this device (so \"empty\" means empty, not \"not saved\"). */\nexport async function hasFolder(accountId: string): Promise<boolean> {\n return hasRecord(await keyOf.folders(accountId))\n}\n\nexport async function saveBody(accountId: string, message: MailMessage): Promise<void> {\n const stripped: MailMessage = {\n ...message,\n attachments: message.attachments.map((a) => ({ ...a, content_base64: null })),\n }\n await put(\n 'body',\n await keyOf.body(accountId, message.id),\n await bucketOf.account(accountId),\n await sealJson(stripped),\n receivedAt(message)\n )\n}\n\nexport async function hasBody(accountId: string, messageId: string): Promise<boolean> {\n return hasRecord(await keyOf.body(accountId, messageId))\n}\n\nexport async function loadBody(\n accountId: string,\n messageId: string\n): Promise<MailMessage | undefined> {\n return readJson<MailMessage>(await getRecord(await keyOf.body(accountId, messageId)))\n}\n\nexport async function saveAttachment(\n accountId: string,\n messageId: string,\n attachmentId: string,\n bytes: Uint8Array,\n /** When its message was received (ms), so it leaves the device with it. */\n sortAt?: number\n): Promise<void> {\n await put(\n 'attachment',\n await keyOf.attachment(accountId, messageId, attachmentId),\n await bucketOf.account(accountId),\n await sealBytes(bytes),\n sortAt || undefined\n )\n}\n\nexport async function hasAttachment(\n accountId: string,\n messageId: string,\n attachmentId: string\n): Promise<boolean> {\n return hasRecord(await keyOf.attachment(accountId, messageId, attachmentId))\n}\n\n/** An attachment's bytes, or undefined when it wasn't saved (too large, or evicted). */\nexport async function loadAttachment(\n accountId: string,\n messageId: string,\n attachmentId: string\n): Promise<Uint8Array | undefined> {\n const key = await keyOf.attachment(accountId, messageId, attachmentId)\n const record = await getRecord(key)\n if (!record) return undefined\n try {\n const bytes = await openBytes(record.data)\n void touchRecord(key)\n return bytes\n } catch {\n return undefined\n }\n}\n\nexport async function removeKeys(keys: string[]): Promise<void> {\n await deleteRecords(keys)\n}\n\n/** Searchable text of an opened message: its text part, else its HTML without tags, and file names. */\nfunction bodyText(body: MailMessage): string {\n const html = body.text ? '' : (body.html ?? '').replace(/<[^>]*>/g, ' ')\n return `${body.text} ${html} ${body.attachments.map((a) => a.name).join(' ')}`\n}\n\nconst folded = (value: string) => value.toLocaleLowerCase().normalize('NFKD')\n\n/**\n * Search a folder on the device: every word must appear in the subject, a participant or the body\n * (the same \"all words\" feel as the server's search).\n */\nexport async function searchFolder(\n accountId: string,\n folderId: string,\n query: string\n): Promise<MailMessage[]> {\n const words = folded(query).split(/\\s+/).filter(Boolean)\n const headers = await loadHeaders(accountId, folderId)\n if (words.length === 0) return headers.map((h) => h.message)\n const out: MailMessage[] = []\n for (const { message } of headers) {\n const people = [...message.sender, ...message.to, ...message.cc]\n .map((p) => `${p.name ?? ''} ${p.email}`)\n .join(' ')\n let haystack = folded(`${message.subject} ${people} ${message.preview}`)\n if (!words.every((w) => haystack.includes(w))) {\n const body = await loadBody(accountId, message.id)\n if (body) haystack += ' ' + folded(bodyText(body))\n }\n if (words.every((w) => haystack.includes(w))) out.push(message)\n }\n return out\n}\n\n/** Whether a folder is a view over the others (Flagged), with no messages of its own to keep. */\nexport const isVirtualFolder = (folder: Pick<MailFolder, 'role'> | undefined) =>\n folder?.role === 'flagged'\n\n/**\n * Messages saved on the device across the account's folders (`include` picks the folders), each\n * once (a Gmail message is listed under every label), newest first.\n */\nexport async function loadAccountMessages(\n accountId: string,\n include: (folder: MailFolder) => boolean,\n query = ''\n): Promise<MailMessage[]> {\n const seen = new Map<string, MailMessage>()\n for (const folder of (await loadFolders(accountId)) ?? []) {\n if (isVirtualFolder(folder) || !include(folder)) continue\n for (const message of await searchFolder(accountId, folder.id, query))\n if (!seen.has(message.id)) seen.set(message.id, message)\n }\n return [...seen.values()].sort((a, b) => receivedAt(b) - receivedAt(a))\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,167 @@
1
+ import { blind, openJson, sealJson } from "./crypto.js";
2
+ import { isNetworkFailure } from "../../offline-store/network.js";
3
+ import { deleteRecords, putRecord, recordsInBucket } from "./store.js";
4
+ import { notifyOfflineMailChange } from "./settings.js";
5
+ const bucket = () => blind("bucket", "outbox");
6
+ const keyFor = (id) => blind("outbox", id);
7
+ function outboxProblem(compose) {
8
+ const missing = compose.attachments.find((a) => a.content_base64 == null);
9
+ if (missing)
10
+ return `${missing.name} isn't saved on this device, so it can't be sent offline. Remove it or send once you're back online.`;
11
+ return null;
12
+ }
13
+ let seqCounter = 0;
14
+ async function write(item) {
15
+ await putRecord({
16
+ key: await keyFor(item.id),
17
+ kind: "outbox",
18
+ bucket: await bucket(),
19
+ sortAt: item.seq,
20
+ size: 0,
21
+ lastAccess: Date.now(),
22
+ data: await sealJson(item)
23
+ });
24
+ }
25
+ async function listOutbox() {
26
+ const records = await recordsInBucket(await bucket());
27
+ const items = [];
28
+ for (const record of records) {
29
+ try {
30
+ items.push(await openJson(record.data));
31
+ } catch {
32
+ }
33
+ }
34
+ return items.sort((a, b) => a.seq - b.seq);
35
+ }
36
+ async function enqueueOutbox(input) {
37
+ const problem = outboxProblem(input.compose);
38
+ if (problem) throw new Error(problem);
39
+ const now = Date.now();
40
+ seqCounter = Math.max(seqCounter + 1, now * 1e3);
41
+ const item = {
42
+ id: crypto.randomUUID(),
43
+ requestId: input.requestId,
44
+ account: input.account,
45
+ compose: input.compose,
46
+ secure: input.secure,
47
+ securePayload: input.securePayload,
48
+ status: "queued",
49
+ attempts: 0,
50
+ createdAt: now,
51
+ seq: seqCounter
52
+ };
53
+ await write(item);
54
+ notifyOfflineMailChange("outbox");
55
+ return item;
56
+ }
57
+ async function update(item, patch) {
58
+ const next = { ...item, ...patch };
59
+ await write(next);
60
+ notifyOfflineMailChange("outbox");
61
+ return next;
62
+ }
63
+ async function deleteOutboxItem(id) {
64
+ await deleteRecords([await keyFor(id)]);
65
+ notifyOfflineMailChange("outbox");
66
+ }
67
+ async function retryOutboxItem(id) {
68
+ const item = (await listOutbox()).find((i) => i.id === id);
69
+ if (item && item.status === "failed")
70
+ await update(item, { status: "queued", error: void 0, inDoubt: item.inDoubt });
71
+ }
72
+ const IN_DOUBT_MESSAGE = "The mail server didn't confirm this message. It may already have been sent: check Sent before sending it again.";
73
+ function classify(error) {
74
+ if (isNetworkFailure(error)) return { kind: "offline" };
75
+ const status = error?.status;
76
+ const detail = error instanceof Error ? error.message : "The message could not be sent";
77
+ if (status === void 0) return { kind: "failed", error: detail, inDoubt: false };
78
+ if (status === 409) return { kind: "failed", error: IN_DOUBT_MESSAGE, inDoubt: true };
79
+ if (status >= 500) return { kind: "failed", error: IN_DOUBT_MESSAGE, inDoubt: true };
80
+ return { kind: "failed", error: detail, inDoubt: false };
81
+ }
82
+ async function prepared(item, sender) {
83
+ if (!item.secure || item.securePayload !== void 0) return item;
84
+ return update(item, { securePayload: await sender.prepareSecure(item) });
85
+ }
86
+ async function attempt(item, sender) {
87
+ try {
88
+ const result = await sender.send(item);
89
+ return { kind: "sent", serverId: result?.id };
90
+ } catch (error) {
91
+ const outcome = classify(error);
92
+ if (error.status === 401) {
93
+ try {
94
+ await sender.unlock(item.account);
95
+ const result = await sender.send(item);
96
+ return { kind: "sent", serverId: result?.id };
97
+ } catch (retryError) {
98
+ return classify(retryError);
99
+ }
100
+ }
101
+ return outcome;
102
+ }
103
+ }
104
+ async function flushOutboxOnce(sender, isOnline) {
105
+ const result = { sent: 0, failed: 0, interrupted: false };
106
+ const unlocked = /* @__PURE__ */ new Set();
107
+ for (; ; ) {
108
+ if (!isOnline()) {
109
+ result.interrupted = true;
110
+ break;
111
+ }
112
+ const next = (await listOutbox()).find((i) => i.status === "queued" || i.status === "sending");
113
+ if (!next) break;
114
+ let item = await update(next, { status: "sending", attempts: next.attempts + 1 });
115
+ try {
116
+ item = await prepared(item, sender);
117
+ } catch (error) {
118
+ const message = error instanceof Error ? error.message : "Could not encrypt the message";
119
+ await update(item, { status: "failed", error: message });
120
+ result.failed++;
121
+ continue;
122
+ }
123
+ if (!unlocked.has(item.account.id)) {
124
+ await sender.unlock(item.account).catch(() => {
125
+ });
126
+ unlocked.add(item.account.id);
127
+ }
128
+ const outcome = await attempt(item, sender);
129
+ if (outcome.kind === "sent") {
130
+ item = await update(item, {
131
+ status: "sent",
132
+ sentAt: Date.now(),
133
+ error: void 0,
134
+ inDoubt: false
135
+ });
136
+ result.sent++;
137
+ } else if (outcome.kind === "offline") {
138
+ await update(item, { status: "queued" });
139
+ result.interrupted = true;
140
+ break;
141
+ } else {
142
+ await update(item, {
143
+ status: "failed",
144
+ error: outcome.error,
145
+ inDoubt: item.inDoubt || outcome.inDoubt
146
+ });
147
+ result.failed++;
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ async function purgeSentOutbox(olderThanMs = 0) {
153
+ const cutoff = Date.now() - olderThanMs;
154
+ const done = (await listOutbox()).filter((i) => i.status === "sent" && (i.sentAt ?? 0) <= cutoff);
155
+ if (done.length === 0) return;
156
+ await deleteRecords(await Promise.all(done.map((i) => keyFor(i.id))));
157
+ notifyOfflineMailChange("outbox");
158
+ }
159
+ export {
160
+ deleteOutboxItem,
161
+ enqueueOutbox,
162
+ flushOutboxOnce,
163
+ listOutbox,
164
+ purgeSentOutbox,
165
+ retryOutboxItem
166
+ };
167
+ //# sourceMappingURL=outbox.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mail/offline/outbox.ts"],
4
+ "sourcesContent": ["/**\n * The mail Outbox: messages the user pressed Send on while offline (or whose send could not reach\n * the server), kept encrypted on this device and sent in order once the connection is back.\n *\n * Exactly-once: every message keeps the `request_id` it was given when the user pressed Send, for\n * its whole life in the Outbox. The send endpoints deduplicate on it (Redis reservation plus a\n * durable record per request_id), so a replay after a lost response, a reload mid-send, or two open\n * tabs flushing at once returns the first result instead of sending again. A secure message is\n * prepared (encrypted, recipient keys generated) once and that exact payload is stored and replayed,\n * because the server fingerprints the ciphertext.\n *\n * Outcomes:\n * - network down / session not ready: the message goes back to \"queued\" and the flush stops, to\n * resume in order on the next reconnect;\n * - refused (4xx): \"failed\" with the server's reason; Retry resends the same request_id;\n * - unknown outcome (409 already attempted, 5xx, unconfirmed relay): \"failed\" and marked\n * `inDoubt` \u2014 the message may have gone out, so the UI says to check Sent; Retry is still safe\n * (same request_id), it reports \"sent\" if the first attempt completed.\n */\n\nimport type { MailAccount, MailCompose } from '../types'\nimport { blind, openJson, sealJson } from './crypto'\nimport { isNetworkFailure } from '../../offline-store/network'\nimport { deleteRecords, putRecord, recordsInBucket } from './store'\nimport { notifyOfflineMailChange } from './settings'\n\ntype OutboxStatus = 'queued' | 'sending' | 'sent' | 'failed'\n\nexport interface OutboxItem {\n id: string\n /** The idempotency key sent with every attempt. Never changes for this item. */\n requestId: string\n account: MailAccount\n compose: MailCompose\n secure: boolean\n /** The encrypted secure-delivery request, prepared on the first attempt and replayed as is. */\n securePayload?: unknown\n status: OutboxStatus\n error?: string\n /** The server may already have sent it (unknown outcome). */\n inDoubt?: boolean\n attempts: number\n createdAt: number\n sentAt?: number\n /** Enqueue order. */\n seq: number\n}\n\nexport interface OutboxSender {\n /** Prepare the secure payload for an item (only for secure items, once). */\n prepareSecure: (item: OutboxItem) => Promise<unknown>\n /** Make the account usable in this session before sending (connector tokens, IMAP credentials). */\n unlock: (account: MailAccount) => Promise<void>\n /** POST the message; resolves with the server's message id. */\n send: (item: OutboxItem) => Promise<{ id?: string }>\n}\n\nconst bucket = () => blind('bucket', 'outbox')\nconst keyFor = (id: string) => blind('outbox', id)\n\n/** Problems that make a message unsendable from the Outbox, reported when the user presses Send. */\nfunction outboxProblem(compose: MailCompose): string | null {\n const missing = compose.attachments.find((a) => a.content_base64 == null)\n if (missing)\n return `${missing.name} isn't saved on this device, so it can't be sent offline. Remove it or send once you're back online.`\n return null\n}\n\nlet seqCounter = 0\n\nasync function write(item: OutboxItem): Promise<void> {\n await putRecord({\n key: await keyFor(item.id),\n kind: 'outbox',\n bucket: await bucket(),\n sortAt: item.seq,\n size: 0,\n lastAccess: Date.now(),\n data: await sealJson(item),\n })\n}\n\nexport async function listOutbox(): Promise<OutboxItem[]> {\n const records = await recordsInBucket(await bucket())\n const items: OutboxItem[] = []\n for (const record of records) {\n try {\n items.push(await openJson<OutboxItem>(record.data))\n } catch {\n // Sealed under a different identity: not this user's mail; ignore.\n }\n }\n return items.sort((a, b) => a.seq - b.seq)\n}\n\nexport async function enqueueOutbox(input: {\n account: MailAccount\n compose: MailCompose\n requestId: string\n secure: boolean\n securePayload?: unknown\n}): Promise<OutboxItem> {\n const problem = outboxProblem(input.compose)\n if (problem) throw new Error(problem)\n const now = Date.now()\n // Monotonic even when two messages are queued in the same millisecond.\n seqCounter = Math.max(seqCounter + 1, now * 1000)\n const item: OutboxItem = {\n id: crypto.randomUUID(),\n requestId: input.requestId,\n account: input.account,\n compose: input.compose,\n secure: input.secure,\n securePayload: input.securePayload,\n status: 'queued',\n attempts: 0,\n createdAt: now,\n seq: seqCounter,\n }\n await write(item)\n notifyOfflineMailChange('outbox')\n return item\n}\n\nasync function update(item: OutboxItem, patch: Partial<OutboxItem>): Promise<OutboxItem> {\n const next = { ...item, ...patch }\n await write(next)\n notifyOfflineMailChange('outbox')\n return next\n}\n\nexport async function deleteOutboxItem(id: string): Promise<void> {\n await deleteRecords([await keyFor(id)])\n notifyOfflineMailChange('outbox')\n}\n\n/** Put a failed message back in the queue (same request_id, so it can't go out twice). */\nexport async function retryOutboxItem(id: string): Promise<void> {\n const item = (await listOutbox()).find((i) => i.id === id)\n if (item && item.status === 'failed')\n await update(item, { status: 'queued', error: undefined, inDoubt: item.inDoubt })\n}\n\ntype Outcome =\n | { kind: 'sent'; serverId?: string }\n | { kind: 'offline' }\n | { kind: 'failed'; error: string; inDoubt: boolean }\n\nconst IN_DOUBT_MESSAGE =\n \"The mail server didn't confirm this message. It may already have been sent: check Sent before sending it again.\"\n\nfunction classify(error: unknown): Outcome {\n // No answer at all: wait for the connection (the request may have arrived; the replay is safe).\n if (isNetworkFailure(error)) return { kind: 'offline' }\n const status = (error as { status?: number } | null)?.status\n const detail = error instanceof Error ? error.message : 'The message could not be sent'\n // Refused before any request left this device (e.g. the session must be signed in again).\n if (status === undefined) return { kind: 'failed', error: detail, inDoubt: false }\n if (status === 409) return { kind: 'failed', error: IN_DOUBT_MESSAGE, inDoubt: true }\n if (status >= 500) return { kind: 'failed', error: IN_DOUBT_MESSAGE, inDoubt: true }\n return { kind: 'failed', error: detail, inDoubt: false }\n}\n\n/** Prepare a secure message's encrypted request once, persisted before it is ever sent. */\nasync function prepared(item: OutboxItem, sender: OutboxSender): Promise<OutboxItem> {\n if (!item.secure || item.securePayload !== undefined) return item\n // Persist before the request: a replay must carry this exact ciphertext (the server\n // fingerprints it), so it is never prepared a second time.\n return update(item, { securePayload: await sender.prepareSecure(item) })\n}\n\nasync function attempt(item: OutboxItem, sender: OutboxSender): Promise<Outcome> {\n try {\n const result = await sender.send(item)\n return { kind: 'sent', serverId: result?.id }\n } catch (error) {\n const outcome = classify(error)\n // A session that hasn't been unlocked for this account yet: unlock once and replay.\n if ((error as { status?: number }).status === 401) {\n try {\n await sender.unlock(item.account)\n const result = await sender.send(item)\n return { kind: 'sent', serverId: result?.id }\n } catch (retryError) {\n return classify(retryError)\n }\n }\n return outcome\n }\n}\n\nexport interface FlushResult {\n sent: number\n failed: number\n /** Stopped early because the connection dropped; the rest stays queued. */\n interrupted: boolean\n}\n\n/**\n * Send every queued message in order. Messages left \"sending\" by a closed tab are replayed with\n * their request_id. Stops (keeping order) when the network goes away; a failed message does not\n * hold back the ones after it.\n */\nexport async function flushOutboxOnce(\n sender: OutboxSender,\n isOnline: () => boolean\n): Promise<FlushResult> {\n const result: FlushResult = { sent: 0, failed: 0, interrupted: false }\n const unlocked = new Set<string>()\n for (;;) {\n if (!isOnline()) {\n result.interrupted = true\n break\n }\n const next = (await listOutbox()).find((i) => i.status === 'queued' || i.status === 'sending')\n if (!next) break\n let item = await update(next, { status: 'sending', attempts: next.attempts + 1 })\n try {\n item = await prepared(item, sender)\n } catch (error) {\n // Local preparation failed (nothing was sent): a refusal the user can fix and retry.\n const message = error instanceof Error ? error.message : 'Could not encrypt the message'\n await update(item, { status: 'failed', error: message })\n result.failed++\n continue\n }\n if (!unlocked.has(item.account.id)) {\n // Connected accounts need this session's tokens; a failure here surfaces as the send's 401.\n await sender.unlock(item.account).catch(() => {})\n unlocked.add(item.account.id)\n }\n const outcome = await attempt(item, sender)\n if (outcome.kind === 'sent') {\n item = await update(item, {\n status: 'sent',\n sentAt: Date.now(),\n error: undefined,\n inDoubt: false,\n })\n result.sent++\n } else if (outcome.kind === 'offline') {\n await update(item, { status: 'queued' })\n result.interrupted = true\n break\n } else {\n await update(item, {\n status: 'failed',\n error: outcome.error,\n inDoubt: item.inDoubt || outcome.inDoubt,\n })\n result.failed++\n }\n }\n return result\n}\n\n/** Drop sent messages older than `olderThanMs` from the Outbox view. */\nexport async function purgeSentOutbox(olderThanMs = 0): Promise<void> {\n const cutoff = Date.now() - olderThanMs\n const done = (await listOutbox()).filter((i) => i.status === 'sent' && (i.sentAt ?? 0) <= cutoff)\n if (done.length === 0) return\n await deleteRecords(await Promise.all(done.map((i) => keyFor(i.id))))\n notifyOfflineMailChange('outbox')\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,159 @@
1
+ import { onlineManager } from "@tanstack/react-query";
2
+ import { logger } from "@arbidocs/client";
3
+ import {
4
+ accountPath,
5
+ conversationsPath,
6
+ mailJson,
7
+ MailOfflineUnavailableError,
8
+ threadPath
9
+ } from "../client.js";
10
+ import { mailConversationKey, groupMailConversations } from "../threads.js";
11
+ import {
12
+ hasFolder,
13
+ isVirtualFolder,
14
+ loadAccountMessages,
15
+ loadAccounts,
16
+ loadBody,
17
+ loadFolders,
18
+ loadPolicy,
19
+ saveAccounts,
20
+ saveBody,
21
+ saveFolders,
22
+ saveHeader,
23
+ savePolicy,
24
+ searchFolder
25
+ } from "./mailbox.js";
26
+ import { getOfflineMailSettings, inOfflineWindow } from "./settings.js";
27
+ import { overlayPendingMailChanges } from "./changes.js";
28
+ import { isNetworkFailure } from "../../offline-store/network.js";
29
+ import { sessionUser } from "./live.js";
30
+ const NOT_SAVED_HINT = "Choose how much mail to keep on this device in Mail settings to read it without a connection.";
31
+ async function onlineFirst(online, offline, unavailable) {
32
+ if (onlineManager.isOnline()) {
33
+ try {
34
+ return await online();
35
+ } catch (error) {
36
+ if (!isNetworkFailure(error)) throw error;
37
+ }
38
+ }
39
+ const saved = await offline().catch(() => void 0);
40
+ if (saved !== void 0) return saved;
41
+ throw new MailOfflineUnavailableError(unavailable);
42
+ }
43
+ async function deviceMessages(account, folderId, query) {
44
+ if (!isVirtualFolder(await savedFolder(account.id, folderId)))
45
+ return searchFolder(account.id, folderId, query);
46
+ const inView = (f) => account.provider === "imap" ? f.role === "inbox" : f.role !== "trash";
47
+ const all = await loadAccountMessages(account.id, inView, query);
48
+ return all.filter((m) => m.flagged);
49
+ }
50
+ async function savedFolder(accountId, folderId) {
51
+ return (await loadFolders(accountId) ?? []).find((f) => f.id === folderId);
52
+ }
53
+ function offlineMailReads(getArbi, { keepsMail = () => true } = {}) {
54
+ const call = (path) => mailJson((p, i) => getArbi().mail.fetch(p, i), path);
55
+ const settings = () => getOfflineMailSettings(sessionUser(getArbi()));
56
+ const storeEnabled = () => settings().enabled;
57
+ const keeps = (message) => inOfflineWindow(settings(), message.received_at);
58
+ const remember = (write) => {
59
+ if (!keepsMail()) return;
60
+ write().catch((error) => logger.warn("[offline-mail] could not save for offline use", error));
61
+ };
62
+ return {
63
+ getPolicy: () => onlineFirst(
64
+ async () => {
65
+ const policy = await call("/policy");
66
+ if (policy.enabled) remember(() => savePolicy(policy));
67
+ return policy;
68
+ },
69
+ loadPolicy,
70
+ "You're offline. Mail opens here once you've used it online on this device."
71
+ ),
72
+ listAccounts: () => onlineFirst(
73
+ async () => {
74
+ const accounts = await call("/accounts");
75
+ remember(() => saveAccounts(accounts));
76
+ return accounts;
77
+ },
78
+ loadAccounts,
79
+ "You're offline. Your mail accounts appear here once you've opened Mail online on this device."
80
+ ),
81
+ listFolders: (account) => onlineFirst(
82
+ async () => {
83
+ const folders = await call(`${accountPath(account.id)}/folders`);
84
+ if (storeEnabled()) remember(() => saveFolders(account.id, folders));
85
+ return folders;
86
+ },
87
+ () => loadFolders(account.id),
88
+ `You're offline and this mailbox isn't saved on this device. ${NOT_SAVED_HINT}`
89
+ ),
90
+ listMessages: (account, folderId, query, cursor) => onlineFirst(
91
+ async () => {
92
+ const page = await call(
93
+ `${accountPath(account.id)}/messages?${new URLSearchParams({ folder_id: folderId, q: query, ...cursor ? { cursor } : {} })}`
94
+ );
95
+ if (storeEnabled() && !query)
96
+ remember(async () => {
97
+ if (isVirtualFolder(await savedFolder(account.id, folderId))) return;
98
+ for (const message of page.messages)
99
+ if (keeps(message)) await saveHeader(account.id, folderId, message);
100
+ await overlayPendingMailChanges();
101
+ });
102
+ return page;
103
+ },
104
+ async () => {
105
+ if (!await hasFolder(account.id)) return void 0;
106
+ return { messages: await deviceMessages(account, folderId, query), fromDevice: true };
107
+ },
108
+ `You're offline and this folder isn't saved on this device. ${NOT_SAVED_HINT}`
109
+ ),
110
+ listConversations: (account, folderId, query, cursor) => onlineFirst(
111
+ async () => {
112
+ const page = await call(
113
+ conversationsPath(account.id, folderId, query, cursor)
114
+ );
115
+ if (storeEnabled() && !query)
116
+ remember(async () => {
117
+ if (isVirtualFolder(await savedFolder(account.id, folderId))) return;
118
+ for (const conversation of page.conversations)
119
+ for (const message of conversation.messages)
120
+ if (keeps(message)) await saveHeader(account.id, folderId, message);
121
+ await overlayPendingMailChanges();
122
+ });
123
+ return page;
124
+ },
125
+ async () => {
126
+ if (!await hasFolder(account.id)) return void 0;
127
+ const messages = await deviceMessages(account, folderId, query);
128
+ return { conversations: groupMailConversations(messages), fromDevice: true };
129
+ },
130
+ `You're offline and this folder isn't saved on this device. ${NOT_SAVED_HINT}`
131
+ ),
132
+ getThread: (account, threadId, folderId) => onlineFirst(
133
+ () => call(threadPath(account.id, threadId, folderId)),
134
+ async () => {
135
+ if (!await hasFolder(account.id)) return void 0;
136
+ const trash = (await savedFolder(account.id, folderId))?.role === "trash";
137
+ const messages = (await loadAccountMessages(account.id, (f) => f.role === "trash" === trash)).filter((m) => mailConversationKey(m) === threadId);
138
+ if (!messages.length) return void 0;
139
+ return { thread_id: threadId, messages: messages.reverse(), fromDevice: true };
140
+ },
141
+ "You're offline and this conversation isn't saved on this device."
142
+ ),
143
+ getMessage: (account, messageId) => onlineFirst(
144
+ async () => {
145
+ const message = await call(
146
+ `${accountPath(account.id)}/messages/${encodeURIComponent(messageId)}`
147
+ );
148
+ if (keeps(message)) remember(() => saveBody(account.id, message));
149
+ return message;
150
+ },
151
+ () => loadBody(account.id, messageId),
152
+ storeEnabled() ? "You're offline and this message hasn't been saved on this device yet." : `You're offline. ${NOT_SAVED_HINT}`
153
+ )
154
+ };
155
+ }
156
+ export {
157
+ offlineMailReads
158
+ };
159
+ //# sourceMappingURL=reads.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mail/offline/reads.ts"],
4
+ "sourcesContent": ["/**\n * Mail reads that keep working offline.\n *\n * Online, each read goes to the server as before; the result is also written to the encrypted\n * offline store \u2014 accounts and policy always (tiny, and needed to compose and queue mail offline),\n * folders and messages only while mail is kept on this device, and only messages received within\n * the chosen window (the last 7 days by default), so browsing older mail online does not grow it.\n * Offline (or when the request can't reach the server), the same read is answered from the store,\n * and when the store has nothing for it the error says so plainly instead of loading forever.\n *\n * The Mail queries use networkMode 'always' with these functions, so they run offline instead of\n * pausing (a paused query would show its loading state indefinitely).\n */\n\nimport { onlineManager } from '@tanstack/react-query'\nimport type { Arbi } from '@arbidocs/sdk/browser'\nimport { logger } from '@arbidocs/client'\nimport {\n accountPath,\n conversationsPath,\n mailJson,\n MailOfflineUnavailableError,\n threadPath,\n type MailClient,\n} from '../client'\nimport type {\n MailAccount,\n MailConversationPage,\n MailFolder,\n MailMessage,\n MailPage,\n MailPolicy,\n MailThread,\n} from '../types'\nimport { mailConversationKey, groupMailConversations } from '../threads'\nimport {\n hasFolder,\n isVirtualFolder,\n loadAccountMessages,\n loadAccounts,\n loadBody,\n loadFolders,\n loadPolicy,\n saveAccounts,\n saveBody,\n saveFolders,\n saveHeader,\n savePolicy,\n searchFolder,\n} from './mailbox'\nimport { getOfflineMailSettings, inOfflineWindow } from './settings'\nimport { overlayPendingMailChanges } from './changes'\nimport { isNetworkFailure } from '../../offline-store/network'\nimport { sessionUser } from './live'\n\nconst NOT_SAVED_HINT =\n 'Choose how much mail to keep on this device in Mail settings to read it without a connection.'\n\nasync function onlineFirst<T>(\n online: () => Promise<T>,\n offline: () => Promise<T | undefined>,\n unavailable: string\n): Promise<T> {\n if (onlineManager.isOnline()) {\n try {\n return await online()\n } catch (error) {\n if (!isNetworkFailure(error)) throw error\n }\n }\n const saved = await offline().catch(() => undefined)\n if (saved !== undefined) return saved\n throw new MailOfflineUnavailableError(unavailable)\n}\n\nexport interface OfflineMailReadsOptions {\n /**\n * Whether this device keeps mail at all (e.g. the person uses Mail): when it doesn't, reads are\n * never written to the device. Default: it does.\n */\n keepsMail?: () => boolean\n}\n\ntype MailReads = Pick<\n MailClient,\n | 'getPolicy'\n | 'listAccounts'\n | 'listFolders'\n | 'listMessages'\n | 'listConversations'\n | 'getThread'\n | 'getMessage'\n>\n\n/**\n * A folder's messages saved on the device, newest first. Flagged is every folder's flagged mail but\n * the Trash's, as the server lists it.\n */\nasync function deviceMessages(account: MailAccount, folderId: string, query: string) {\n if (!isVirtualFolder(await savedFolder(account.id, folderId)))\n return searchFolder(account.id, folderId, query)\n // As each server lists it: IMAP's Flagged is the Inbox's flagged mail (SEARCH FLAGGED there).\n const inView = (f: MailFolder) =>\n account.provider === 'imap' ? f.role === 'inbox' : f.role !== 'trash'\n const all = await loadAccountMessages(account.id, inView, query)\n return all.filter((m) => m.flagged)\n}\n\n/** The saved folder `folderId` (its role says whether it is the Trash, or a view like Flagged). */\nasync function savedFolder(accountId: string, folderId: string) {\n return ((await loadFolders(accountId)) ?? []).find((f) => f.id === folderId)\n}\n\n/**\n * The mail client's reads, kept working offline, on the session `getArbi()` returns (read per call,\n * so a renewed session is used). Pass them to `createMailClient` as overrides.\n */\nexport function offlineMailReads(\n getArbi: () => Arbi,\n { keepsMail = () => true }: OfflineMailReadsOptions = {}\n): MailReads {\n const call = <T>(path: string) => mailJson<T>((p, i) => getArbi().mail.fetch(p, i), path)\n const settings = () => getOfflineMailSettings(sessionUser(getArbi()))\n const storeEnabled = () => settings().enabled\n const keeps = (message: MailMessage) => inOfflineWindow(settings(), message.received_at)\n /** Write-through that never fails the read it piggybacks on. */\n const remember = (write: () => Promise<void>): void => {\n // Only for people who use Mail: everyone else never gets an offline mail store on their device.\n if (!keepsMail()) return\n write().catch((error) => logger.warn('[offline-mail] could not save for offline use', error))\n }\n return {\n getPolicy: () =>\n onlineFirst(\n async () => {\n const policy = await call<MailPolicy>('/policy')\n // Mail switched off on the deployment: nothing goes on the device.\n if (policy.enabled) remember(() => savePolicy(policy))\n return policy\n },\n loadPolicy,\n \"You're offline. Mail opens here once you've used it online on this device.\"\n ),\n listAccounts: () =>\n onlineFirst(\n async () => {\n const accounts = await call<MailAccount[]>('/accounts')\n remember(() => saveAccounts(accounts))\n return accounts\n },\n loadAccounts,\n \"You're offline. Your mail accounts appear here once you've opened Mail online on this device.\"\n ),\n listFolders: (account) =>\n onlineFirst(\n async () => {\n const folders = await call<MailFolder[]>(`${accountPath(account.id)}/folders`)\n if (storeEnabled()) remember(() => saveFolders(account.id, folders))\n return folders\n },\n () => loadFolders(account.id),\n `You're offline and this mailbox isn't saved on this device. ${NOT_SAVED_HINT}`\n ),\n listMessages: (account, folderId, query, cursor) =>\n onlineFirst<MailPage>(\n async () => {\n const page = await call<MailPage>(\n `${accountPath(account.id)}/messages?${new URLSearchParams({ folder_id: folderId, q: query, ...(cursor ? { cursor } : {}) })}`\n )\n // Search results are a subset; only the unfiltered folder list is a faithful copy.\n if (storeEnabled() && !query)\n remember(async () => {\n if (isVirtualFolder(await savedFolder(account.id, folderId))) return\n for (const message of page.messages)\n if (keeps(message)) await saveHeader(account.id, folderId, message)\n // A change still waiting must not be undone by the server's state just written.\n await overlayPendingMailChanges()\n })\n return page\n },\n async () => {\n if (!(await hasFolder(account.id))) return undefined\n return { messages: await deviceMessages(account, folderId, query), fromDevice: true }\n },\n `You're offline and this folder isn't saved on this device. ${NOT_SAVED_HINT}`\n ),\n listConversations: (account, folderId, query, cursor) =>\n onlineFirst<MailConversationPage>(\n async () => {\n const page = await call<MailConversationPage>(\n conversationsPath(account.id, folderId, query, cursor)\n )\n // As the message list: only an unfiltered real folder is a faithful copy of its rows.\n if (storeEnabled() && !query)\n remember(async () => {\n if (isVirtualFolder(await savedFolder(account.id, folderId))) return\n for (const conversation of page.conversations)\n for (const message of conversation.messages)\n if (keeps(message)) await saveHeader(account.id, folderId, message)\n await overlayPendingMailChanges()\n })\n return page\n },\n async () => {\n if (!(await hasFolder(account.id))) return undefined\n const messages = await deviceMessages(account, folderId, query)\n return { conversations: groupMailConversations(messages), fromDevice: true }\n },\n `You're offline and this folder isn't saved on this device. ${NOT_SAVED_HINT}`\n ),\n getThread: (account, threadId, folderId) =>\n onlineFirst<MailThread>(\n () => call<MailThread>(threadPath(account.id, threadId, folderId)),\n async () => {\n if (!(await hasFolder(account.id))) return undefined\n // Across folders but the Trash, unless it is the Trash being read: as the server does.\n const trash = (await savedFolder(account.id, folderId))?.role === 'trash'\n const messages = (\n await loadAccountMessages(account.id, (f) => (f.role === 'trash') === trash)\n ).filter((m) => mailConversationKey(m) === threadId)\n if (!messages.length) return undefined\n return { thread_id: threadId, messages: messages.reverse(), fromDevice: true }\n },\n \"You're offline and this conversation isn't saved on this device.\"\n ),\n getMessage: (account, messageId) =>\n onlineFirst(\n async () => {\n const message = await call<MailMessage>(\n `${accountPath(account.id)}/messages/${encodeURIComponent(messageId)}`\n )\n if (keeps(message)) remember(() => saveBody(account.id, message))\n return message\n },\n () => loadBody(account.id, messageId),\n storeEnabled()\n ? \"You're offline and this message hasn't been saved on this device yet.\"\n : `You're offline. ${NOT_SAVED_HINT}`\n ),\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,100 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { onlineManager, useQueryClient } from "@tanstack/react-query";
3
+ import { logger } from "@arbidocs/client";
4
+ import { useArbi } from "../../useArbi.js";
5
+ import { useFeature } from "../../host/features.js";
6
+ import { useIsEntitled } from "../../account/entitlements.js";
7
+ import { isUnderQueryKey } from "../../query-keys.js";
8
+ import { useArbiSession } from "../../session/signed-in.js";
9
+ import { mailQueryKeys, useMailPolicy } from "../hooks.js";
10
+ import {
11
+ flushOutbox,
12
+ sessionUser,
13
+ syncOfflineMailbox,
14
+ uploadOfflineDrafts
15
+ } from "./live.js";
16
+ import { getOfflineMailSettings, onOfflineMailChange } from "./settings.js";
17
+ import { trimOfflineMailbox } from "./sync.js";
18
+ import { claimOfflineMail } from "./wipe.js";
19
+ const SYNC_INTERVAL_MS = 5 * 60 * 1e3;
20
+ const NEW_MAIL_DEBOUNCE_MS = 5e3;
21
+ function useOfflineMailActive() {
22
+ const { user, status } = useArbiSession();
23
+ const mailEnabled = useFeature("mail");
24
+ const entitled = useIsEntitled("mail");
25
+ const eligible = status === "signed-in" && !!user?.external_id && mailEnabled && entitled;
26
+ const { data: policy } = useMailPolicy({ enabled: eligible });
27
+ return eligible && !!policy?.enabled && (policy.native_enabled || policy.enabled_providers.length > 0);
28
+ }
29
+ function useOfflineMailRuntime({ openOutbox } = {}) {
30
+ const { user, serverSessionKey } = useArbiSession();
31
+ const userExtId = user?.external_id;
32
+ const hasSession = !!serverSessionKey;
33
+ const active = useOfflineMailActive();
34
+ const queryClient = useQueryClient();
35
+ const arbi = useArbi();
36
+ const latest = useRef({ arbi, queryClient, openOutbox });
37
+ latest.current = { arbi, queryClient, openOutbox };
38
+ useEffect(() => {
39
+ if (!active || !userExtId) return;
40
+ claimOfflineMail(userExtId).catch(
41
+ (error) => logger.warn("[offline-mail] could not bind the store", error)
42
+ );
43
+ }, [active, userExtId]);
44
+ useEffect(() => {
45
+ if (!active || !userExtId) return;
46
+ return onOfflineMailChange((change) => {
47
+ if (change !== "settings") return;
48
+ trimOfflineMailbox(getOfflineMailSettings(userExtId)).then(
49
+ () => latest.current.queryClient.invalidateQueries({ queryKey: mailQueryKeys.allMessages() })
50
+ ).catch((error) => logger.warn("[offline-mail] could not trim the device copy", error));
51
+ });
52
+ }, [active, userExtId]);
53
+ useEffect(() => {
54
+ if (!active || !hasSession) return;
55
+ const context = () => latest.current;
56
+ const catchUp = async () => {
57
+ await flushOutbox(context());
58
+ await uploadOfflineDrafts(context());
59
+ await syncOfflineMailbox(context());
60
+ };
61
+ const run = () => {
62
+ catchUp().catch((error) => logger.warn("[offline-mail] catch-up failed", error));
63
+ };
64
+ run();
65
+ let wasOnline = onlineManager.isOnline();
66
+ const offOnline = onlineManager.subscribe((online) => {
67
+ if (online && !wasOnline) run();
68
+ wasOnline = online;
69
+ });
70
+ const interval = setInterval(() => {
71
+ if (document.visibilityState === "visible") run();
72
+ }, SYNC_INTERVAL_MS);
73
+ const enabled = () => getOfflineMailSettings(sessionUser(context().arbi)).enabled;
74
+ let newMailTimer = null;
75
+ const offCache = queryClient.getQueryCache().subscribe((event) => {
76
+ if (event.type !== "updated" || event.action.type !== "invalidate") return;
77
+ if (!isUnderQueryKey(event.query.queryKey, mailQueryKeys.all()) || !enabled()) return;
78
+ if (newMailTimer) clearTimeout(newMailTimer);
79
+ newMailTimer = setTimeout(() => void syncOfflineMailbox(context()), NEW_MAIL_DEBOUNCE_MS);
80
+ });
81
+ const offSettings = onOfflineMailChange((change) => {
82
+ if (change === "settings" && enabled()) void syncOfflineMailbox(context(), { full: true });
83
+ if (change === "outbox") void flushOutbox(context());
84
+ if (change === "changes") void flushOutbox(context());
85
+ if (change === "drafts") void uploadOfflineDrafts(context());
86
+ });
87
+ return () => {
88
+ offOnline();
89
+ clearInterval(interval);
90
+ if (newMailTimer) clearTimeout(newMailTimer);
91
+ offCache();
92
+ offSettings();
93
+ };
94
+ }, [active, hasSession, queryClient]);
95
+ }
96
+ export {
97
+ useOfflineMailActive,
98
+ useOfflineMailRuntime
99
+ };
100
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/mail/offline/runtime.ts"],
4
+ "sourcesContent": ["/**\n * useOfflineMailRuntime \u2014 mounted once by the host for a signed-in user with Mail switched on.\n * Wherever the user is in the app, it:\n * - binds the offline mail store to this account (another account's data on the device is removed);\n * - on reconnect (and at start, when online): sends the Outbox, applies the changes made offline\n * (read, moved, trashed, deleted), uploads offline drafts, then syncs the offline mailbox if it\n * is on for this device. Nothing goes out while the person chose \"Work offline\": that turns the\n * app's online state off, and every runner checks it before each request;\n * - keeps the mailbox in sync while online: every few minutes and shortly after new mail arrives\n * (the server's mail notification refreshes the Mail queries; that is the cue);\n * - when the user changes how much mail is kept, trims the device copy to it at once (offline too)\n * and, online, syncs fully so a longer window fills in.\n */\n\nimport { useEffect, useRef } from 'react'\nimport { onlineManager, useQueryClient } from '@tanstack/react-query'\nimport { logger } from '@arbidocs/client'\nimport { useArbi } from '../../useArbi'\nimport { useFeature } from '../../host/features'\nimport { useIsEntitled } from '../../account/entitlements'\nimport { isUnderQueryKey } from '../../query-keys'\nimport { useArbiSession } from '../../session/signed-in'\nimport { mailQueryKeys, useMailPolicy } from '../hooks'\nimport {\n flushOutbox,\n sessionUser,\n syncOfflineMailbox,\n uploadOfflineDrafts,\n type OfflineMailContext,\n} from './live'\nimport { getOfflineMailSettings, onOfflineMailChange } from './settings'\nimport { trimOfflineMailbox } from './sync'\nimport { claimOfflineMail } from './wipe'\n\nconst SYNC_INTERVAL_MS = 5 * 60 * 1000\nconst NEW_MAIL_DEBOUNCE_MS = 5_000\n\nexport interface OfflineMailRuntimeOptions {\n /** Show the Outbox, offered on the notice that a queued message could not be sent. */\n openOutbox?: () => void\n}\n\n/**\n * Whether mail is kept on this device for the signed-in user: Mail switched on for them, their plan\n * includes it, and the deployment offers mail (its native inbox or a mail provider). Entitlement\n * alone is not enough: it is granted wherever billing is off, mail or not. Nothing runs, and no\n * store is made, while this is false.\n */\nexport function useOfflineMailActive(): boolean {\n const { user, status } = useArbiSession()\n const mailEnabled = useFeature('mail')\n const entitled = useIsEntitled('mail')\n const eligible = status === 'signed-in' && !!user?.external_id && mailEnabled && entitled\n const { data: policy } = useMailPolicy({ enabled: eligible })\n return (\n eligible && !!policy?.enabled && (policy.native_enabled || policy.enabled_providers.length > 0)\n )\n}\n\nexport function useOfflineMailRuntime({ openOutbox }: OfflineMailRuntimeOptions = {}): void {\n const { user, serverSessionKey } = useArbiSession()\n const userExtId = user?.external_id\n const hasSession = !!serverSessionKey\n const active = useOfflineMailActive()\n const queryClient = useQueryClient()\n // A new `Arbi` (another workspace opened) is the same session: each run reads the current one.\n const arbi = useArbi()\n const latest = useRef<OfflineMailContext>({ arbi, queryClient, openOutbox })\n latest.current = { arbi, queryClient, openOutbox }\n\n useEffect(() => {\n if (!active || !userExtId) return\n claimOfflineMail(userExtId).catch((error) =>\n logger.warn('[offline-mail] could not bind the store', error)\n )\n }, [active, userExtId])\n\n // A shorter window (or Off) applies at once, with no network or session keys: only plaintext\n // receive times are read.\n useEffect(() => {\n if (!active || !userExtId) return\n return onOfflineMailChange((change) => {\n if (change !== 'settings') return\n trimOfflineMailbox(getOfflineMailSettings(userExtId))\n // Offline the lists come from the device: show what is left.\n .then(() =>\n latest.current.queryClient.invalidateQueries({ queryKey: mailQueryKeys.allMessages() })\n )\n .catch((error) => logger.warn('[offline-mail] could not trim the device copy', error))\n })\n }, [active, userExtId])\n\n useEffect(() => {\n if (!active || !hasSession) return\n const context = () => latest.current\n const catchUp = async () => {\n await flushOutbox(context())\n await uploadOfflineDrafts(context())\n await syncOfflineMailbox(context())\n }\n const run = () => {\n catchUp().catch((error) => logger.warn('[offline-mail] catch-up failed', error))\n }\n run()\n let wasOnline = onlineManager.isOnline()\n const offOnline = onlineManager.subscribe((online) => {\n if (online && !wasOnline) run()\n wasOnline = online\n })\n // What waits (an Outbox message or a change whose request got no answer while online) is tried\n // again with each regular sync, not only on reconnect.\n const interval = setInterval(() => {\n if (document.visibilityState === 'visible') run()\n }, SYNC_INTERVAL_MS)\n const enabled = () => getOfflineMailSettings(sessionUser(context().arbi)).enabled\n let newMailTimer: ReturnType<typeof setTimeout> | null = null\n const offCache = queryClient.getQueryCache().subscribe((event) => {\n if (event.type !== 'updated' || event.action.type !== 'invalidate') return\n if (!isUnderQueryKey(event.query.queryKey, mailQueryKeys.all()) || !enabled()) return\n if (newMailTimer) clearTimeout(newMailTimer)\n newMailTimer = setTimeout(() => void syncOfflineMailbox(context()), NEW_MAIL_DEBOUNCE_MS)\n })\n const offSettings = onOfflineMailChange((change) => {\n if (change === 'settings' && enabled()) void syncOfflineMailbox(context(), { full: true })\n if (change === 'outbox') void flushOutbox(context())\n if (change === 'changes') void flushOutbox(context())\n if (change === 'drafts') void uploadOfflineDrafts(context())\n })\n return () => {\n offOnline()\n clearInterval(interval)\n if (newMailTimer) clearTimeout(newMailTimer)\n offCache()\n offSettings()\n }\n }, [active, hasSession, queryClient])\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,84 @@
1
+ const DEFAULT_OFFLINE_MAIL_SETTINGS = {
2
+ enabled: true,
3
+ scope: "7d",
4
+ attachmentCapMb: 25,
5
+ quotaMb: 1024
6
+ };
7
+ const SCOPE_DAYS = {
8
+ "1d": 1,
9
+ "7d": 7,
10
+ "30d": 30,
11
+ "90d": 90
12
+ };
13
+ const DAY_MS = 24 * 60 * 60 * 1e3;
14
+ function offlineWindowStart(settings, now = Date.now()) {
15
+ return now - SCOPE_DAYS[settings.scope] * DAY_MS;
16
+ }
17
+ function inOfflineWindow(settings, receivedAt, now = Date.now()) {
18
+ const at = receivedAt ? Date.parse(receivedAt) || 0 : 0;
19
+ return settings.enabled && (!at || at >= offlineWindowStart(settings, now));
20
+ }
21
+ function normalize(saved) {
22
+ const merged = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, ...saved };
23
+ const scope = merged.scope;
24
+ if (scope in SCOPE_DAYS) return merged;
25
+ const longer = scope === "365d" || scope === "all";
26
+ return { ...merged, scope: longer ? "90d" : DEFAULT_OFFLINE_MAIL_SETTINGS.scope };
27
+ }
28
+ const STORAGE_PREFIX = "arbi-offline-mail-settings:";
29
+ const SIGNED_OUT = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, enabled: false };
30
+ function getOfflineMailSettings(userExtId) {
31
+ if (!userExtId) return SIGNED_OUT;
32
+ const key = STORAGE_PREFIX + userExtId;
33
+ try {
34
+ const raw = localStorage.getItem(key);
35
+ return raw ? normalize(JSON.parse(raw)) : DEFAULT_OFFLINE_MAIL_SETTINGS;
36
+ } catch {
37
+ return DEFAULT_OFFLINE_MAIL_SETTINGS;
38
+ }
39
+ }
40
+ function setOfflineMailSettings(userExtId, patch) {
41
+ const next = { ...getOfflineMailSettings(userExtId), ...patch };
42
+ if (userExtId) {
43
+ try {
44
+ localStorage.setItem(STORAGE_PREFIX + userExtId, JSON.stringify(next));
45
+ } catch {
46
+ }
47
+ }
48
+ notifyOfflineMailChange("settings");
49
+ return next;
50
+ }
51
+ const listeners = /* @__PURE__ */ new Set();
52
+ const CHANNEL = "arbi-offline-mail";
53
+ let channel = null;
54
+ function getChannel() {
55
+ if (channel || typeof BroadcastChannel === "undefined") return channel;
56
+ channel = new BroadcastChannel(CHANNEL);
57
+ channel.onmessage = (event) => {
58
+ for (const listener of listeners) listener(event.data);
59
+ };
60
+ return channel;
61
+ }
62
+ function notifyOfflineMailChange(change) {
63
+ for (const listener of listeners) listener(change);
64
+ try {
65
+ getChannel()?.postMessage(change);
66
+ } catch {
67
+ }
68
+ }
69
+ function onOfflineMailChange(listener) {
70
+ getChannel();
71
+ listeners.add(listener);
72
+ return () => listeners.delete(listener);
73
+ }
74
+ export {
75
+ DEFAULT_OFFLINE_MAIL_SETTINGS,
76
+ SCOPE_DAYS,
77
+ getOfflineMailSettings,
78
+ inOfflineWindow,
79
+ notifyOfflineMailChange,
80
+ offlineWindowStart,
81
+ onOfflineMailChange,
82
+ setOfflineMailSettings
83
+ };
84
+ //# sourceMappingURL=settings.js.map