@arbidocs/react 0.5.111 → 0.5.113
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.
- package/dist/account/client.js +96 -0
- package/dist/account/client.js.map +7 -0
- package/dist/account/delete.js +70 -0
- package/dist/account/delete.js.map +7 -0
- package/dist/account/didit.js +34 -0
- package/dist/account/didit.js.map +7 -0
- package/dist/account/dismissed-tips.js +33 -0
- package/dist/account/dismissed-tips.js.map +7 -0
- package/dist/account/email.js +84 -0
- package/dist/account/email.js.map +7 -0
- package/dist/account/entitlements.js +47 -0
- package/dist/account/entitlements.js.map +7 -0
- package/dist/account/identity.js +270 -0
- package/dist/account/identity.js.map +7 -0
- package/dist/account/index.js +93 -0
- package/dist/account/index.js.map +7 -0
- package/dist/account/notifications.js +59 -0
- package/dist/account/notifications.js.map +7 -0
- package/dist/account/password.js +103 -0
- package/dist/account/password.js.map +7 -0
- package/dist/account/profile.js +116 -0
- package/dist/account/profile.js.map +7 -0
- package/dist/account/recovery.js +13 -0
- package/dist/account/recovery.js.map +7 -0
- package/dist/account/relevance.js +82 -0
- package/dist/account/relevance.js.map +7 -0
- package/dist/account/sessions.js +37 -0
- package/dist/account/sessions.js.map +7 -0
- package/dist/account/settings.js +39 -0
- package/dist/account/settings.js.map +7 -0
- package/dist/account/table-views.js +16 -0
- package/dist/account/table-views.js.map +7 -0
- package/dist/ai/AiAnswer.js +83 -0
- package/dist/ai/AiAnswer.js.map +7 -0
- package/dist/ai/AiMarkdown.js +40 -0
- package/dist/ai/AiMarkdown.js.map +7 -0
- package/dist/ai/ArtifactCanvas.js +193 -0
- package/dist/ai/ArtifactCanvas.js.map +7 -0
- package/dist/ai/CitationRenderer.js +413 -0
- package/dist/ai/CitationRenderer.js.map +7 -0
- package/dist/{chunk-WL2Y2T47.js → ai/MarkdownRenderer.js} +35 -101
- package/dist/ai/MarkdownRenderer.js.map +7 -0
- package/dist/ai/MessageArtifactCard.js +75 -0
- package/dist/ai/MessageArtifactCard.js.map +7 -0
- package/dist/ai/PassageCard.js +79 -0
- package/dist/ai/PassageCard.js.map +7 -0
- package/dist/ai/engine.js +110 -0
- package/dist/ai/engine.js.map +7 -0
- package/dist/ai/index.js +63 -0
- package/dist/ai/index.js.map +7 -0
- package/dist/ai/markdown-base-components.js +37 -0
- package/dist/ai/markdown-base-components.js.map +7 -0
- package/dist/ai/markdown-streaming-context.js +6 -0
- package/dist/ai/markdown-streaming-context.js.map +7 -0
- package/dist/{chunk-VSCU7K4V.js → ai/normalize-citations.js} +12 -19
- package/dist/ai/normalize-citations.js.map +7 -0
- package/dist/ai/redline.js +41 -0
- package/dist/ai/redline.js.map +7 -0
- package/dist/ai/split-markdown-blocks.js +40 -0
- package/dist/ai/split-markdown-blocks.js.map +7 -0
- package/dist/{chunk-UW2SGTI5.js → ai/stored-markdown.js} +41 -93
- package/dist/ai/stored-markdown.js.map +7 -0
- package/dist/annotations/AnnotationPageOverlay.js +807 -0
- package/dist/annotations/AnnotationPageOverlay.js.map +7 -0
- package/dist/annotations/AnnotationToolbar.js +398 -0
- package/dist/annotations/AnnotationToolbar.js.map +7 -0
- package/dist/annotations/FreezeDialog.js +170 -0
- package/dist/annotations/FreezeDialog.js.map +7 -0
- package/dist/annotations/ProductionDialog.js +354 -0
- package/dist/annotations/ProductionDialog.js.map +7 -0
- package/dist/annotations/controller.js +49 -0
- package/dist/annotations/controller.js.map +7 -0
- package/dist/annotations/freeze/browser.js +21 -0
- package/dist/annotations/freeze/browser.js.map +7 -0
- package/dist/annotations/freeze/bundle.js +201 -0
- package/dist/annotations/freeze/bundle.js.map +7 -0
- package/dist/annotations/freeze/coords.js +37 -0
- package/dist/annotations/freeze/coords.js.map +7 -0
- package/dist/annotations/freeze/freeze.js +261 -0
- package/dist/annotations/freeze/freeze.js.map +7 -0
- package/dist/annotations/freeze/hash.js +28 -0
- package/dist/annotations/freeze/hash.js.map +7 -0
- package/dist/annotations/freeze/index-page.js +78 -0
- package/dist/annotations/freeze/index-page.js.map +7 -0
- package/dist/annotations/freeze/index.js +28 -22
- package/dist/annotations/freeze/index.js.map +7 -1
- package/dist/annotations/freeze/raster.js +208 -0
- package/dist/annotations/freeze/raster.js.map +7 -0
- package/dist/annotations/freeze/stamp.js +117 -0
- package/dist/annotations/freeze/stamp.js.map +7 -0
- package/dist/annotations/freeze/types.js +14 -0
- package/dist/annotations/freeze/types.js.map +7 -0
- package/dist/annotations/freeze/vector.js +283 -0
- package/dist/annotations/freeze/vector.js.map +7 -0
- package/dist/annotations/geometry.js +81 -0
- package/dist/annotations/geometry.js.map +7 -0
- package/dist/annotations/index.js +125 -6
- package/dist/annotations/index.js.map +7 -1
- package/dist/annotations/merge.js +53 -0
- package/dist/annotations/merge.js.map +7 -0
- package/dist/{chunk-T3USOSVN.js → annotations/model.js} +23 -16
- package/dist/annotations/model.js.map +7 -0
- package/dist/annotations/overlay-geometry.js +41 -0
- package/dist/annotations/overlay-geometry.js.map +7 -0
- package/dist/annotations/redaction-log.js +33 -0
- package/dist/annotations/redaction-log.js.map +7 -0
- package/dist/annotations/selection-marks.js +122 -0
- package/dist/annotations/selection-marks.js.map +7 -0
- package/dist/annotations/state.js +140 -0
- package/dist/annotations/state.js.map +7 -0
- package/dist/annotations/store.js +49 -0
- package/dist/annotations/store.js.map +7 -0
- package/dist/annotations/useAnnotationLayer.js +306 -0
- package/dist/annotations/useAnnotationLayer.js.map +7 -0
- package/dist/annotations/useAnnotationShortcuts.js +31 -0
- package/dist/annotations/useAnnotationShortcuts.js.map +7 -0
- package/dist/annotations/useTextSelectionMarks.js +100 -0
- package/dist/annotations/useTextSelectionMarks.js.map +7 -0
- package/dist/artifacts/artifact-kind.js +19 -0
- package/dist/artifacts/artifact-kind.js.map +7 -0
- package/dist/artifacts/index.js +89 -571
- package/dist/artifacts/index.js.map +7 -1
- package/dist/{chunk-WUTL62MC.js → artifacts/normalize-markdown.js} +12 -14
- package/dist/artifacts/normalize-markdown.js.map +7 -0
- package/dist/artifacts/redline-core.js +12 -3
- package/dist/artifacts/redline-core.js.map +7 -1
- package/dist/{chunk-74N436VK.js → artifacts/redline-diff.js} +10 -9
- package/dist/artifacts/redline-diff.js.map +7 -0
- package/dist/artifacts/redline-engine.js +14 -0
- package/dist/artifacts/redline-engine.js.map +7 -0
- package/dist/artifacts/renderArtifactMarkdown.js +9 -0
- package/dist/artifacts/renderArtifactMarkdown.js.map +7 -0
- package/dist/artifacts/useArtifactEditSession.js +183 -0
- package/dist/artifacts/useArtifactEditSession.js.map +7 -0
- package/dist/artifacts/useArtifactSession.js +173 -0
- package/dist/artifacts/useArtifactSession.js.map +7 -0
- package/dist/artifacts/useArtifactSource.js +16 -0
- package/dist/artifacts/useArtifactSource.js.map +7 -0
- package/dist/artifacts/useArtifactText.js +78 -0
- package/dist/artifacts/useArtifactText.js.map +7 -0
- package/dist/artifacts/useRedline.js +65 -0
- package/dist/artifacts/useRedline.js.map +7 -0
- package/dist/artifacts/useStreamedArtifact.js +74 -0
- package/dist/artifacts/useStreamedArtifact.js.map +7 -0
- package/dist/artifacts/versions.js +68 -0
- package/dist/artifacts/versions.js.map +7 -0
- package/dist/billing.js +200 -0
- package/dist/billing.js.map +7 -0
- package/dist/boot-timeout.js +19 -0
- package/dist/boot-timeout.js.map +7 -0
- package/dist/calendar/accounts.js +213 -0
- package/dist/calendar/accounts.js.map +7 -0
- package/dist/calendar/hooks.js +409 -0
- package/dist/calendar/hooks.js.map +7 -0
- package/dist/calendar/index.js +31 -0
- package/dist/calendar/index.js.map +7 -0
- package/dist/calendar/keys.js +5 -0
- package/dist/calendar/keys.js.map +7 -0
- package/dist/calendar/offline/outbox.js +231 -0
- package/dist/calendar/offline/outbox.js.map +7 -0
- package/dist/calendar/offline/runtime.js +109 -0
- package/dist/calendar/offline/runtime.js.map +7 -0
- package/dist/calendar/offline/support.js +41 -0
- package/dist/calendar/offline/support.js.map +7 -0
- package/dist/calendar/support.js +34 -0
- package/dist/calendar/support.js.map +7 -0
- package/dist/calls/actions.js +226 -0
- package/dist/calls/actions.js.map +7 -0
- package/dist/calls/events.js +138 -0
- package/dist/calls/events.js.map +7 -0
- package/dist/calls/gate.js +19 -0
- package/dist/calls/gate.js.map +7 -0
- package/dist/calls/hooks.js +115 -0
- package/dist/calls/hooks.js.map +7 -0
- package/dist/calls/in-call-chat.js +132 -0
- package/dist/calls/in-call-chat.js.map +7 -0
- package/dist/calls/index.js +24 -0
- package/dist/calls/index.js.map +7 -0
- package/dist/calls/keys.js +17 -0
- package/dist/calls/keys.js.map +7 -0
- package/dist/calls/livekit/index.js +6 -958
- package/dist/calls/livekit/index.js.map +7 -1
- package/dist/calls/livekit/room.js +98 -0
- package/dist/calls/livekit/room.js.map +7 -0
- package/dist/calls/livekit/useCallRecorder.js +219 -0
- package/dist/calls/livekit/useCallRecorder.js.map +7 -0
- package/dist/calls/livekit/useCallTranscriber.js +309 -0
- package/dist/calls/livekit/useCallTranscriber.js.map +7 -0
- package/dist/calls/livekit/useHostMuteNotice.js +29 -0
- package/dist/calls/livekit/useHostMuteNotice.js.map +7 -0
- package/dist/calls/livekit/useInCallChat.js +161 -0
- package/dist/calls/livekit/useInCallChat.js.map +7 -0
- package/dist/calls/livekit/useWebinar.js +239 -0
- package/dist/calls/livekit/useWebinar.js.map +7 -0
- package/dist/calls/meeting-chat.js +149 -0
- package/dist/calls/meeting-chat.js.map +7 -0
- package/dist/calls/moderation.js +50 -0
- package/dist/calls/moderation.js.map +7 -0
- package/dist/calls/open-meeting-chat.js +29 -0
- package/dist/calls/open-meeting-chat.js.map +7 -0
- package/dist/calls/read-only.js +24 -0
- package/dist/calls/read-only.js.map +7 -0
- package/dist/calls/recording-media.js +218 -0
- package/dist/calls/recording-media.js.map +7 -0
- package/dist/calls/recording-playback.js +260 -0
- package/dist/calls/recording-playback.js.map +7 -0
- package/dist/calls/recording.js +268 -0
- package/dist/calls/recording.js.map +7 -0
- package/dist/calls/save-to-thread.js +80 -0
- package/dist/calls/save-to-thread.js.map +7 -0
- package/dist/calls/session.js +134 -0
- package/dist/calls/session.js.map +7 -0
- package/dist/calls/transcript-format.js +147 -0
- package/dist/calls/transcript-format.js.map +7 -0
- package/dist/calls/transcription.js +101 -0
- package/dist/calls/transcription.js.map +7 -0
- package/dist/calls/webinar.js +71 -0
- package/dist/calls/webinar.js.map +7 -0
- package/dist/charge-watch.js +71 -0
- package/dist/charge-watch.js.map +7 -0
- package/dist/chat/ChatBubble.js +146 -0
- package/dist/chat/ChatBubble.js.map +7 -0
- package/dist/chat/ConversationScope.js +30 -0
- package/dist/chat/ConversationScope.js.map +7 -0
- package/dist/chat/active-config.js +20 -0
- package/dist/chat/active-config.js.map +7 -0
- package/dist/chat/agent-chat-owner.js +25 -0
- package/dist/chat/agent-chat-owner.js.map +7 -0
- package/dist/chat/chat-artifacts.js +64 -0
- package/dist/chat/chat-artifacts.js.map +7 -0
- package/dist/chat/citations.js +32 -0
- package/dist/chat/citations.js.map +7 -0
- package/dist/chat/config-model.js +10 -0
- package/dist/chat/config-model.js.map +7 -0
- package/dist/chat/conversation-cache.js +676 -0
- package/dist/chat/conversation-cache.js.map +7 -0
- package/dist/chat/conversation-events.js +57 -0
- package/dist/chat/conversation-events.js.map +7 -0
- package/dist/chat/conversation-mentions.js +58 -0
- package/dist/chat/conversation-mentions.js.map +7 -0
- package/dist/chat/conversation-workspace.js +58 -0
- package/dist/chat/conversation-workspace.js.map +7 -0
- package/dist/chat/mentions.js +116 -0
- package/dist/chat/mentions.js.map +7 -0
- package/dist/chat/open-document.js +106 -0
- package/dist/chat/open-document.js.map +7 -0
- package/dist/chat/orphaned.js +44 -0
- package/dist/chat/orphaned.js.map +7 -0
- package/dist/chat/pendingFirstTurn.js +30 -0
- package/dist/chat/pendingFirstTurn.js.map +7 -0
- package/dist/chat/reply-target.js +12 -0
- package/dist/chat/reply-target.js.map +7 -0
- package/dist/chat/run-finished.js +28 -0
- package/dist/chat/run-finished.js.map +7 -0
- package/dist/chat/run-state.js +91 -0
- package/dist/chat/run-state.js.map +7 -0
- package/dist/chat/stream-handlers.js +419 -0
- package/dist/chat/stream-handlers.js.map +7 -0
- package/dist/chat/thread-binding.js +76 -0
- package/dist/chat/thread-binding.js.map +7 -0
- package/dist/chat/thread-document.js +53 -0
- package/dist/chat/thread-document.js.map +7 -0
- package/dist/chat/turn-control.js +79 -0
- package/dist/chat/turn-control.js.map +7 -0
- package/dist/chat/useAgentRunControl.js +36 -0
- package/dist/chat/useAgentRunControl.js.map +7 -0
- package/dist/chat/useAnswerAgentQuestion.js +19 -0
- package/dist/chat/useAnswerAgentQuestion.js.map +7 -0
- package/dist/chat/useAskAgain.js +32 -0
- package/dist/chat/useAskAgain.js.map +7 -0
- package/dist/chat/useAutoSendDraft.js +17 -0
- package/dist/chat/useAutoSendDraft.js.map +7 -0
- package/dist/chat/useBranchNavigation.js +97 -0
- package/dist/chat/useBranchNavigation.js.map +7 -0
- package/dist/chat/useConnectRunStream.js +169 -0
- package/dist/chat/useConnectRunStream.js.map +7 -0
- package/dist/chat/useConversation.js +431 -0
- package/dist/chat/useConversation.js.map +7 -0
- package/dist/chat/useConversationActions.js +662 -0
- package/dist/chat/useConversationActions.js.map +7 -0
- package/dist/chat/useConversationData.js +143 -0
- package/dist/chat/useConversationData.js.map +7 -0
- package/dist/chat/useConversationDocuments.js +41 -0
- package/dist/chat/useConversationDocuments.js.map +7 -0
- package/dist/chat/useConversationSettings.js +78 -0
- package/dist/chat/useConversationSettings.js.map +7 -0
- package/dist/chat/useConversationTurn.js +31 -0
- package/dist/chat/useConversationTurn.js.map +7 -0
- package/dist/chat/useConversationWorkspace.js +12 -0
- package/dist/chat/useConversationWorkspace.js.map +7 -0
- package/dist/chat/useDeleteMessage.js +15 -0
- package/dist/chat/useDeleteMessage.js.map +7 -0
- package/dist/chat/useDocumentAutoAdd.js +73 -0
- package/dist/chat/useDocumentAutoAdd.js.map +7 -0
- package/dist/chat/useMessageTimer.js +39 -0
- package/dist/chat/useMessageTimer.js.map +7 -0
- package/dist/chat/useThinkingContent.js +67 -0
- package/dist/chat/useThinkingContent.js.map +7 -0
- package/dist/chat/useThreadDocument.js +61 -0
- package/dist/chat/useThreadDocument.js.map +7 -0
- package/dist/chat/workflow-run-titles.js +50 -0
- package/dist/chat/workflow-run-titles.js.map +7 -0
- package/dist/config/config-hooks.js +209 -0
- package/dist/config/config-hooks.js.map +7 -0
- package/dist/connectivity.js +72 -0
- package/dist/connectivity.js.map +7 -0
- package/dist/connectors/hooks.js +263 -0
- package/dist/connectors/hooks.js.map +7 -0
- package/dist/connectors/index.js +28 -0
- package/dist/connectors/index.js.map +7 -0
- package/dist/contacts.js +100 -0
- package/dist/contacts.js.map +7 -0
- package/dist/conversation-list.js +65 -0
- package/dist/conversation-list.js.map +7 -0
- package/dist/databases.js +195 -0
- package/dist/databases.js.map +7 -0
- package/dist/datasets/aggregate.js +281 -0
- package/dist/datasets/aggregate.js.map +7 -0
- package/dist/datasets/index.js +122 -0
- package/dist/datasets/index.js.map +7 -0
- package/dist/datasets/members.js +27 -0
- package/dist/datasets/members.js.map +7 -0
- package/dist/datasets/panel.js +44 -0
- package/dist/datasets/panel.js.map +7 -0
- package/dist/datasets/registry.js +47 -0
- package/dist/datasets/registry.js.map +7 -0
- package/dist/datasets/rows-source.js +44 -0
- package/dist/datasets/rows-source.js.map +7 -0
- package/dist/datasets/sources/activity.js +83 -0
- package/dist/datasets/sources/activity.js.map +7 -0
- package/dist/datasets/sources/conversations.js +74 -0
- package/dist/datasets/sources/conversations.js.map +7 -0
- package/dist/datasets/sources/database.js +209 -0
- package/dist/datasets/sources/database.js.map +7 -0
- package/dist/datasets/sources/documents.js +93 -0
- package/dist/datasets/sources/documents.js.map +7 -0
- package/dist/datasets/sources/facts.js +56 -0
- package/dist/datasets/sources/facts.js.map +7 -0
- package/dist/datasets/sources/projects.js +117 -0
- package/dist/datasets/sources/projects.js.map +7 -0
- package/dist/datasets/sources/tags.js +67 -0
- package/dist/datasets/sources/tags.js.map +7 -0
- package/dist/datasets/sources/usage.js +268 -0
- package/dist/datasets/sources/usage.js.map +7 -0
- package/dist/datasets/sources/workflows.js +91 -0
- package/dist/datasets/sources/workflows.js.map +7 -0
- package/dist/datasets/sources/workspaces.js +67 -0
- package/dist/datasets/sources/workspaces.js.map +7 -0
- package/dist/datasets/store.js +194 -0
- package/dist/datasets/store.js.map +7 -0
- package/dist/datasets/types.js +14 -0
- package/dist/datasets/types.js.map +7 -0
- package/dist/directmessages/ConversationList.js +148 -0
- package/dist/directmessages/ConversationList.js.map +7 -0
- package/dist/directmessages/DateSeparator.js +8 -0
- package/dist/directmessages/DateSeparator.js.map +7 -0
- package/dist/directmessages/DmAvatar.js +29 -0
- package/dist/directmessages/DmAvatar.js.map +7 -0
- package/dist/directmessages/DmComposer.js +201 -0
- package/dist/directmessages/DmComposer.js.map +7 -0
- package/dist/directmessages/arrivals.js +29 -0
- package/dist/directmessages/arrivals.js.map +7 -0
- package/dist/directmessages/chat-cache.js +192 -0
- package/dist/directmessages/chat-cache.js.map +7 -0
- package/dist/directmessages/grouping.js +26 -0
- package/dist/directmessages/grouping.js.map +7 -0
- package/dist/directmessages/index.js +81 -0
- package/dist/directmessages/index.js.map +7 -0
- package/dist/directmessages/useChats.js +266 -0
- package/dist/directmessages/useChats.js.map +7 -0
- package/dist/directmessages/utils.js +23 -0
- package/dist/directmessages/utils.js.map +7 -0
- package/dist/document-tools/can-annotate.js +7 -0
- package/dist/document-tools/can-annotate.js.map +7 -0
- package/dist/document-tools/core.js +218 -12
- package/dist/document-tools/core.js.map +7 -1
- package/dist/document-tools/document-pdf.js +76 -0
- package/dist/document-tools/document-pdf.js.map +7 -0
- package/dist/document-tools/environment.js +136 -0
- package/dist/document-tools/environment.js.map +7 -0
- package/dist/document-tools/file-shares.js +112 -0
- package/dist/document-tools/file-shares.js.map +7 -0
- package/dist/document-tools/frozen-copy.js +27 -0
- package/dist/document-tools/frozen-copy.js.map +7 -0
- package/dist/document-tools/index.js +26 -148
- package/dist/document-tools/index.js.map +7 -1
- package/dist/document-tools/markup-download.js +51 -0
- package/dist/document-tools/markup-download.js.map +7 -0
- package/dist/document-tools/markup-store.js +138 -0
- package/dist/document-tools/markup-store.js.map +7 -0
- package/dist/document-tools/markup-transfer.js +88 -0
- package/dist/document-tools/markup-transfer.js.map +7 -0
- package/dist/document-tools/production.js +147 -0
- package/dist/document-tools/production.js.map +7 -0
- package/dist/document-tools/saved-signature.js +90 -0
- package/dist/document-tools/saved-signature.js.map +7 -0
- package/dist/document-tools/signature-requests.js +310 -0
- package/dist/document-tools/signature-requests.js.map +7 -0
- package/dist/document-tools/signing.js +127 -0
- package/dist/document-tools/signing.js.map +7 -0
- package/dist/document-tools/useDocumentBytes.js +38 -0
- package/dist/document-tools/useDocumentBytes.js.map +7 -0
- package/dist/document-tools/useDocumentMarkup.js +154 -0
- package/dist/document-tools/useDocumentMarkup.js.map +7 -0
- package/dist/document-tools/useIsOnline.js +13 -0
- package/dist/document-tools/useIsOnline.js.map +7 -0
- package/dist/document-tools/verification.js +60 -0
- package/dist/document-tools/verification.js.map +7 -0
- package/dist/documents/DocumentReview.js +220 -0
- package/dist/documents/DocumentReview.js.map +7 -0
- package/dist/documents/DraftTitleControls.js +41 -0
- package/dist/documents/DraftTitleControls.js.map +7 -0
- package/dist/documents/InlineMicButton.js +90 -0
- package/dist/documents/InlineMicButton.js.map +7 -0
- package/dist/documents/MicRecordingOverlay.js +29 -0
- package/dist/documents/MicRecordingOverlay.js.map +7 -0
- package/dist/documents/MicWaveformIndicator.js +68 -0
- package/dist/documents/MicWaveformIndicator.js.map +7 -0
- package/dist/documents/PhotoCapture.js +120 -0
- package/dist/documents/PhotoCapture.js.map +7 -0
- package/dist/documents/VideoCapture.js +129 -0
- package/dist/documents/VideoCapture.js.map +7 -0
- package/dist/documents/VoiceNoteRecorder.js +166 -0
- package/dist/documents/VoiceNoteRecorder.js.map +7 -0
- package/dist/documents/audio-segmenter.js +63 -0
- package/dist/documents/audio-segmenter.js.map +7 -0
- package/dist/documents/capture-frame.js +21 -0
- package/dist/documents/capture-frame.js.map +7 -0
- package/dist/documents/chunked-recorder.js +190 -0
- package/dist/documents/chunked-recorder.js.map +7 -0
- package/dist/documents/document-download.js +53 -0
- package/dist/documents/document-download.js.map +7 -0
- package/dist/documents/document-fields.js +109 -0
- package/dist/documents/document-fields.js.map +7 -0
- package/dist/documents/documents-cache.js +110 -0
- package/dist/documents/documents-cache.js.map +7 -0
- package/dist/documents/documents-data.js +128 -0
- package/dist/documents/documents-data.js.map +7 -0
- package/dist/documents/documents-loader.js +113 -0
- package/dist/documents/documents-loader.js.map +7 -0
- package/dist/documents/documents-writes.js +448 -0
- package/dist/documents/documents-writes.js.map +7 -0
- package/dist/documents/format-elapsed.js +10 -0
- package/dist/documents/format-elapsed.js.map +7 -0
- package/dist/documents/has-video-track.js +26 -0
- package/dist/documents/has-video-track.js.map +7 -0
- package/dist/documents/index.js +266 -0
- package/dist/documents/index.js.map +7 -0
- package/dist/documents/media-error.js +18 -0
- package/dist/documents/media-error.js.map +7 -0
- package/dist/documents/offline-documents.js +160 -0
- package/dist/documents/offline-documents.js.map +7 -0
- package/dist/documents/pending-sync.js +210 -0
- package/dist/documents/pending-sync.js.map +7 -0
- package/dist/documents/processing-live.js +122 -0
- package/dist/documents/processing-live.js.map +7 -0
- package/dist/documents/processing-order.js +59 -0
- package/dist/documents/processing-order.js.map +7 -0
- package/dist/documents/processing-settle.js +21 -0
- package/dist/documents/processing-settle.js.map +7 -0
- package/dist/documents/related-documents.js +133 -0
- package/dist/documents/related-documents.js.map +7 -0
- package/dist/documents/support.js +29 -0
- package/dist/documents/support.js.map +7 -0
- package/dist/documents/upload-files.js +169 -0
- package/dist/documents/upload-files.js.map +7 -0
- package/dist/documents/useAudioRecorder.js +315 -0
- package/dist/documents/useAudioRecorder.js.map +7 -0
- package/dist/documents/useCameraStream.js +35 -0
- package/dist/documents/useCameraStream.js.map +7 -0
- package/dist/documents/useDocumentThumbnails.js +71 -0
- package/dist/documents/useDocumentThumbnails.js.map +7 -0
- package/dist/documents/useDownloadDocuments.js +90 -0
- package/dist/documents/useDownloadDocuments.js.map +7 -0
- package/dist/documents/usePassageQuotes.js +37 -0
- package/dist/documents/usePassageQuotes.js.map +7 -0
- package/dist/documents/useUploadFiles.js +165 -0
- package/dist/documents/useUploadFiles.js.map +7 -0
- package/dist/documents/useUploadFromUrls.js +63 -0
- package/dist/documents/useUploadFromUrls.js.map +7 -0
- package/dist/documents/useVideoRecorder.js +62 -0
- package/dist/documents/useVideoRecorder.js.map +7 -0
- package/dist/entities/AgentMarkers.js +61 -0
- package/dist/entities/AgentMarkers.js.map +7 -0
- package/dist/entities/DocumentEntity.js +125 -0
- package/dist/entities/DocumentEntity.js.map +7 -0
- package/dist/entities/EntityPill.js +250 -0
- package/dist/entities/EntityPill.js.map +7 -0
- package/dist/entities/ProjectEntity.js +61 -0
- package/dist/entities/ProjectEntity.js.map +7 -0
- package/dist/entities/ShieldIcon.js +49 -0
- package/dist/entities/ShieldIcon.js.map +7 -0
- package/dist/entities/TagEntity.js +79 -0
- package/dist/entities/TagEntity.js.map +7 -0
- package/dist/entities/ThreadVisibilityIcon.js +15 -0
- package/dist/entities/ThreadVisibilityIcon.js.map +7 -0
- package/dist/entities/UserEntity.js +211 -0
- package/dist/entities/UserEntity.js.map +7 -0
- package/dist/entities/VerifiedBadge.js +19 -0
- package/dist/entities/VerifiedBadge.js.map +7 -0
- package/dist/entities/agent-avatar-colors.js +26 -0
- package/dist/entities/agent-avatar-colors.js.map +7 -0
- package/dist/entities/colors.js +64 -0
- package/dist/entities/colors.js.map +7 -0
- package/dist/entities/file-type.js +151 -0
- package/dist/entities/file-type.js.map +7 -0
- package/dist/entities/index.js +100 -0
- package/dist/entities/index.js.map +7 -0
- package/dist/entities/media-type.js +61 -0
- package/dist/entities/media-type.js.map +7 -0
- package/dist/entities/registry.js +385 -0
- package/dist/entities/registry.js.map +7 -0
- package/dist/entities/status.js +43 -0
- package/dist/entities/status.js.map +7 -0
- package/dist/entities/tag-kinds.js +23 -0
- package/dist/entities/tag-kinds.js.map +7 -0
- package/dist/entities/tag-types.js +44 -0
- package/dist/entities/tag-types.js.map +7 -0
- package/dist/entities/vocabulary.js +12 -0
- package/dist/entities/vocabulary.js.map +7 -0
- package/dist/entity-extraction.js +25 -0
- package/dist/entity-extraction.js.map +7 -0
- package/dist/facts-search.js +57 -0
- package/dist/facts-search.js.map +7 -0
- package/dist/facts.js +69 -0
- package/dist/facts.js.map +7 -0
- package/dist/hooks.js +320 -0
- package/dist/hooks.js.map +7 -0
- package/dist/host/ArbiHost.js +24 -0
- package/dist/host/ArbiHost.js.map +7 -0
- package/dist/host/assistant-copy.js +54 -0
- package/dist/host/assistant-copy.js.map +7 -0
- package/dist/host/config.js +9 -0
- package/dist/host/config.js.map +7 -0
- package/dist/host/context.js +10 -0
- package/dist/host/context.js.map +7 -0
- package/dist/host/features.js +144 -0
- package/dist/host/features.js.map +7 -0
- package/dist/host/index.js +12 -0
- package/dist/host/index.js.map +7 -0
- package/dist/{chunk-DPYBZDJB.js → host/merge.js} +7 -7
- package/dist/host/merge.js.map +7 -0
- package/dist/house-template.js +40 -0
- package/dist/house-template.js.map +7 -0
- package/dist/icons/AppleIcon.js +25 -0
- package/dist/icons/AppleIcon.js.map +7 -0
- package/dist/icons/ArbiBotHeadIcon.js +46 -0
- package/dist/icons/ArbiBotHeadIcon.js.map +7 -0
- package/dist/icons/ArbiIcon.js +24 -0
- package/dist/icons/ArbiIcon.js.map +7 -0
- package/dist/icons/AudIcon.js +56 -0
- package/dist/icons/AudIcon.js.map +7 -0
- package/dist/icons/ClaudeIcon.js +28 -0
- package/dist/icons/ClaudeIcon.js.map +7 -0
- package/dist/icons/DocIcon.js +56 -0
- package/dist/icons/DocIcon.js.map +7 -0
- package/dist/icons/EmlIcon.js +57 -0
- package/dist/icons/EmlIcon.js.map +7 -0
- package/dist/icons/FireworksIcon.js +47 -0
- package/dist/icons/FireworksIcon.js.map +7 -0
- package/dist/icons/GeminiIcon.js +37 -0
- package/dist/icons/GeminiIcon.js.map +7 -0
- package/dist/icons/GitHubIcon.js +32 -0
- package/dist/icons/GitHubIcon.js.map +7 -0
- package/dist/icons/GoogleIcon.js +53 -0
- package/dist/icons/GoogleIcon.js.map +7 -0
- package/dist/icons/HtmlIcon.js +56 -0
- package/dist/icons/HtmlIcon.js.map +7 -0
- package/dist/icons/ImgIcon.js +56 -0
- package/dist/icons/ImgIcon.js.map +7 -0
- package/dist/icons/LinkedInIcon.js +25 -0
- package/dist/icons/LinkedInIcon.js.map +7 -0
- package/dist/icons/OpenAIIcon.js +28 -0
- package/dist/icons/OpenAIIcon.js.map +7 -0
- package/dist/icons/PdfIcon.js +56 -0
- package/dist/icons/PdfIcon.js.map +7 -0
- package/dist/icons/ProviderIcon.js +42 -0
- package/dist/icons/ProviderIcon.js.map +7 -0
- package/dist/icons/TxtIcon.js +56 -0
- package/dist/icons/TxtIcon.js.map +7 -0
- package/dist/icons/VidIcon.js +56 -0
- package/dist/icons/VidIcon.js.map +7 -0
- package/dist/icons/XlsIcon.js +56 -0
- package/dist/icons/XlsIcon.js.map +7 -0
- package/dist/icons/arbi-mark.js +5 -0
- package/dist/icons/arbi-mark.js.map +7 -0
- package/dist/icons/colors.js +31 -0
- package/dist/icons/colors.js.map +7 -0
- package/dist/icons/file-type.js +105 -0
- package/dist/icons/file-type.js.map +7 -0
- package/dist/icons/index.js +55 -0
- package/dist/icons/index.js.map +7 -0
- package/dist/imagecrop/ImageCropModal.js +56 -0
- package/dist/imagecrop/ImageCropModal.js.map +7 -0
- package/dist/imagecrop/ImageCropper.js +100 -0
- package/dist/imagecrop/ImageCropper.js.map +7 -0
- package/dist/imagecrop/cropImage.js +43 -0
- package/dist/imagecrop/cropImage.js.map +7 -0
- package/dist/imagecrop/index.js +13 -0
- package/dist/imagecrop/index.js.map +7 -0
- package/dist/index.js +702 -17116
- package/dist/index.js.map +7 -1
- package/dist/{chunk-SSXFVWCT.js → lazy-chunk.js} +8 -8
- package/dist/lazy-chunk.js.map +7 -0
- package/dist/lib/collision-padding.js +13 -0
- package/dist/lib/collision-padding.js.map +7 -0
- package/dist/lib/format-friendly-date.js +55 -0
- package/dist/lib/format-friendly-date.js.map +7 -0
- package/dist/lib/format-relative-time.js +52 -0
- package/dist/lib/format-relative-time.js.map +7 -0
- package/dist/lib/markdown-to-text.js +35 -0
- package/dist/lib/markdown-to-text.js.map +7 -0
- package/dist/lib/toast-interaction.js +9 -0
- package/dist/lib/toast-interaction.js.map +7 -0
- package/dist/lib/utils.js +9 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/mail/client.js +103 -0
- package/dist/mail/client.js.map +7 -0
- package/dist/mail/document-attachment.js +38 -0
- package/dist/mail/document-attachment.js.map +7 -0
- package/dist/mail/filing.js +132 -0
- package/dist/mail/filing.js.map +7 -0
- package/dist/mail/hooks.js +393 -0
- package/dist/mail/hooks.js.map +7 -0
- package/dist/mail/index.js +120 -0
- package/dist/mail/index.js.map +7 -0
- package/dist/mail/offline/changes.js +258 -0
- package/dist/mail/offline/changes.js.map +7 -0
- package/dist/mail/offline/confirm-sign-out.js +37 -0
- package/dist/mail/offline/confirm-sign-out.js.map +7 -0
- package/dist/mail/offline/crypto.js +26 -0
- package/dist/mail/offline/crypto.js.map +7 -0
- package/dist/mail/offline/drafts.js +69 -0
- package/dist/mail/offline/drafts.js.map +7 -0
- package/dist/mail/offline/hooks.js +104 -0
- package/dist/mail/offline/hooks.js.map +7 -0
- package/dist/mail/offline/index.js +48 -0
- package/dist/mail/offline/index.js.map +7 -0
- package/dist/mail/offline/live.js +233 -0
- package/dist/mail/offline/live.js.map +7 -0
- package/dist/mail/offline/mailbox.js +191 -0
- package/dist/mail/offline/mailbox.js.map +7 -0
- package/dist/mail/offline/outbox.js +167 -0
- package/dist/mail/offline/outbox.js.map +7 -0
- package/dist/mail/offline/reads.js +159 -0
- package/dist/mail/offline/reads.js.map +7 -0
- package/dist/mail/offline/runtime.js +100 -0
- package/dist/mail/offline/runtime.js.map +7 -0
- package/dist/mail/offline/settings.js +84 -0
- package/dist/mail/offline/settings.js.map +7 -0
- package/dist/mail/offline/store.js +29 -0
- package/dist/mail/offline/store.js.map +7 -0
- package/dist/mail/offline/sync.js +251 -0
- package/dist/mail/offline/sync.js.map +7 -0
- package/dist/mail/offline/wipe.js +19 -0
- package/dist/mail/offline/wipe.js.map +7 -0
- package/dist/mail/thread-keys.js +53 -0
- package/dist/mail/thread-keys.js.map +7 -0
- package/dist/mail/threads.js +71 -0
- package/dist/mail/threads.js.map +7 -0
- package/dist/mail/useMailClient.js +42 -0
- package/dist/mail/useMailClient.js.map +7 -0
- package/dist/model-connections.js +58 -0
- package/dist/model-connections.js.map +7 -0
- package/dist/notifications/ActivityFeed.js +70 -0
- package/dist/notifications/ActivityFeed.js.map +7 -0
- package/dist/notifications/NotificationBell.js +200 -0
- package/dist/notifications/NotificationBell.js.map +7 -0
- package/dist/notifications/Toaster.js +24 -0
- package/dist/notifications/Toaster.js.map +7 -0
- package/dist/notifications/activity-types.js +65 -0
- package/dist/notifications/activity-types.js.map +7 -0
- package/dist/notifications/bell.js +24 -0
- package/dist/notifications/bell.js.map +7 -0
- package/dist/notifications/calendar-notifications.js +79 -0
- package/dist/notifications/calendar-notifications.js.map +7 -0
- package/dist/notifications/index.js +108 -0
- package/dist/notifications/index.js.map +7 -0
- package/dist/notifications/live.js +28 -0
- package/dist/notifications/live.js.map +7 -0
- package/dist/notifications/notification-types.js +127 -0
- package/dist/notifications/notification-types.js.map +7 -0
- package/dist/{chunk-BF5KW5ZI.js → notifications/notify-store.js} +24 -74
- package/dist/notifications/notify-store.js.map +7 -0
- package/dist/notifications/server-notifications.js +452 -0
- package/dist/notifications/server-notifications.js.map +7 -0
- package/dist/notifications/useNotifications.js +34 -0
- package/dist/notifications/useNotifications.js.map +7 -0
- package/dist/offline-store/network.js +11 -0
- package/dist/offline-store/network.js.map +7 -0
- package/dist/offline-store/store.js +136 -0
- package/dist/offline-store/store.js.map +7 -0
- package/dist/offline-store/wipe.js +8 -0
- package/dist/offline-store/wipe.js.map +7 -0
- package/dist/offline.js +58 -0
- package/dist/offline.js.map +7 -0
- package/dist/people.js +122 -0
- package/dist/people.js.map +7 -0
- package/dist/projects.js +30 -0
- package/dist/projects.js.map +7 -0
- package/dist/query-client.js +63 -0
- package/dist/query-client.js.map +7 -0
- package/dist/{chunk-ZM6N7YZH.js → query-keys.js} +18 -31
- package/dist/query-keys.js.map +7 -0
- package/dist/query-persist.js +66 -0
- package/dist/query-persist.js.map +7 -0
- package/dist/queued-writes.js +59 -0
- package/dist/queued-writes.js.map +7 -0
- package/dist/remote-control.js +67 -0
- package/dist/remote-control.js.map +7 -0
- package/dist/search/index.js +23 -0
- package/dist/search/index.js.map +7 -0
- package/dist/search/session.js +58 -0
- package/dist/search/session.js.map +7 -0
- package/dist/search/useSemanticSearch.js +82 -0
- package/dist/search/useSemanticSearch.js.map +7 -0
- package/dist/session/chat.js +512 -0
- package/dist/session/chat.js.map +7 -0
- package/dist/session/documents.js +196 -0
- package/dist/session/documents.js.map +7 -0
- package/dist/session/drafts.js +103 -0
- package/dist/session/drafts.js.map +7 -0
- package/dist/session/index.js +123 -0
- package/dist/session/index.js.map +7 -0
- package/dist/session/layout.js +339 -0
- package/dist/session/layout.js.map +7 -0
- package/dist/session/persist.js +106 -0
- package/dist/session/persist.js.map +7 -0
- package/dist/session/session-hooks.js +53 -0
- package/dist/session/session-hooks.js.map +7 -0
- package/dist/session/signed-in.js +36 -0
- package/dist/session/signed-in.js.map +7 -0
- package/dist/session/store.js +166 -0
- package/dist/session/store.js.map +7 -0
- package/dist/session/thread-tabs.js +121 -0
- package/dist/session/thread-tabs.js.map +7 -0
- package/dist/session/ui-state-storage.js +76 -0
- package/dist/session/ui-state-storage.js.map +7 -0
- package/dist/session/workspace.js +77 -0
- package/dist/session/workspace.js.map +7 -0
- package/dist/shell/index.js +43 -10
- package/dist/shell/index.js.map +7 -1
- package/dist/skill-pack.js +111 -0
- package/dist/skill-pack.js.map +7 -0
- package/dist/storage.js +186 -0
- package/dist/storage.js.map +7 -0
- package/dist/support/index.js +35 -0
- package/dist/support/index.js.map +7 -0
- package/dist/tags.js +204 -0
- package/dist/tags.js.map +7 -0
- package/dist/ui/DebouncedInput.js +109 -0
- package/dist/ui/DebouncedInput.js.map +7 -0
- package/dist/ui/DictateContext.js +8 -0
- package/dist/ui/DictateContext.js.map +7 -0
- package/dist/ui/IconToggle.js +79 -0
- package/dist/ui/IconToggle.js.map +7 -0
- package/dist/ui/VerifiedToggle.js +38 -0
- package/dist/ui/VerifiedToggle.js.map +7 -0
- package/dist/ui/VisibilityToggle.js +103 -0
- package/dist/ui/VisibilityToggle.js.map +7 -0
- package/dist/ui/avatar.js +40 -0
- package/dist/ui/avatar.js.map +7 -0
- package/dist/ui/badge.js +28 -0
- package/dist/ui/badge.js.map +7 -0
- package/dist/ui/button.js +48 -0
- package/dist/ui/button.js.map +7 -0
- package/dist/ui/card.js +90 -0
- package/dist/ui/card.js.map +7 -0
- package/dist/ui/checkbox.js +22 -0
- package/dist/ui/checkbox.js.map +7 -0
- package/dist/ui/collapsible.js +10 -0
- package/dist/ui/collapsible.js.map +7 -0
- package/dist/ui/command.js +103 -0
- package/dist/ui/command.js.map +7 -0
- package/dist/{chunk-YNNAJJ5Z.js → ui/dialog.js} +30 -68
- package/dist/ui/dialog.js.map +7 -0
- package/dist/{chunk-47CCGUWR.js → ui/dropdown-menu.js} +40 -45
- package/dist/ui/dropdown-menu.js.map +7 -0
- package/dist/ui/hover-card.js +36 -0
- package/dist/ui/hover-card.js.map +7 -0
- package/dist/ui/index.js +290 -10
- package/dist/ui/index.js.map +7 -1
- package/dist/ui/input.js +65 -0
- package/dist/ui/input.js.map +7 -0
- package/dist/ui/label.js +16 -0
- package/dist/ui/label.js.map +7 -0
- package/dist/ui/mark-menu.js +300 -0
- package/dist/ui/mark-menu.js.map +7 -0
- package/dist/ui/popover.js +40 -0
- package/dist/ui/popover.js.map +7 -0
- package/dist/ui/portal-container.js +17 -0
- package/dist/ui/portal-container.js.map +7 -0
- package/dist/ui/progress.js +24 -0
- package/dist/ui/progress.js.map +7 -0
- package/dist/ui/radio-group.js +29 -0
- package/dist/ui/radio-group.js.map +7 -0
- package/dist/ui/scroll-area.js +39 -0
- package/dist/ui/scroll-area.js.map +7 -0
- package/dist/ui/select.js +130 -0
- package/dist/ui/select.js.map +7 -0
- package/dist/ui/separator.js +23 -0
- package/dist/ui/separator.js.map +7 -0
- package/dist/ui/slider.js +23 -0
- package/dist/ui/slider.js.map +7 -0
- package/dist/ui/switch.js +28 -0
- package/dist/ui/switch.js.map +7 -0
- package/dist/ui/table.js +75 -0
- package/dist/ui/table.js.map +7 -0
- package/dist/ui/tabs.js +48 -0
- package/dist/ui/tabs.js.map +7 -0
- package/dist/ui/textarea.js +63 -0
- package/dist/ui/textarea.js.map +7 -0
- package/dist/ui/toggle-group.js +43 -0
- package/dist/ui/toggle-group.js.map +7 -0
- package/dist/ui/toggle.js +40 -0
- package/dist/ui/toggle.js.map +7 -0
- package/dist/ui/tooltip.js +215 -0
- package/dist/ui/tooltip.js.map +7 -0
- package/dist/usage-metered.js +8 -0
- package/dist/usage-metered.js.map +7 -0
- package/dist/useArbi.js +16 -0
- package/dist/useArbi.js.map +7 -0
- package/dist/useInstallSkillPack.js +81 -0
- package/dist/useInstallSkillPack.js.map +7 -0
- package/dist/usermenu/UserMenu.js +188 -0
- package/dist/usermenu/UserMenu.js.map +7 -0
- package/dist/usermenu/index.js +5 -0
- package/dist/usermenu/index.js.map +7 -0
- package/dist/users/index.js +15 -0
- package/dist/users/index.js.map +7 -0
- package/dist/users/user-display.js +60 -0
- package/dist/users/user-display.js.map +7 -0
- package/dist/viewers/AudioViewer.js +23 -0
- package/dist/viewers/AudioViewer.js.map +7 -0
- package/dist/viewers/DocxViewer.js +81 -0
- package/dist/viewers/DocxViewer.js.map +7 -0
- package/dist/viewers/ImageViewer.js +31 -0
- package/dist/viewers/ImageViewer.js.map +7 -0
- package/dist/viewers/PdfViewer.js +19 -3
- package/dist/viewers/PdfViewer.js.map +7 -1
- package/dist/{PdfViewerImpl-TEBYF2QI.js → viewers/PdfViewerImpl.js} +25 -19
- package/dist/viewers/PdfViewerImpl.js.map +7 -0
- package/dist/viewers/VideoViewer.js +31 -0
- package/dist/viewers/VideoViewer.js.map +7 -0
- package/dist/viewers/XlsxViewer.js +102 -0
- package/dist/viewers/XlsxViewer.js.map +7 -0
- package/dist/viewers/index.js +61 -271
- package/dist/viewers/index.js.map +7 -1
- package/dist/viewers/page-scroll.js +59 -0
- package/dist/viewers/page-scroll.js.map +7 -0
- package/dist/viewers/pdf-viewer-constants.js +9 -0
- package/dist/viewers/pdf-viewer-constants.js.map +7 -0
- package/dist/{chunk-PTOP7OHG.js → viewers/pdfjs-polyfills.js} +5 -6
- package/dist/viewers/pdfjs-polyfills.js.map +7 -0
- package/dist/viewers/selection-runs.js +38 -0
- package/dist/viewers/selection-runs.js.map +7 -0
- package/dist/viewers/usePageIntersectionObserver.js +68 -0
- package/dist/viewers/usePageIntersectionObserver.js.map +7 -0
- package/dist/viewers/useProcessedChunks.js +58 -0
- package/dist/viewers/useProcessedChunks.js.map +7 -0
- package/dist/viewers/viewer-zoom.js +163 -0
- package/dist/viewers/viewer-zoom.js.map +7 -0
- package/dist/websocket/ArbiWebSocketProvider.js +207 -0
- package/dist/websocket/ArbiWebSocketProvider.js.map +7 -0
- package/dist/websocket/context.js +14 -0
- package/dist/websocket/context.js.map +7 -0
- package/dist/websocket/hooks.js +30 -0
- package/dist/websocket/hooks.js.map +7 -0
- package/dist/websocket/index.js +22 -0
- package/dist/websocket/index.js.map +7 -0
- package/dist/websocket/live-updates.js +38 -0
- package/dist/websocket/live-updates.js.map +7 -0
- package/dist/websocket/reconnect.js +13 -0
- package/dist/websocket/reconnect.js.map +7 -0
- package/dist/websocket/store.js +58 -0
- package/dist/websocket/store.js.map +7 -0
- package/dist/workforce.js +607 -0
- package/dist/workforce.js.map +7 -0
- package/dist/workspaces/access-request.js +35 -0
- package/dist/workspaces/access-request.js.map +7 -0
- package/dist/workspaces/archive.js +59 -0
- package/dist/workspaces/archive.js.map +7 -0
- package/dist/workspaces/current.js +20 -0
- package/dist/workspaces/current.js.map +7 -0
- package/dist/workspaces/delete-workspaces.js +15 -0
- package/dist/workspaces/delete-workspaces.js.map +7 -0
- package/dist/workspaces/export-archive.js +40 -0
- package/dist/workspaces/export-archive.js.map +7 -0
- package/dist/workspaces/index.js +99 -0
- package/dist/workspaces/index.js.map +7 -0
- package/dist/workspaces/settings.js +45 -0
- package/dist/workspaces/settings.js.map +7 -0
- package/dist/workspaces/shell-workspaces.js +49 -0
- package/dist/workspaces/shell-workspaces.js.map +7 -0
- package/dist/workspaces/switch.js +234 -0
- package/dist/workspaces/switch.js.map +7 -0
- package/dist/workspaces/transfer.js +170 -0
- package/dist/workspaces/transfer.js.map +7 -0
- package/dist/workspaces/useShellWorkspaces.js +86 -0
- package/dist/workspaces/useShellWorkspaces.js.map +7 -0
- package/dist/workspaces/workspace-users.js +65 -0
- package/dist/workspaces/workspace-users.js.map +7 -0
- package/package.json +4 -4
- package/dist/PdfViewerImpl-TEBYF2QI.js.map +0 -1
- package/dist/PdfViewerImpl.d-B28hLEuj.d.cts +0 -62
- package/dist/ProductionDialog.d-CpzslFn0.d.cts +0 -51
- package/dist/annotations/freeze/index.d.cts +0 -205
- package/dist/annotations/index.d.cts +0 -360
- package/dist/artifacts/index.d.cts +0 -476
- package/dist/artifacts/redline-core.d.cts +0 -39
- package/dist/bundle-YNOX7VIL.js +0 -5
- package/dist/bundle-YNOX7VIL.js.map +0 -1
- package/dist/bundle.d-BnCSbHy2.d.cts +0 -85
- package/dist/calls/livekit/index.d.cts +0 -280
- package/dist/chunk-47CCGUWR.js.map +0 -1
- package/dist/chunk-4FL3X3LA.js +0 -2057
- package/dist/chunk-4FL3X3LA.js.map +0 -1
- package/dist/chunk-6QNBSXCE.js +0 -271
- package/dist/chunk-6QNBSXCE.js.map +0 -1
- package/dist/chunk-74N436VK.js.map +0 -1
- package/dist/chunk-APZZQZ6P.js +0 -147
- package/dist/chunk-APZZQZ6P.js.map +0 -1
- package/dist/chunk-BF5KW5ZI.js.map +0 -1
- package/dist/chunk-DPYBZDJB.js.map +0 -1
- package/dist/chunk-DZKRL5H3.js +0 -2766
- package/dist/chunk-DZKRL5H3.js.map +0 -1
- package/dist/chunk-HDCRPPBW.js +0 -75
- package/dist/chunk-HDCRPPBW.js.map +0 -1
- package/dist/chunk-L2IPPTVC.js +0 -20
- package/dist/chunk-L2IPPTVC.js.map +0 -1
- package/dist/chunk-LSU4UPS5.js +0 -1011
- package/dist/chunk-LSU4UPS5.js.map +0 -1
- package/dist/chunk-NIKPG4PW.js +0 -1128
- package/dist/chunk-NIKPG4PW.js.map +0 -1
- package/dist/chunk-NNAJLY6B.js +0 -4974
- package/dist/chunk-NNAJLY6B.js.map +0 -1
- package/dist/chunk-OYPKKHXY.js +0 -559
- package/dist/chunk-OYPKKHXY.js.map +0 -1
- package/dist/chunk-PTOP7OHG.js.map +0 -1
- package/dist/chunk-QMY2JKBI.js +0 -246
- package/dist/chunk-QMY2JKBI.js.map +0 -1
- package/dist/chunk-SN5YV6FF.js +0 -11
- package/dist/chunk-SN5YV6FF.js.map +0 -1
- package/dist/chunk-SSXFVWCT.js.map +0 -1
- package/dist/chunk-T3USOSVN.js.map +0 -1
- package/dist/chunk-T4CLW3DN.js +0 -2560
- package/dist/chunk-T4CLW3DN.js.map +0 -1
- package/dist/chunk-UW2SGTI5.js.map +0 -1
- package/dist/chunk-VSCU7K4V.js.map +0 -1
- package/dist/chunk-WL2Y2T47.js.map +0 -1
- package/dist/chunk-WUTL62MC.js.map +0 -1
- package/dist/chunk-XWPLOFDN.js +0 -644
- package/dist/chunk-XWPLOFDN.js.map +0 -1
- package/dist/chunk-Y3MTRA7D.js +0 -97
- package/dist/chunk-Y3MTRA7D.js.map +0 -1
- package/dist/chunk-YNNAJJ5Z.js.map +0 -1
- package/dist/chunk-ZM6N7YZH.js.map +0 -1
- package/dist/document-tools/core.d.cts +0 -883
- package/dist/document-tools/index.d.cts +0 -90
- package/dist/index.d-LArLnpI8.d.cts +0 -2036
- package/dist/index.d-oR5FKZpJ.d.cts +0 -579
- package/dist/index.d.cts +0 -15923
- package/dist/lazy-chunk.d-B6YDLMKI.d.cts +0 -31
- package/dist/model.d-CNtRWKVK.d.cts +0 -110
- package/dist/query-keys.d-B1Am9tIF.d.cts +0 -337
- package/dist/shell/index.d.cts +0 -6
- package/dist/store.d-GADDvC7w.d.cts +0 -35
- package/dist/stored-markdown.d-Bp18vrbr.d.cts +0 -125
- package/dist/ui/index.d.cts +0 -26
- package/dist/useAudioRecorder.d-B9rlMI_E.d.cts +0 -778
- package/dist/viewers/PdfViewer.d.cts +0 -9
- package/dist/viewers/index.d.cts +0 -262
|
@@ -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
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mail/offline/settings.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Per-device offline mail settings and the shared change/status signal.\n *\n * Settings are a device preference, not mail content, so they live in localStorage keyed by the\n * user (two people sharing a browser keep separate choices). The mail itself is wiped on sign-out;\n * the preference survives so the mailbox re-syncs after the user signs back in on this device\n * (decided in #2187: they hold no mail, and keeping them favours the person coming back).\n *\n * By default the last 7 days of mail are kept; the person can choose a shorter or longer window,\n * or none (\"Off\": only drafts and the Outbox stay on the device).\n *\n * `onOfflineMailChange` notifies this tab and, through a BroadcastChannel, other open tabs whenever\n * the offline store changes (sync progress, Outbox, drafts), so every Mail view stays current.\n */\n\n/** How far back mail is kept on this device. */\nexport type OfflineMailScope = '1d' | '7d' | '30d' | '90d'\n\nexport interface OfflineMailSettings {\n /** Keep recent mail on this device (the window is `scope`). Off: only drafts and the Outbox. */\n enabled: boolean\n /** How far back mail is kept. */\n scope: OfflineMailScope\n /** Attachments of one message larger than this (MB, in total) are not saved. */\n attachmentCapMb: number\n /** Total space the offline mailbox may use (MB); the least recently opened attachments go first. */\n quotaMb: number\n}\n\n/** On, for the last week: recent mail can be read and answered offline unless the person opts out. */\nexport const DEFAULT_OFFLINE_MAIL_SETTINGS: OfflineMailSettings = {\n enabled: true,\n scope: '7d',\n attachmentCapMb: 25,\n quotaMb: 1024,\n}\n\nexport const SCOPE_DAYS: Record<OfflineMailScope, number> = {\n '1d': 1,\n '7d': 7,\n '30d': 30,\n '90d': 90,\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\n/** The oldest receive time (ms) kept on the device under `settings`. */\nexport function offlineWindowStart(settings: OfflineMailSettings, now = Date.now()): number {\n return now - SCOPE_DAYS[settings.scope] * DAY_MS\n}\n\n/** Whether a message received at `receivedAt` belongs on the device (an undated one always does). */\nexport function inOfflineWindow(\n settings: OfflineMailSettings,\n receivedAt: string | undefined,\n now = Date.now()\n): boolean {\n const at = receivedAt ? Date.parse(receivedAt) || 0 : 0\n return settings.enabled && (!at || at >= offlineWindowStart(settings, now))\n}\n\n/**\n * Saved choices from earlier versions: a longer window (\"Last year\", \"All mail\") becomes the\n * longest one offered now, anything unknown the default.\n */\nfunction normalize(saved: Partial<OfflineMailSettings>): OfflineMailSettings {\n const merged = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, ...saved }\n const scope: string = merged.scope\n if (scope in SCOPE_DAYS) return merged\n const longer = scope === '365d' || scope === 'all'\n return { ...merged, scope: longer ? '90d' : DEFAULT_OFFLINE_MAIL_SETTINGS.scope }\n}\n\nconst STORAGE_PREFIX = 'arbi-offline-mail-settings:'\n\n/** The signed-in user whose settings apply (`null`: signed out, nothing is kept). */\nexport type OfflineMailUser = string | null | undefined\n\n/** Nobody signed in (or a restored tab whose profile isn't loaded yet): keep no mail. */\nconst SIGNED_OUT: OfflineMailSettings = { ...DEFAULT_OFFLINE_MAIL_SETTINGS, enabled: false }\n\nexport function getOfflineMailSettings(userExtId: OfflineMailUser): OfflineMailSettings {\n if (!userExtId) return SIGNED_OUT\n const key = STORAGE_PREFIX + userExtId\n try {\n const raw = localStorage.getItem(key)\n return raw\n ? normalize(JSON.parse(raw) as Partial<OfflineMailSettings>)\n : DEFAULT_OFFLINE_MAIL_SETTINGS\n } catch {\n return DEFAULT_OFFLINE_MAIL_SETTINGS\n }\n}\n\nexport function setOfflineMailSettings(\n userExtId: OfflineMailUser,\n patch: Partial<OfflineMailSettings>\n): OfflineMailSettings {\n const next = { ...getOfflineMailSettings(userExtId), ...patch }\n if (userExtId) {\n try {\n localStorage.setItem(STORAGE_PREFIX + userExtId, JSON.stringify(next))\n } catch {\n // Storage full/blocked: the choice applies for this session only.\n }\n }\n notifyOfflineMailChange('settings')\n return next\n}\n\nexport type OfflineMailChange =\n 'settings' | 'mailbox' | 'outbox' | 'drafts' | 'changes' | 'status' | 'wiped'\n\nconst listeners = new Set<(change: OfflineMailChange) => void>()\nconst CHANNEL = 'arbi-offline-mail'\nlet channel: BroadcastChannel | null = null\n\nfunction getChannel(): BroadcastChannel | null {\n if (channel || typeof BroadcastChannel === 'undefined') return channel\n channel = new BroadcastChannel(CHANNEL)\n channel.onmessage = (event: MessageEvent<OfflineMailChange>) => {\n for (const listener of listeners) listener(event.data)\n }\n return channel\n}\n\nexport function notifyOfflineMailChange(change: OfflineMailChange): void {\n for (const listener of listeners) listener(change)\n try {\n getChannel()?.postMessage(change)\n } catch {\n // Channel closed (tab unloading): other tabs pick the change up on their next read.\n }\n}\n\nexport function onOfflineMailChange(listener: (change: OfflineMailChange) => void): () => void {\n getChannel()\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n"],
|
|
5
|
+
"mappings": "AA8BO,MAAM,gCAAqD;AAAA,EAChE,SAAS;AAAA,EACT,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,SAAS;AACX;AAEO,MAAM,aAA+C;AAAA,EAC1D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AACT;AAEA,MAAM,SAAS,KAAK,KAAK,KAAK;AAGvB,SAAS,mBAAmB,UAA+B,MAAM,KAAK,IAAI,GAAW;AAC1F,SAAO,MAAM,WAAW,SAAS,KAAK,IAAI;AAC5C;AAGO,SAAS,gBACd,UACA,YACA,MAAM,KAAK,IAAI,GACN;AACT,QAAM,KAAK,aAAa,KAAK,MAAM,UAAU,KAAK,IAAI;AACtD,SAAO,SAAS,YAAY,CAAC,MAAM,MAAM,mBAAmB,UAAU,GAAG;AAC3E;AAMA,SAAS,UAAU,OAA0D;AAC3E,QAAM,SAAS,EAAE,GAAG,+BAA+B,GAAG,MAAM;AAC5D,QAAM,QAAgB,OAAO;AAC7B,MAAI,SAAS,WAAY,QAAO;AAChC,QAAM,SAAS,UAAU,UAAU,UAAU;AAC7C,SAAO,EAAE,GAAG,QAAQ,OAAO,SAAS,QAAQ,8BAA8B,MAAM;AAClF;AAEA,MAAM,iBAAiB;AAMvB,MAAM,aAAkC,EAAE,GAAG,+BAA+B,SAAS,MAAM;AAEpF,SAAS,uBAAuB,WAAiD;AACtF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAAM,iBAAiB;AAC7B,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,GAAG;AACpC,WAAO,MACH,UAAU,KAAK,MAAM,GAAG,CAAiC,IACzD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,uBACd,WACA,OACqB;AACrB,QAAM,OAAO,EAAE,GAAG,uBAAuB,SAAS,GAAG,GAAG,MAAM;AAC9D,MAAI,WAAW;AACb,QAAI;AACF,mBAAa,QAAQ,iBAAiB,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IACvE,QAAQ;AAAA,IAER;AAAA,EACF;AACA,0BAAwB,UAAU;AAClC,SAAO;AACT;AAKA,MAAM,YAAY,oBAAI,IAAyC;AAC/D,MAAM,UAAU;AAChB,IAAI,UAAmC;AAEvC,SAAS,aAAsC;AAC7C,MAAI,WAAW,OAAO,qBAAqB,YAAa,QAAO;AAC/D,YAAU,IAAI,iBAAiB,OAAO;AACtC,UAAQ,YAAY,CAAC,UAA2C;AAC9D,eAAW,YAAY,UAAW,UAAS,MAAM,IAAI;AAAA,EACvD;AACA,SAAO;AACT;AAEO,SAAS,wBAAwB,QAAiC;AACvE,aAAW,YAAY,UAAW,UAAS,MAAM;AACjD,MAAI;AACF,eAAW,GAAG,YAAY,MAAM;AAAA,EAClC,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,oBAAoB,UAA2D;AAC7F,aAAW;AACX,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM,UAAU,OAAO,QAAQ;AACxC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|