@arbidocs/react 0.5.111 → 0.5.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/{chunk-Z3W4XFWB.cjs → chunk-C72FSQUE.cjs} +3 -3
- package/dist/{chunk-Z3W4XFWB.cjs.map → chunk-C72FSQUE.cjs.map} +1 -1
- package/dist/{chunk-LBAG7KL7.cjs → chunk-FQVNZCTD.cjs} +3 -3
- package/dist/{chunk-LBAG7KL7.cjs.map → chunk-FQVNZCTD.cjs.map} +1 -1
- package/dist/{chunk-DI5KSVKY.cjs → chunk-UDDJIUHJ.cjs} +3 -3
- package/dist/{chunk-DI5KSVKY.cjs.map → chunk-UDDJIUHJ.cjs.map} +1 -1
- 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.cjs +80 -80
- 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.cjs +80 -80
- 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.cjs +282 -282
- 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.cjs +37 -37
- 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.cjs +154 -154
- 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,29 @@
|
|
|
1
|
+
import { createRecordStore } from "../../offline-store/store.js";
|
|
2
|
+
const OFFLINE_MAIL_DB = "arbi-offline-mail";
|
|
3
|
+
const store = createRecordStore(OFFLINE_MAIL_DB);
|
|
4
|
+
const putRecord = store.putRecord;
|
|
5
|
+
const getRecord = store.getRecord;
|
|
6
|
+
const hasRecord = store.hasRecord;
|
|
7
|
+
const deleteRecords = store.deleteRecords;
|
|
8
|
+
const recordsInBucket = store.recordsInBucket;
|
|
9
|
+
const recordsOfKind = store.recordsOfKind;
|
|
10
|
+
const allRecordSizes = store.allRecordSizes;
|
|
11
|
+
const touchRecord = store.touchRecord;
|
|
12
|
+
const getMeta = store.getMeta;
|
|
13
|
+
const setMeta = store.setMeta;
|
|
14
|
+
const deleteOfflineMailDatabase = store.deleteDatabase;
|
|
15
|
+
export {
|
|
16
|
+
OFFLINE_MAIL_DB,
|
|
17
|
+
allRecordSizes,
|
|
18
|
+
deleteOfflineMailDatabase,
|
|
19
|
+
deleteRecords,
|
|
20
|
+
getMeta,
|
|
21
|
+
getRecord,
|
|
22
|
+
hasRecord,
|
|
23
|
+
putRecord,
|
|
24
|
+
recordsInBucket,
|
|
25
|
+
recordsOfKind,
|
|
26
|
+
setMeta,
|
|
27
|
+
touchRecord
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mail/offline/store.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The offline mailbox's IndexedDB store (the generic encrypted record store, see\n * ../../offline-store/store, under the `arbi-offline-mail` database).\n *\n * Record plaintext fields are only what IndexedDB needs to find and evict a record: a blind key, its\n * kind, a blind bucket (which folder/account it belongs to), a sort time, its size and last read.\n * Everything else \u2014 subjects, addresses, bodies, attachments, drafts, queued mail and changes \u2014 is sealed in\n * `data` with the key from ./crypto. `meta` holds non-content facts: owner, sync times and counts.\n */\n\nimport { createRecordStore, type StoredRecord as GenericRecord } from '../../offline-store/store'\n\nexport const OFFLINE_MAIL_DB = 'arbi-offline-mail'\n\nexport type RecordKind =\n | 'accounts'\n | 'policy'\n | 'folders'\n | 'header'\n | 'body'\n | 'attachment'\n | 'draft'\n | 'outbox'\n | 'change'\n\nexport type StoredRecord = GenericRecord<RecordKind>\n\nconst store = createRecordStore<RecordKind>(OFFLINE_MAIL_DB)\n\nexport const putRecord = store.putRecord\nexport const getRecord = store.getRecord\nexport const hasRecord = store.hasRecord\nexport const deleteRecords = store.deleteRecords\nexport const recordsInBucket = store.recordsInBucket\nexport const recordsOfKind = store.recordsOfKind\n/** Keys + sizes only (no data), for quota accounting without loading ciphertext. */\nexport const allRecordSizes = store.allRecordSizes\nexport const touchRecord = store.touchRecord\nexport const getMeta = store.getMeta\nexport const setMeta = store.setMeta\n/** Delete the whole database (sign-out / \"Remove offline data\"). */\nexport const deleteOfflineMailDatabase = store.deleteDatabase\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,yBAA6D;AAE/D,MAAM,kBAAkB;AAe/B,MAAM,QAAQ,kBAA8B,eAAe;AAEpD,MAAM,YAAY,MAAM;AACxB,MAAM,YAAY,MAAM;AACxB,MAAM,YAAY,MAAM;AACxB,MAAM,gBAAgB,MAAM;AAC5B,MAAM,kBAAkB,MAAM;AAC9B,MAAM,gBAAgB,MAAM;AAE5B,MAAM,iBAAiB,MAAM;AAC7B,MAAM,cAAc,MAAM;AAC1B,MAAM,UAAU,MAAM;AACtB,MAAM,UAAU,MAAM;AAEtB,MAAM,4BAA4B,MAAM;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { logger } from "@arbidocs/client";
|
|
2
|
+
import {
|
|
3
|
+
bucketOf,
|
|
4
|
+
hasAttachment,
|
|
5
|
+
hasBody,
|
|
6
|
+
isVirtualFolder,
|
|
7
|
+
keyOf,
|
|
8
|
+
loadAccounts,
|
|
9
|
+
loadFolders,
|
|
10
|
+
loadHeaders,
|
|
11
|
+
removeKeys,
|
|
12
|
+
saveAccounts,
|
|
13
|
+
saveAttachment,
|
|
14
|
+
saveBody,
|
|
15
|
+
saveFolders,
|
|
16
|
+
saveHeader,
|
|
17
|
+
savePolicy,
|
|
18
|
+
toHeader
|
|
19
|
+
} from "./mailbox.js";
|
|
20
|
+
import { allRecordSizes, getMeta, recordsInBucket, recordsOfKind, setMeta } from "./store.js";
|
|
21
|
+
import { overlayPendingMailChanges } from "./changes.js";
|
|
22
|
+
import {
|
|
23
|
+
notifyOfflineMailChange,
|
|
24
|
+
offlineWindowStart,
|
|
25
|
+
SCOPE_DAYS
|
|
26
|
+
} from "./settings.js";
|
|
27
|
+
const STATUS_KEY = "status";
|
|
28
|
+
const FULL_SYNC_EVERY_MS = 60 * 60 * 1e3;
|
|
29
|
+
const MAX_PAGES = 200;
|
|
30
|
+
async function getOfflineMailStatus() {
|
|
31
|
+
return await getMeta(STATUS_KEY) ?? { messages: 0, bytes: 0, skipped: [] };
|
|
32
|
+
}
|
|
33
|
+
const MB = 1024 * 1024;
|
|
34
|
+
const time = (m) => m.received_at ? Date.parse(m.received_at) || 0 : 0;
|
|
35
|
+
const sameHeader = (a, b) => JSON.stringify(toHeader(a)) === JSON.stringify(toHeader(b));
|
|
36
|
+
class Budget {
|
|
37
|
+
constructor(used, quota) {
|
|
38
|
+
this.used = used;
|
|
39
|
+
this.quota = quota;
|
|
40
|
+
}
|
|
41
|
+
used;
|
|
42
|
+
quota;
|
|
43
|
+
get full() {
|
|
44
|
+
return this.used >= this.quota;
|
|
45
|
+
}
|
|
46
|
+
take(bytes) {
|
|
47
|
+
this.used += bytes;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function saveMessageContent(transport, account, header, settings, budget) {
|
|
51
|
+
const message = await transport.message(account, header.id);
|
|
52
|
+
await saveBody(account.id, message);
|
|
53
|
+
budget.take((message.text?.length ?? 0) + (message.html?.length ?? 0));
|
|
54
|
+
let attachmentBytes = 0;
|
|
55
|
+
for (const attachment of message.attachments) {
|
|
56
|
+
if (budget.full) return;
|
|
57
|
+
if (attachmentBytes + attachment.size > settings.attachmentCapMb * MB) continue;
|
|
58
|
+
if (await hasAttachment(account.id, message.id, attachment.id)) continue;
|
|
59
|
+
const bytes = attachment.content_base64 ? Uint8Array.from(atob(attachment.content_base64), (c) => c.charCodeAt(0)) : await transport.attachment(account, message.id, attachment.id);
|
|
60
|
+
await saveAttachment(account.id, message.id, attachment.id, bytes, time(message));
|
|
61
|
+
attachmentBytes += bytes.byteLength;
|
|
62
|
+
budget.take(bytes.byteLength);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function syncFolder(transport, account, folder, settings, windowStart, full, budget) {
|
|
66
|
+
const saved = new Map(
|
|
67
|
+
(await loadHeaders(account.id, folder.id)).map((h) => [h.message.id, h])
|
|
68
|
+
);
|
|
69
|
+
const seen = /* @__PURE__ */ new Set();
|
|
70
|
+
let oldestSeen = Infinity;
|
|
71
|
+
let cursor = null;
|
|
72
|
+
let reachedEnd = false;
|
|
73
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
74
|
+
const result = await transport.messages(account, folder.id, cursor);
|
|
75
|
+
let anythingNew = false;
|
|
76
|
+
let outOfScope = false;
|
|
77
|
+
for (const message of result.messages) {
|
|
78
|
+
const at = time(message);
|
|
79
|
+
if (at && at < windowStart) {
|
|
80
|
+
outOfScope = true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
seen.add(message.id);
|
|
84
|
+
if (at) oldestSeen = Math.min(oldestSeen, at);
|
|
85
|
+
const previous = saved.get(message.id)?.message;
|
|
86
|
+
if (!previous || !sameHeader(previous, message)) {
|
|
87
|
+
await saveHeader(account.id, folder.id, message);
|
|
88
|
+
anythingNew = true;
|
|
89
|
+
}
|
|
90
|
+
if (!budget.full && !await hasBody(account.id, message.id)) {
|
|
91
|
+
try {
|
|
92
|
+
await saveMessageContent(transport, account, message, settings, budget);
|
|
93
|
+
anythingNew = true;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger.warn("[offline-mail] could not save a message body", error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
cursor = result.next_cursor ?? null;
|
|
100
|
+
if (outOfScope || !cursor) {
|
|
101
|
+
reachedEnd = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
if (!full && !anythingNew) break;
|
|
105
|
+
}
|
|
106
|
+
const stale = [...saved.values()].filter(
|
|
107
|
+
(h) => !seen.has(h.message.id) && (reachedEnd || h.sortAt >= oldestSeen)
|
|
108
|
+
);
|
|
109
|
+
await removeKeys(stale.map((h) => h.key));
|
|
110
|
+
}
|
|
111
|
+
async function collectGarbage(accounts) {
|
|
112
|
+
for (const account of accounts) {
|
|
113
|
+
const folders = await loadFolders(account.id) ?? [];
|
|
114
|
+
const expected = /* @__PURE__ */ new Set([await keyOf.folders(account.id)]);
|
|
115
|
+
for (const folder of folders) {
|
|
116
|
+
for (const { message } of await loadHeaders(account.id, folder.id)) {
|
|
117
|
+
expected.add(await keyOf.body(account.id, message.id));
|
|
118
|
+
for (const attachment of message.attachments)
|
|
119
|
+
expected.add(await keyOf.attachment(account.id, message.id, attachment.id));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const orphans = (await recordsInBucket(await bucketOf.account(account.id))).filter((r) => (r.kind === "body" || r.kind === "attachment") && !expected.has(r.key)).map((r) => r.key);
|
|
123
|
+
await removeKeys(orphans);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function forgetAccount(accountId) {
|
|
127
|
+
for (const folder of await loadFolders(accountId) ?? []) {
|
|
128
|
+
const records2 = await recordsInBucket(await bucketOf.folder(accountId, folder.id));
|
|
129
|
+
await removeKeys(records2.map((r) => r.key));
|
|
130
|
+
}
|
|
131
|
+
const records = await recordsInBucket(await bucketOf.account(accountId));
|
|
132
|
+
await removeKeys(records.filter((r) => r.kind !== "draft").map((r) => r.key));
|
|
133
|
+
}
|
|
134
|
+
async function enforceQuota(quotaBytes) {
|
|
135
|
+
let used = (await allRecordSizes()).reduce((n, r) => n + r.size, 0);
|
|
136
|
+
if (used <= quotaBytes) return used;
|
|
137
|
+
for (const kind of ["attachment", "body"]) {
|
|
138
|
+
const records = (await recordsOfKind(kind)).sort(
|
|
139
|
+
(a, b) => kind === "attachment" ? a.lastAccess - b.lastAccess : a.sortAt - b.sortAt
|
|
140
|
+
);
|
|
141
|
+
const evict = [];
|
|
142
|
+
for (const record of records) {
|
|
143
|
+
if (used <= quotaBytes) break;
|
|
144
|
+
evict.push(record.key);
|
|
145
|
+
used -= record.size;
|
|
146
|
+
}
|
|
147
|
+
await removeKeys(evict);
|
|
148
|
+
if (used <= quotaBytes) break;
|
|
149
|
+
}
|
|
150
|
+
return used;
|
|
151
|
+
}
|
|
152
|
+
const CONTENT_KINDS = ["header", "body", "attachment"];
|
|
153
|
+
async function trimOfflineMailbox(settings, now = Date.now()) {
|
|
154
|
+
const windowStart = offlineWindowStart(settings, now);
|
|
155
|
+
const kinds = settings.enabled ? CONTENT_KINDS : [...CONTENT_KINDS, "folders"];
|
|
156
|
+
for (const kind of kinds) {
|
|
157
|
+
const records = await recordsOfKind(kind);
|
|
158
|
+
const old = settings.enabled ? records.filter((r) => r.sortAt > 0 && r.sortAt < windowStart) : records;
|
|
159
|
+
await removeKeys(old.map((r) => r.key));
|
|
160
|
+
}
|
|
161
|
+
const previous = await getOfflineMailStatus();
|
|
162
|
+
const status = {
|
|
163
|
+
...previous,
|
|
164
|
+
messages: (await recordsOfKind("header")).length,
|
|
165
|
+
bytes: (await allRecordSizes()).reduce((n, r) => n + r.size, 0),
|
|
166
|
+
// A shorter window no longer covers what the last full pass fetched: growing it again (even
|
|
167
|
+
// before the next hourly full pass) makes the next pass a full one.
|
|
168
|
+
windowDays: settings.enabled && previous.windowDays !== void 0 ? Math.min(previous.windowDays, SCOPE_DAYS[settings.scope]) : void 0,
|
|
169
|
+
// Off: nothing is saved, so turning it back on starts with a full pass.
|
|
170
|
+
...!settings.enabled && { lastSyncedAt: void 0, lastFullSyncAt: void 0 }
|
|
171
|
+
};
|
|
172
|
+
await setMeta(STATUS_KEY, status);
|
|
173
|
+
notifyOfflineMailChange("status");
|
|
174
|
+
return status;
|
|
175
|
+
}
|
|
176
|
+
async function syncMailbox(transport, settings, options = {}) {
|
|
177
|
+
const now = options.now ?? Date.now();
|
|
178
|
+
const previous = await getOfflineMailStatus();
|
|
179
|
+
const days = SCOPE_DAYS[settings.scope];
|
|
180
|
+
const full = options.full || !previous.lastFullSyncAt || now - previous.lastFullSyncAt > FULL_SYNC_EVERY_MS || (previous.windowDays ?? 0) < days;
|
|
181
|
+
const windowStart = offlineWindowStart(settings, now);
|
|
182
|
+
const quota = settings.quotaMb * MB;
|
|
183
|
+
const skipped = [];
|
|
184
|
+
let error;
|
|
185
|
+
try {
|
|
186
|
+
await savePolicy(await transport.policy());
|
|
187
|
+
const accounts = await transport.accounts();
|
|
188
|
+
const before = await loadAccounts() ?? [];
|
|
189
|
+
await saveAccounts(accounts);
|
|
190
|
+
for (const gone of before.filter((b) => !accounts.some((a) => a.id === b.id)))
|
|
191
|
+
await forgetAccount(gone.id);
|
|
192
|
+
const budget = new Budget(
|
|
193
|
+
(await allRecordSizes()).reduce((n, r) => n + r.size, 0),
|
|
194
|
+
quota
|
|
195
|
+
);
|
|
196
|
+
for (const account of accounts) {
|
|
197
|
+
if (account.needs_reconsent) {
|
|
198
|
+
skipped.push(account.email);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
await transport.unlock(account);
|
|
203
|
+
const folders = await transport.folders(account);
|
|
204
|
+
const oldFolders = await loadFolders(account.id) ?? [];
|
|
205
|
+
await saveFolders(account.id, folders);
|
|
206
|
+
for (const gone of oldFolders.filter((o) => !folders.some((f) => f.id === o.id))) {
|
|
207
|
+
const records = await recordsInBucket(await bucketOf.folder(account.id, gone.id));
|
|
208
|
+
await removeKeys(records.map((r) => r.key));
|
|
209
|
+
}
|
|
210
|
+
for (const folder of folders.filter((f) => !isVirtualFolder(f)))
|
|
211
|
+
await syncFolder(transport, account, folder, settings, windowStart, full, budget);
|
|
212
|
+
notifyOfflineMailChange("mailbox");
|
|
213
|
+
} catch (accountError) {
|
|
214
|
+
if (accountError.status === void 0) throw accountError;
|
|
215
|
+
logger.warn("[offline-mail] account sync failed", accountError);
|
|
216
|
+
skipped.push(account.email);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (full) await collectGarbage(accounts);
|
|
220
|
+
} catch (syncError) {
|
|
221
|
+
error = syncError.status === void 0 ? "The connection dropped during sync. It will continue when you are back online." : syncError instanceof Error ? syncError.message : "Sync failed";
|
|
222
|
+
}
|
|
223
|
+
await overlayPendingMailChanges().catch(
|
|
224
|
+
(overlayError) => logger.warn("[offline-mail] could not lay pending changes over the device copy", overlayError)
|
|
225
|
+
);
|
|
226
|
+
const current = options.latest?.() ?? settings;
|
|
227
|
+
const trimmed = await trimOfflineMailbox(current, now);
|
|
228
|
+
if (!current.enabled) return trimmed;
|
|
229
|
+
const bytes = await enforceQuota(current.quotaMb * MB);
|
|
230
|
+
const messages = (await recordsOfKind("header")).length;
|
|
231
|
+
const fullDone = !error && full;
|
|
232
|
+
const status = {
|
|
233
|
+
lastSyncedAt: error ? previous.lastSyncedAt : now,
|
|
234
|
+
lastFullSyncAt: fullDone ? now : previous.lastFullSyncAt,
|
|
235
|
+
windowDays: fullDone ? Math.min(days, SCOPE_DAYS[current.scope]) : trimmed.windowDays,
|
|
236
|
+
messages,
|
|
237
|
+
bytes,
|
|
238
|
+
error,
|
|
239
|
+
skipped
|
|
240
|
+
};
|
|
241
|
+
await setMeta(STATUS_KEY, status);
|
|
242
|
+
notifyOfflineMailChange("status");
|
|
243
|
+
return status;
|
|
244
|
+
}
|
|
245
|
+
export {
|
|
246
|
+
enforceQuota,
|
|
247
|
+
getOfflineMailStatus,
|
|
248
|
+
syncMailbox,
|
|
249
|
+
trimOfflineMailbox
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mail/offline/sync.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Mailbox sync: copies the user's mail (native inbox and connected accounts, through the same list/\n * get endpoints the Mail page uses) into the encrypted offline store, incrementally.\n *\n * For each account \u2192 folder, list pages newest first within the chosen scope. Headers are always\n * refreshed (cheap: read/unread and moves show up); a body and its attachments are fetched only for\n * a message not saved yet. A quick pass stops at the first page that holds nothing new; a full pass\n * (hourly, or \"Sync now\") walks the whole scope and drops what was deleted or moved on the server.\n *\n * Space: attachments of one message are saved up to the per-message cap; the whole store is held\n * under the quota, evicting the least recently opened attachments first, then the oldest bodies.\n * A message whose body isn't saved still lists offline and says so when opened.\n *\n * Window: only mail received within the chosen window is kept (`trimOfflineMailbox`, after every\n * pass and as soon as the setting changes, offline too). A longer window than the last full pass\n * covered makes the next pass a full one, which fetches the older mail.\n */\n\nimport type { MailAccount, MailFolder, MailMessage, MailPolicy } from '../types'\nimport { logger } from '@arbidocs/client'\nimport {\n bucketOf,\n hasAttachment,\n hasBody,\n isVirtualFolder,\n keyOf,\n loadAccounts,\n loadFolders,\n loadHeaders,\n removeKeys,\n saveAccounts,\n saveAttachment,\n saveBody,\n saveFolders,\n saveHeader,\n savePolicy,\n toHeader,\n} from './mailbox'\nimport { allRecordSizes, getMeta, recordsInBucket, recordsOfKind, setMeta } from './store'\nimport { overlayPendingMailChanges } from './changes'\nimport {\n notifyOfflineMailChange,\n offlineWindowStart,\n SCOPE_DAYS,\n type OfflineMailSettings,\n} from './settings'\n\nexport interface MailTransport {\n policy: () => Promise<MailPolicy>\n accounts: () => Promise<MailAccount[]>\n unlock: (account: MailAccount) => Promise<void>\n folders: (account: MailAccount) => Promise<MailFolder[]>\n messages: (\n account: MailAccount,\n folderId: string,\n cursor: string | null\n ) => Promise<{ messages: MailMessage[]; next_cursor?: string | null }>\n message: (account: MailAccount, messageId: string) => Promise<MailMessage>\n attachment: (account: MailAccount, messageId: string, attachmentId: string) => Promise<Uint8Array>\n}\n\nexport interface OfflineMailStatus {\n lastSyncedAt?: number\n lastFullSyncAt?: number\n messages: number\n bytes: number\n /** Why the last sync didn't finish, in plain words. */\n error?: string\n /** Accounts that could not be synced (e.g. need reconnecting), by email. */\n skipped: string[]\n /** The window (days) the last full pass covered; a longer one needs another full pass. */\n windowDays?: number\n}\n\nconst STATUS_KEY = 'status'\nconst FULL_SYNC_EVERY_MS = 60 * 60 * 1000\n/** Safety stop per folder per pass. */\nconst MAX_PAGES = 200\n\nexport async function getOfflineMailStatus(): Promise<OfflineMailStatus> {\n return (await getMeta<OfflineMailStatus>(STATUS_KEY)) ?? { messages: 0, bytes: 0, skipped: [] }\n}\n\nconst MB = 1024 * 1024\nconst time = (m: MailMessage) => (m.received_at ? Date.parse(m.received_at) || 0 : 0)\nconst sameHeader = (a: MailMessage, b: MailMessage) =>\n JSON.stringify(toHeader(a)) === JSON.stringify(toHeader(b))\n\nclass Budget {\n constructor(\n public used: number,\n private readonly quota: number\n ) {}\n get full() {\n return this.used >= this.quota\n }\n take(bytes: number) {\n this.used += bytes\n }\n}\n\nasync function saveMessageContent(\n transport: MailTransport,\n account: MailAccount,\n header: MailMessage,\n settings: OfflineMailSettings,\n budget: Budget\n): Promise<void> {\n const message = await transport.message(account, header.id)\n await saveBody(account.id, message)\n budget.take((message.text?.length ?? 0) + (message.html?.length ?? 0))\n let attachmentBytes = 0\n for (const attachment of message.attachments) {\n if (budget.full) return\n if (attachmentBytes + attachment.size > settings.attachmentCapMb * MB) continue\n if (await hasAttachment(account.id, message.id, attachment.id)) continue\n const bytes = attachment.content_base64\n ? Uint8Array.from(atob(attachment.content_base64), (c) => c.charCodeAt(0))\n : await transport.attachment(account, message.id, attachment.id)\n await saveAttachment(account.id, message.id, attachment.id, bytes, time(message))\n attachmentBytes += bytes.byteLength\n budget.take(bytes.byteLength)\n }\n}\n\nasync function syncFolder(\n transport: MailTransport,\n account: MailAccount,\n folder: MailFolder,\n settings: OfflineMailSettings,\n windowStart: number,\n full: boolean,\n budget: Budget\n): Promise<void> {\n const saved = new Map(\n (await loadHeaders(account.id, folder.id)).map((h) => [h.message.id, h] as const)\n )\n const seen = new Set<string>()\n let oldestSeen = Infinity\n let cursor: string | null = null\n let reachedEnd = false\n for (let page = 0; page < MAX_PAGES; page++) {\n const result = await transport.messages(account, folder.id, cursor)\n let anythingNew = false\n let outOfScope = false\n for (const message of result.messages) {\n const at = time(message)\n if (at && at < windowStart) {\n outOfScope = true\n break\n }\n seen.add(message.id)\n if (at) oldestSeen = Math.min(oldestSeen, at)\n const previous = saved.get(message.id)?.message\n if (!previous || !sameHeader(previous, message)) {\n await saveHeader(account.id, folder.id, message)\n anythingNew = true\n }\n if (!budget.full && !(await hasBody(account.id, message.id))) {\n try {\n await saveMessageContent(transport, account, message, settings, budget)\n anythingNew = true\n } catch (error) {\n // One unreadable message must not stop the folder; it lists, and says it isn't saved.\n logger.warn('[offline-mail] could not save a message body', error)\n }\n }\n }\n cursor = result.next_cursor ?? null\n if (outOfScope || !cursor) {\n reachedEnd = true\n break\n }\n if (!full && !anythingNew) break\n }\n // Drop what the server no longer has here: everything unseen in the range this pass covered\n // (the whole folder when it reached the end of the scope).\n const stale = [...saved.values()].filter(\n (h) => !seen.has(h.message.id) && (reachedEnd || h.sortAt >= oldestSeen)\n )\n await removeKeys(stale.map((h) => h.key))\n}\n\n/** Remove bodies/attachments no header points to any more, and folders/accounts that went away. */\nasync function collectGarbage(accounts: MailAccount[]): Promise<void> {\n for (const account of accounts) {\n const folders = (await loadFolders(account.id)) ?? []\n const expected = new Set<string>([await keyOf.folders(account.id)])\n for (const folder of folders) {\n for (const { message } of await loadHeaders(account.id, folder.id)) {\n expected.add(await keyOf.body(account.id, message.id))\n for (const attachment of message.attachments)\n expected.add(await keyOf.attachment(account.id, message.id, attachment.id))\n }\n }\n const orphans = (await recordsInBucket(await bucketOf.account(account.id)))\n .filter((r) => (r.kind === 'body' || r.kind === 'attachment') && !expected.has(r.key))\n .map((r) => r.key)\n await removeKeys(orphans)\n }\n}\n\nasync function forgetAccount(accountId: string): Promise<void> {\n for (const folder of (await loadFolders(accountId)) ?? []) {\n const records = await recordsInBucket(await bucketOf.folder(accountId, folder.id))\n await removeKeys(records.map((r) => r.key))\n }\n const records = await recordsInBucket(await bucketOf.account(accountId))\n await removeKeys(records.filter((r) => r.kind !== 'draft').map((r) => r.key))\n}\n\n/** Hold the store under the quota: least recently opened attachments first, then oldest bodies. */\nexport async function enforceQuota(quotaBytes: number): Promise<number> {\n let used = (await allRecordSizes()).reduce((n, r) => n + r.size, 0)\n if (used <= quotaBytes) return used\n for (const kind of ['attachment', 'body'] as const) {\n const records = (await recordsOfKind(kind)).sort((a, b) =>\n kind === 'attachment' ? a.lastAccess - b.lastAccess : a.sortAt - b.sortAt\n )\n const evict: string[] = []\n for (const record of records) {\n if (used <= quotaBytes) break\n evict.push(record.key)\n used -= record.size\n }\n await removeKeys(evict)\n if (used <= quotaBytes) break\n }\n return used\n}\n\nconst CONTENT_KINDS = ['header', 'body', 'attachment'] as const\n\n/**\n * Hold the device copy to the chosen window, with no network: mail received before it goes (its\n * list row, body and attachments; an undated message stays). Off: every folder and message goes,\n * and only the accounts, drafts and the Outbox stay. Returns the status with the new totals.\n */\nexport async function trimOfflineMailbox(\n settings: OfflineMailSettings,\n now = Date.now()\n): Promise<OfflineMailStatus> {\n const windowStart = offlineWindowStart(settings, now)\n const kinds = settings.enabled ? CONTENT_KINDS : ([...CONTENT_KINDS, 'folders'] as const)\n for (const kind of kinds) {\n const records = await recordsOfKind(kind)\n const old = settings.enabled\n ? records.filter((r) => r.sortAt > 0 && r.sortAt < windowStart)\n : records\n await removeKeys(old.map((r) => r.key))\n }\n const previous = await getOfflineMailStatus()\n const status: OfflineMailStatus = {\n ...previous,\n messages: (await recordsOfKind('header')).length,\n bytes: (await allRecordSizes()).reduce((n, r) => n + r.size, 0),\n // A shorter window no longer covers what the last full pass fetched: growing it again (even\n // before the next hourly full pass) makes the next pass a full one.\n windowDays:\n settings.enabled && previous.windowDays !== undefined\n ? Math.min(previous.windowDays, SCOPE_DAYS[settings.scope])\n : undefined,\n // Off: nothing is saved, so turning it back on starts with a full pass.\n ...(!settings.enabled && { lastSyncedAt: undefined, lastFullSyncAt: undefined }),\n }\n await setMeta(STATUS_KEY, status)\n notifyOfflineMailChange('status')\n return status\n}\n\n/**\n * One sync pass. Never throws: the outcome is recorded in the status (and announced), because it\n * runs in the background and a failure must show up as \"couldn't sync\", not vanish.\n *\n * `latest` reads the settings again at the end: a window shortened (or Off chosen) while the pass\n * ran is what the device copy is trimmed to, not the settings the pass started with.\n */\nexport async function syncMailbox(\n transport: MailTransport,\n settings: OfflineMailSettings,\n options: { full?: boolean; now?: number; latest?: () => OfflineMailSettings } = {}\n): Promise<OfflineMailStatus> {\n const now = options.now ?? Date.now()\n const previous = await getOfflineMailStatus()\n const days = SCOPE_DAYS[settings.scope]\n const full =\n options.full ||\n !previous.lastFullSyncAt ||\n now - previous.lastFullSyncAt > FULL_SYNC_EVERY_MS ||\n (previous.windowDays ?? 0) < days\n const windowStart = offlineWindowStart(settings, now)\n const quota = settings.quotaMb * MB\n const skipped: string[] = []\n let error: string | undefined\n try {\n await savePolicy(await transport.policy())\n const accounts = await transport.accounts()\n const before = (await loadAccounts()) ?? []\n await saveAccounts(accounts)\n for (const gone of before.filter((b) => !accounts.some((a) => a.id === b.id)))\n await forgetAccount(gone.id)\n const budget = new Budget(\n (await allRecordSizes()).reduce((n, r) => n + r.size, 0),\n quota\n )\n for (const account of accounts) {\n if (account.needs_reconsent) {\n skipped.push(account.email)\n continue\n }\n try {\n await transport.unlock(account)\n const folders = await transport.folders(account)\n const oldFolders = (await loadFolders(account.id)) ?? []\n await saveFolders(account.id, folders)\n for (const gone of oldFolders.filter((o) => !folders.some((f) => f.id === o.id))) {\n const records = await recordsInBucket(await bucketOf.folder(account.id, gone.id))\n await removeKeys(records.map((r) => r.key))\n }\n // Flagged is a view over the other folders: its messages are saved where they are.\n for (const folder of folders.filter((f) => !isVirtualFolder(f)))\n await syncFolder(transport, account, folder, settings, windowStart, full, budget)\n notifyOfflineMailChange('mailbox')\n } catch (accountError) {\n // Network loss mid-sync: stop; everything saved so far stays and the next pass continues.\n if ((accountError as { status?: number }).status === undefined) throw accountError\n logger.warn('[offline-mail] account sync failed', accountError)\n skipped.push(account.email)\n }\n }\n if (full) await collectGarbage(accounts)\n } catch (syncError) {\n error =\n (syncError as { status?: number }).status === undefined\n ? 'The connection dropped during sync. It will continue when you are back online.'\n : syncError instanceof Error\n ? syncError.message\n : 'Sync failed'\n }\n // Changes made offline that the server hasn't applied yet: the pass wrote the server's state.\n await overlayPendingMailChanges().catch((overlayError) =>\n logger.warn('[offline-mail] could not lay pending changes over the device copy', overlayError)\n )\n // Mail that aged out of the window since the last pass, under the settings as they are now.\n const current = options.latest?.() ?? settings\n const trimmed = await trimOfflineMailbox(current, now)\n // Off was chosen while this pass ran: nothing stays, and nothing counts as synced.\n if (!current.enabled) return trimmed\n const bytes = await enforceQuota(current.quotaMb * MB)\n const messages = (await recordsOfKind('header')).length\n const fullDone = !error && full\n const status: OfflineMailStatus = {\n lastSyncedAt: error ? previous.lastSyncedAt : now,\n lastFullSyncAt: fullDone ? now : previous.lastFullSyncAt,\n windowDays: fullDone ? Math.min(days, SCOPE_DAYS[current.scope]) : trimmed.windowDays,\n messages,\n bytes,\n error,\n skipped,\n }\n await setMeta(STATUS_KEY, status)\n notifyOfflineMailChange('status')\n return status\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["records"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { forgetOfflineMailKeys } from "./crypto.js";
|
|
2
|
+
import { deleteOfflineMailDatabase, getMeta, setMeta } from "./store.js";
|
|
3
|
+
import { notifyOfflineMailChange } from "./settings.js";
|
|
4
|
+
const OWNER_KEY = "owner";
|
|
5
|
+
async function wipeOfflineMail() {
|
|
6
|
+
forgetOfflineMailKeys();
|
|
7
|
+
await deleteOfflineMailDatabase();
|
|
8
|
+
notifyOfflineMailChange("wiped");
|
|
9
|
+
}
|
|
10
|
+
async function claimOfflineMail(userExtId) {
|
|
11
|
+
const owner = await getMeta(OWNER_KEY);
|
|
12
|
+
if (owner && owner !== userExtId) await wipeOfflineMail();
|
|
13
|
+
await setMeta(OWNER_KEY, userExtId);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
claimOfflineMail,
|
|
17
|
+
wipeOfflineMail
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=wipe.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/mail/offline/wipe.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Removing the offline mailbox: on sign-out, on \"Remove offline data\", and when a different account\n * signs in on this device.\n */\n\nimport { forgetOfflineMailKeys } from './crypto'\nimport { deleteOfflineMailDatabase, getMeta, setMeta } from './store'\nimport { notifyOfflineMailChange } from './settings'\n\nconst OWNER_KEY = 'owner'\n\n/** Delete everything the offline mailbox stored on this device (mail, drafts, Outbox, status). */\nexport async function wipeOfflineMail(): Promise<void> {\n forgetOfflineMailKeys()\n await deleteOfflineMailDatabase()\n notifyOfflineMailChange('wiped')\n}\n\n/**\n * Bind the store to the account signing in. A store written by someone else on this device is\n * removed (it is sealed to their key, so it is unreadable anyway, but it must not take space or\n * leave their queued mail behind).\n */\nexport async function claimOfflineMail(userExtId: string): Promise<void> {\n const owner = await getMeta<string>(OWNER_KEY)\n if (owner && owner !== userExtId) await wipeOfflineMail()\n await setMeta(OWNER_KEY, userExtId)\n}\n"],
|
|
5
|
+
"mappings": "AAKA,SAAS,6BAA6B;AACtC,SAAS,2BAA2B,SAAS,eAAe;AAC5D,SAAS,+BAA+B;AAExC,MAAM,YAAY;AAGlB,eAAsB,kBAAiC;AACrD,wBAAsB;AACtB,QAAM,0BAA0B;AAChC,0BAAwB,OAAO;AACjC;AAOA,eAAsB,iBAAiB,WAAkC;AACvE,QAAM,QAAQ,MAAM,QAAgB,SAAS;AAC7C,MAAI,SAAS,UAAU,UAAW,OAAM,gBAAgB;AACxD,QAAM,QAAQ,WAAW,SAAS;AACpC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { logger } from "@arbidocs/client";
|
|
4
|
+
import { mail as mailOps } from "@arbidocs/sdk/browser";
|
|
5
|
+
import { useMailOnline, useReadMailMessage } from "./hooks.js";
|
|
6
|
+
import { useMailClient } from "./useMailClient.js";
|
|
7
|
+
import { nativeThreadKeys } from "./threads.js";
|
|
8
|
+
import { mailQueryKeys } from "../query-keys.js";
|
|
9
|
+
const BATCH = 50;
|
|
10
|
+
const SECRET_FRESH_MS = 6e4;
|
|
11
|
+
const tried = /* @__PURE__ */ new Set();
|
|
12
|
+
function useNativeThreadKeys(account, messages) {
|
|
13
|
+
const { client, enabled } = useMailClient();
|
|
14
|
+
const isOnline = useMailOnline();
|
|
15
|
+
const queryClient = useQueryClient();
|
|
16
|
+
const readMessage = useReadMailMessage();
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!enabled || !isOnline || account?.provider !== "native" || !messages) return;
|
|
19
|
+
const unkeyed = messages.filter((m) => !m.thread_id && m.id.startsWith("eml-") && !tried.has(m.id)).slice(0, BATCH);
|
|
20
|
+
if (!unkeyed.length) return;
|
|
21
|
+
for (const m of unkeyed) tried.add(m.id);
|
|
22
|
+
const keyable = unkeyed.filter((m) => m.message_id);
|
|
23
|
+
if (!keyable.length) return;
|
|
24
|
+
void (async () => {
|
|
25
|
+
try {
|
|
26
|
+
const secret = await queryClient.fetchQuery({
|
|
27
|
+
queryKey: mailQueryKeys.threadSecret(),
|
|
28
|
+
queryFn: () => mailOps.getNativeThreadSecret(client.fetch),
|
|
29
|
+
staleTime: SECRET_FRESH_MS
|
|
30
|
+
});
|
|
31
|
+
const keys = {};
|
|
32
|
+
for (const listed of keyable) {
|
|
33
|
+
const source = listed.references.length ? listed : await readMessage(account, listed.id).catch(() => void 0);
|
|
34
|
+
const key = source && await nativeThreadKeys(secret, source);
|
|
35
|
+
if (key) keys[listed.id] = key;
|
|
36
|
+
}
|
|
37
|
+
if (!Object.keys(keys).length) return;
|
|
38
|
+
if (await mailOps.submitNativeThreadKeys(client.fetch, keys) > 0) {
|
|
39
|
+
await queryClient.invalidateQueries({ queryKey: mailQueryKeys.allConversations() });
|
|
40
|
+
await queryClient.invalidateQueries({ queryKey: mailQueryKeys.allMessages() });
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
queryClient.removeQueries({ queryKey: mailQueryKeys.threadSecret() });
|
|
44
|
+
for (const m of keyable) tried.delete(m.id);
|
|
45
|
+
logger.warn("[mail] could not key older mail into conversations", error);
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
}, [account, messages, enabled, isOnline, client, queryClient, readMessage]);
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
useNativeThreadKeys
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=thread-keys.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/mail/thread-keys.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Keying older native mail into conversations, lazily, in the background.\n *\n * The server keys every message it stores from now on, but it cannot key mail stored before: its\n * Message-IDs are encrypted to the owner. The owner's client can read them, so it computes the keys\n * (with the owner's thread secret) for the unkeyed messages it lists and submits them. Until then\n * such a message is a conversation of its own, online and offline alike.\n *\n * A list row carries the Message-ID and References; only a row with no References is read in full\n * (for its In-Reply-To), through the query cache, so a message already opened is not read again.\n */\n\nimport { useEffect } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { logger } from '@arbidocs/client'\nimport { mail as mailOps } from '@arbidocs/sdk/browser'\nimport { useMailOnline, useReadMailMessage } from './hooks'\nimport { useMailClient } from './useMailClient'\nimport { nativeThreadKeys } from './threads'\nimport { mailQueryKeys } from '../query-keys'\nimport type { MailAccount, MailMessage } from './types'\n\n/** Messages keyed per round: the next list read brings the next ones. */\nconst BATCH = 50\n\n/** How long a fetched thread secret is used before it is fetched again. */\nconst SECRET_FRESH_MS = 60_000\n\n/**\n * Messages tried in this page's life, across mounts: a keyed one needs no second try, and one with\n * no Message-ID never gets a key (it stays on its own). A failed round is tried again later.\n */\nconst tried = new Set<string>()\n\n/** Key the native `messages` listed that have no thread yet. Failures stay quiet. */\nexport function useNativeThreadKeys(\n account: MailAccount | undefined,\n messages: MailMessage[] | undefined\n): void {\n const { client, enabled } = useMailClient()\n const isOnline = useMailOnline()\n const queryClient = useQueryClient()\n const readMessage = useReadMailMessage()\n useEffect(() => {\n if (!enabled || !isOnline || account?.provider !== 'native' || !messages) return\n const unkeyed = messages\n .filter((m) => !m.thread_id && m.id.startsWith('eml-') && !tried.has(m.id))\n .slice(0, BATCH)\n if (!unkeyed.length) return\n for (const m of unkeyed) tried.add(m.id)\n const keyable = unkeyed.filter((m) => m.message_id)\n if (!keyable.length) return\n void (async () => {\n try {\n // Fetched again for a batch after a minute, and after any failure: a secret kept across a\n // change on the server (a tab open through a deploy) would key mail into conversations\n // nobody else's keys match, for good.\n const secret = await queryClient.fetchQuery({\n queryKey: mailQueryKeys.threadSecret(),\n queryFn: () => mailOps.getNativeThreadSecret(client.fetch),\n staleTime: SECRET_FRESH_MS,\n })\n const keys: Record<string, { thread_hmac: string; message_id_hmac?: string }> = {}\n for (const listed of keyable) {\n // Older list rows carry no In-Reply-To: one without References is read in full for it.\n const source = listed.references.length\n ? listed\n : await readMessage(account, listed.id).catch(() => undefined)\n const key = source && (await nativeThreadKeys(secret, source))\n if (key) keys[listed.id] = key\n }\n if (!Object.keys(keys).length) return\n if ((await mailOps.submitNativeThreadKeys(client.fetch, keys)) > 0) {\n await queryClient.invalidateQueries({ queryKey: mailQueryKeys.allConversations() })\n await queryClient.invalidateQueries({ queryKey: mailQueryKeys.allMessages() })\n }\n } catch (error) {\n queryClient.removeQueries({ queryKey: mailQueryKeys.threadSecret() })\n for (const m of keyable) tried.delete(m.id)\n logger.warn('[mail] could not key older mail into conversations', error)\n }\n })()\n }, [account, messages, enabled, isOnline, client, queryClient, readMessage])\n}\n"],
|
|
5
|
+
"mappings": "AAYA,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AACvB,SAAS,QAAQ,eAAe;AAChC,SAAS,eAAe,0BAA0B;AAClD,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAI9B,MAAM,QAAQ;AAGd,MAAM,kBAAkB;AAMxB,MAAM,QAAQ,oBAAI,IAAY;AAGvB,SAAS,oBACd,SACA,UACM;AACN,QAAM,EAAE,QAAQ,QAAQ,IAAI,cAAc;AAC1C,QAAM,WAAW,cAAc;AAC/B,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,mBAAmB;AACvC,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,YAAY,SAAS,aAAa,YAAY,CAAC,SAAU;AAC1E,UAAM,UAAU,SACb,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,GAAG,WAAW,MAAM,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,EACzE,MAAM,GAAG,KAAK;AACjB,QAAI,CAAC,QAAQ,OAAQ;AACrB,eAAW,KAAK,QAAS,OAAM,IAAI,EAAE,EAAE;AACvC,UAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU;AAClD,QAAI,CAAC,QAAQ,OAAQ;AACrB,UAAM,YAAY;AAChB,UAAI;AAIF,cAAM,SAAS,MAAM,YAAY,WAAW;AAAA,UAC1C,UAAU,cAAc,aAAa;AAAA,UACrC,SAAS,MAAM,QAAQ,sBAAsB,OAAO,KAAK;AAAA,UACzD,WAAW;AAAA,QACb,CAAC;AACD,cAAM,OAA0E,CAAC;AACjF,mBAAW,UAAU,SAAS;AAE5B,gBAAM,SAAS,OAAO,WAAW,SAC7B,SACA,MAAM,YAAY,SAAS,OAAO,EAAE,EAAE,MAAM,MAAM,MAAS;AAC/D,gBAAM,MAAM,UAAW,MAAM,iBAAiB,QAAQ,MAAM;AAC5D,cAAI,IAAK,MAAK,OAAO,EAAE,IAAI;AAAA,QAC7B;AACA,YAAI,CAAC,OAAO,KAAK,IAAI,EAAE,OAAQ;AAC/B,YAAK,MAAM,QAAQ,uBAAuB,OAAO,OAAO,IAAI,IAAK,GAAG;AAClE,gBAAM,YAAY,kBAAkB,EAAE,UAAU,cAAc,iBAAiB,EAAE,CAAC;AAClF,gBAAM,YAAY,kBAAkB,EAAE,UAAU,cAAc,YAAY,EAAE,CAAC;AAAA,QAC/E;AAAA,MACF,SAAS,OAAO;AACd,oBAAY,cAAc,EAAE,UAAU,cAAc,aAAa,EAAE,CAAC;AACpE,mBAAW,KAAK,QAAS,OAAM,OAAO,EAAE,EAAE;AAC1C,eAAO,KAAK,sDAAsD,KAAK;AAAA,MACzE;AAAA,IACF,GAAG;AAAA,EACL,GAAG,CAAC,SAAS,UAAU,SAAS,UAAU,QAAQ,aAAa,WAAW,CAAC;AAC7E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
function mailConversationKey(message) {
|
|
2
|
+
return message.thread_id || message.id;
|
|
3
|
+
}
|
|
4
|
+
function toMailConversation(threadId, members) {
|
|
5
|
+
const newest = members[0];
|
|
6
|
+
const senders = /* @__PURE__ */ new Map();
|
|
7
|
+
for (const message of [...members].reverse())
|
|
8
|
+
for (const address of message.sender)
|
|
9
|
+
if (!senders.has(address.email.toLowerCase()))
|
|
10
|
+
senders.set(address.email.toLowerCase(), address);
|
|
11
|
+
return {
|
|
12
|
+
thread_id: threadId,
|
|
13
|
+
subject: [...members].reverse().find((m) => m.subject)?.subject ?? "",
|
|
14
|
+
preview: newest.preview,
|
|
15
|
+
senders: [...senders.values()],
|
|
16
|
+
received_at: newest.received_at ?? null,
|
|
17
|
+
message_count: members.length,
|
|
18
|
+
unread_count: members.filter((m) => m.unread).length,
|
|
19
|
+
flagged: members.some((m) => m.flagged),
|
|
20
|
+
messages: members
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function groupMailConversations(messages) {
|
|
24
|
+
const groups = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const message of messages) {
|
|
26
|
+
const key = mailConversationKey(message);
|
|
27
|
+
const members = groups.get(key);
|
|
28
|
+
if (members) members.push(message);
|
|
29
|
+
else groups.set(key, [message]);
|
|
30
|
+
}
|
|
31
|
+
return [...groups].map(([key, members]) => toMailConversation(key, members));
|
|
32
|
+
}
|
|
33
|
+
const MESSAGE_ID = /<[^<>\s]+>/g;
|
|
34
|
+
function ids(header) {
|
|
35
|
+
if (!header || header.length === 0) return [];
|
|
36
|
+
const text = Array.isArray(header) ? header.join(" ") : header;
|
|
37
|
+
return text.match(MESSAGE_ID) ?? text.split(/\s+/).filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
function mailThreadRoot(message) {
|
|
40
|
+
return ids(message.references)[0] ?? ids(message.in_reply_to)[0] ?? ids(message.message_id)[0];
|
|
41
|
+
}
|
|
42
|
+
async function hmacKey(secret, value) {
|
|
43
|
+
const key = await crypto.subtle.importKey(
|
|
44
|
+
"raw",
|
|
45
|
+
secret,
|
|
46
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
47
|
+
false,
|
|
48
|
+
["sign"]
|
|
49
|
+
);
|
|
50
|
+
const mac = new Uint8Array(
|
|
51
|
+
await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`message:${value.trim()}`))
|
|
52
|
+
);
|
|
53
|
+
return Array.from(mac.slice(0, 16), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
54
|
+
}
|
|
55
|
+
async function nativeThreadKeys(secret, message) {
|
|
56
|
+
const root = mailThreadRoot(message);
|
|
57
|
+
if (!root) return void 0;
|
|
58
|
+
const own = ids(message.message_id)[0];
|
|
59
|
+
return {
|
|
60
|
+
thread_hmac: await hmacKey(secret, root),
|
|
61
|
+
...own && { message_id_hmac: await hmacKey(secret, own) }
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
groupMailConversations,
|
|
66
|
+
mailConversationKey,
|
|
67
|
+
mailThreadRoot,
|
|
68
|
+
nativeThreadKeys,
|
|
69
|
+
toMailConversation
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=threads.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/mail/threads.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Conversations: how messages group into threads, the same rule as the server's, so a list read\n * from this device groups exactly as one read online (`group_conversations` in the backend).\n *\n * A message's conversation is its `thread_id`, else (it stands alone) its own id. Native mail's\n * thread id is a per-user key: an HMAC of the thread's root Message-ID (the first References entry,\n * else In-Reply-To, else its own Message-ID), as the backend's `email_threading.thread_keys`. The\n * server keys mail it stores; older mail is keyed by the owner's client ({@link nativeThreadKeys}),\n * because only the client can read its Message-IDs.\n */\n\nimport type { MailAddress, MailConversation, MailMessage } from './types'\n\n/** What groups a message into a conversation. */\nexport function mailConversationKey(message: Pick<MailMessage, 'id' | 'thread_id'>): string {\n return message.thread_id || message.id\n}\n\n/** A conversation row from its messages (newest first). */\nexport function toMailConversation(threadId: string, members: MailMessage[]): MailConversation {\n const newest = members[0]\n const senders = new Map<string, MailAddress>()\n for (const message of [...members].reverse())\n for (const address of message.sender)\n if (!senders.has(address.email.toLowerCase()))\n senders.set(address.email.toLowerCase(), address)\n return {\n thread_id: threadId,\n subject: [...members].reverse().find((m) => m.subject)?.subject ?? '',\n preview: newest.preview,\n senders: [...senders.values()],\n received_at: newest.received_at ?? null,\n message_count: members.length,\n unread_count: members.filter((m) => m.unread).length,\n flagged: members.some((m) => m.flagged),\n messages: members,\n }\n}\n\n/** Conversations of `messages` (newest first), in the order of each one's newest message. */\nexport function groupMailConversations(messages: MailMessage[]): MailConversation[] {\n const groups = new Map<string, MailMessage[]>()\n for (const message of messages) {\n const key = mailConversationKey(message)\n const members = groups.get(key)\n if (members) members.push(message)\n else groups.set(key, [message])\n }\n return [...groups].map(([key, members]) => toMailConversation(key, members))\n}\n\nconst MESSAGE_ID = /<[^<>\\s]+>/g\n\n/** Message-IDs in a header, as the backend reads them (`<\u2026>` tokens, else whitespace-split). */\nfunction ids(header: string | string[] | null | undefined): string[] {\n if (!header || header.length === 0) return []\n const text = Array.isArray(header) ? header.join(' ') : header\n return text.match(MESSAGE_ID) ?? text.split(/\\s+/).filter(Boolean)\n}\n\n/** The thread's root Message-ID: the first References entry, else In-Reply-To, else its own. */\nexport function mailThreadRoot(\n message: Pick<MailMessage, 'message_id' | 'in_reply_to' | 'references'>\n): string | undefined {\n return ids(message.references)[0] ?? ids(message.in_reply_to)[0] ?? ids(message.message_id)[0]\n}\n\nasync function hmacKey(secret: Uint8Array, value: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n secret as BufferSource,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n )\n const mac = new Uint8Array(\n await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(`message:${value.trim()}`))\n )\n return Array.from(mac.slice(0, 16), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * A native message's keys, computed with the owner's thread secret: its thread key and its own\n * Message-ID's. `undefined` when it has no Message-ID to key (it stays a conversation of its own).\n */\nexport async function nativeThreadKeys(\n secret: Uint8Array,\n message: Pick<MailMessage, 'message_id' | 'in_reply_to' | 'references'>\n): Promise<{ thread_hmac: string; message_id_hmac?: string } | undefined> {\n const root = mailThreadRoot(message)\n if (!root) return undefined\n const own = ids(message.message_id)[0]\n return {\n thread_hmac: await hmacKey(secret, root),\n ...(own && { message_id_hmac: await hmacKey(secret, own) }),\n }\n}\n"],
|
|
5
|
+
"mappings": "AAcO,SAAS,oBAAoB,SAAwD;AAC1F,SAAO,QAAQ,aAAa,QAAQ;AACtC;AAGO,SAAS,mBAAmB,UAAkB,SAA0C;AAC7F,QAAM,SAAS,QAAQ,CAAC;AACxB,QAAM,UAAU,oBAAI,IAAyB;AAC7C,aAAW,WAAW,CAAC,GAAG,OAAO,EAAE,QAAQ;AACzC,eAAW,WAAW,QAAQ;AAC5B,UAAI,CAAC,QAAQ,IAAI,QAAQ,MAAM,YAAY,CAAC;AAC1C,gBAAQ,IAAI,QAAQ,MAAM,YAAY,GAAG,OAAO;AACtD,SAAO;AAAA,IACL,WAAW;AAAA,IACX,SAAS,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW;AAAA,IACnE,SAAS,OAAO;AAAA,IAChB,SAAS,CAAC,GAAG,QAAQ,OAAO,CAAC;AAAA,IAC7B,aAAa,OAAO,eAAe;AAAA,IACnC,eAAe,QAAQ;AAAA,IACvB,cAAc,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE;AAAA,IAC9C,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO;AAAA,IACtC,UAAU;AAAA,EACZ;AACF;AAGO,SAAS,uBAAuB,UAA6C;AAClF,QAAM,SAAS,oBAAI,IAA2B;AAC9C,aAAW,WAAW,UAAU;AAC9B,UAAM,MAAM,oBAAoB,OAAO;AACvC,UAAM,UAAU,OAAO,IAAI,GAAG;AAC9B,QAAI,QAAS,SAAQ,KAAK,OAAO;AAAA,QAC5B,QAAO,IAAI,KAAK,CAAC,OAAO,CAAC;AAAA,EAChC;AACA,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,OAAO,MAAM,mBAAmB,KAAK,OAAO,CAAC;AAC7E;AAEA,MAAM,aAAa;AAGnB,SAAS,IAAI,QAAwD;AACnE,MAAI,CAAC,UAAU,OAAO,WAAW,EAAG,QAAO,CAAC;AAC5C,QAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,KAAK,GAAG,IAAI;AACxD,SAAO,KAAK,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK,EAAE,OAAO,OAAO;AACnE;AAGO,SAAS,eACd,SACoB;AACpB,SAAO,IAAI,QAAQ,UAAU,EAAE,CAAC,KAAK,IAAI,QAAQ,WAAW,EAAE,CAAC,KAAK,IAAI,QAAQ,UAAU,EAAE,CAAC;AAC/F;AAEA,eAAe,QAAQ,QAAoB,OAAgC;AACzE,QAAM,MAAM,MAAM,OAAO,OAAO;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,MAAM,IAAI;AAAA,IACd,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,WAAW,MAAM,KAAK,CAAC,EAAE,CAAC;AAAA,EAC3F;AACA,SAAO,MAAM,KAAK,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACrF;AAMA,eAAsB,iBACpB,QACA,SACwE;AACxE,QAAM,OAAO,eAAe,OAAO;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,MAAM,IAAI,QAAQ,UAAU,EAAE,CAAC;AACrC,SAAO;AAAA,IACL,aAAa,MAAM,QAAQ,QAAQ,IAAI;AAAA,IACvC,GAAI,OAAO,EAAE,iBAAiB,MAAM,QAAQ,QAAQ,GAAG,EAAE;AAAA,EAC3D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useArbiHost } from "../host/config.js";
|
|
3
|
+
import { arbiMailClient, createMailClient } from "./client.js";
|
|
4
|
+
import { offlineMailReads } from "./offline/reads.js";
|
|
5
|
+
const NO_MAIL = {
|
|
6
|
+
client: createMailClient(
|
|
7
|
+
() => Promise.reject(new Error("Mail needs an <ArbiHost> with `mail` or an `arbi`"))
|
|
8
|
+
),
|
|
9
|
+
enabled: false,
|
|
10
|
+
sessionKey: null
|
|
11
|
+
};
|
|
12
|
+
const ALWAYS = () => ({ enabled: true, sessionKey: "session" });
|
|
13
|
+
function useMailClient() {
|
|
14
|
+
const { mail, arbi } = useArbiHost();
|
|
15
|
+
const fallback = useMemo(
|
|
16
|
+
() => arbi ? arbiMailClient(
|
|
17
|
+
arbi,
|
|
18
|
+
offlineMailReads(() => arbi)
|
|
19
|
+
) : null,
|
|
20
|
+
[arbi]
|
|
21
|
+
);
|
|
22
|
+
const useStatus = mail ? mail.useStatus ?? ALWAYS : ALWAYS;
|
|
23
|
+
const { enabled, sessionKey } = useStatus();
|
|
24
|
+
const client = mail?.client;
|
|
25
|
+
const provided = useMemo(
|
|
26
|
+
() => client ? { client, enabled, sessionKey } : null,
|
|
27
|
+
[client, enabled, sessionKey]
|
|
28
|
+
);
|
|
29
|
+
if (provided) return provided;
|
|
30
|
+
if (!arbi || !fallback) return NO_MAIL;
|
|
31
|
+
let identity = null;
|
|
32
|
+
try {
|
|
33
|
+
identity = arbi.isLoggedIn ? arbi.user.identity()?.externalId ?? "session" : null;
|
|
34
|
+
} catch {
|
|
35
|
+
identity = null;
|
|
36
|
+
}
|
|
37
|
+
return { client: fallback, enabled: identity !== null, sessionKey: identity };
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
useMailClient
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=useMailClient.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/mail/useMailClient.ts"],
|
|
4
|
+
"sourcesContent": ["import { useMemo } from 'react'\nimport { useArbiHost } from '../host/config'\nimport { arbiMailClient, createMailClient, type MailClient } from './client'\nimport { offlineMailReads } from './offline/reads'\n\nexport interface MailClientContextValue {\n client: MailClient\n /** Mail reads run only while this is true (signed in). */\n enabled: boolean\n /**\n * Identifies the server session the mailbox is unlocked for: unlocking runs again when it\n * changes. `null`: no session yet, so nothing is unlocked.\n */\n sessionKey: string | null\n}\n\nconst NO_MAIL: MailClientContextValue = {\n client: createMailClient(() =>\n Promise.reject(new Error('Mail needs an <ArbiHost> with `mail` or an `arbi`'))\n ),\n enabled: false,\n sessionKey: null,\n}\n\nconst ALWAYS = () => ({ enabled: true, sessionKey: 'session' })\n\n/**\n * The mail client in scope: the host configuration's `mail`, else one on its `arbi` session\n * (`arbiMailClient`) whose reads keep working offline from the mail kept on this device\n * (`offlineMailReads`).\n */\nexport function useMailClient(): MailClientContextValue {\n const { mail, arbi } = useArbiHost()\n const fallback = useMemo(\n () =>\n arbi\n ? arbiMailClient(\n arbi,\n offlineMailReads(() => arbi)\n )\n : null,\n [arbi]\n )\n // The host's status hook is fixed for its configuration's life.\n const useStatus = mail ? (mail.useStatus ?? ALWAYS) : ALWAYS\n const { enabled, sessionKey } = useStatus()\n const client = mail?.client\n const provided = useMemo(\n () => (client ? { client, enabled, sessionKey } : null),\n [client, enabled, sessionKey]\n )\n if (provided) return provided\n // Neither: mail stays off (reads disabled, writes refused) rather than breaking the screen.\n if (!arbi || !fallback) return NO_MAIL\n let identity: string | null = null\n try {\n identity = arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? 'session') : null\n } catch {\n identity = null\n }\n return { client: fallback, enabled: identity !== null, sessionKey: identity }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB,wBAAyC;AAClE,SAAS,wBAAwB;AAajC,MAAM,UAAkC;AAAA,EACtC,QAAQ;AAAA,IAAiB,MACvB,QAAQ,OAAO,IAAI,MAAM,mDAAmD,CAAC;AAAA,EAC/E;AAAA,EACA,SAAS;AAAA,EACT,YAAY;AACd;AAEA,MAAM,SAAS,OAAO,EAAE,SAAS,MAAM,YAAY,UAAU;AAOtD,SAAS,gBAAwC;AACtD,QAAM,EAAE,MAAM,KAAK,IAAI,YAAY;AACnC,QAAM,WAAW;AAAA,IACf,MACE,OACI;AAAA,MACE;AAAA,MACA,iBAAiB,MAAM,IAAI;AAAA,IAC7B,IACA;AAAA,IACN,CAAC,IAAI;AAAA,EACP;AAEA,QAAM,YAAY,OAAQ,KAAK,aAAa,SAAU;AACtD,QAAM,EAAE,SAAS,WAAW,IAAI,UAAU;AAC1C,QAAM,SAAS,MAAM;AACrB,QAAM,WAAW;AAAA,IACf,MAAO,SAAS,EAAE,QAAQ,SAAS,WAAW,IAAI;AAAA,IAClD,CAAC,QAAQ,SAAS,UAAU;AAAA,EAC9B;AACA,MAAI,SAAU,QAAO;AAErB,MAAI,CAAC,QAAQ,CAAC,SAAU,QAAO;AAC/B,MAAI,WAA0B;AAC9B,MAAI;AACF,eAAW,KAAK,aAAc,KAAK,KAAK,SAAS,GAAG,cAAc,YAAa;AAAA,EACjF,QAAQ;AACN,eAAW;AAAA,EACb;AACA,SAAO,EAAE,QAAQ,UAAU,SAAS,aAAa,MAAM,YAAY,SAAS;AAC9E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|