@arbidocs/blocks 0.4.106 → 0.4.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/bin/arbidocs-render-take.mjs +27 -0
  2. package/dist/NativeDocxEditor-3VKJT43D.js +10 -0
  3. package/dist/NativeDocxEditor-3VKJT43D.js.map +1 -0
  4. package/dist/NativeDocxEditor-GNG5QV7V.cjs +31 -0
  5. package/dist/NativeDocxEditor-GNG5QV7V.cjs.map +1 -0
  6. package/dist/agents/index.cjs +209 -209
  7. package/dist/agents/index.js +4 -4
  8. package/dist/chat/index.cjs +125 -125
  9. package/dist/chat/index.js +3 -3
  10. package/dist/{chunk-PIT2YZCK.js → chunk-2PYENCW5.js} +3 -3
  11. package/dist/{chunk-PIT2YZCK.js.map → chunk-2PYENCW5.js.map} +1 -1
  12. package/dist/{chunk-W2RTZOK3.js → chunk-2SJTXYRM.js} +1749 -1778
  13. package/dist/chunk-2SJTXYRM.js.map +1 -0
  14. package/dist/{chunk-S7RHJI5Q.js → chunk-7NLPKJV5.js} +3 -3
  15. package/dist/{chunk-S7RHJI5Q.js.map → chunk-7NLPKJV5.js.map} +1 -1
  16. package/dist/{chunk-ZWWN4CUL.cjs → chunk-BHKFENKT.cjs} +1748 -1778
  17. package/dist/chunk-BHKFENKT.cjs.map +1 -0
  18. package/dist/{chunk-3R6XBYL7.cjs → chunk-DTBSGG36.cjs} +7 -2
  19. package/dist/chunk-DTBSGG36.cjs.map +1 -0
  20. package/dist/{chunk-3Y6NQIDE.js → chunk-EB7QAZLH.js} +5 -5
  21. package/dist/{chunk-3Y6NQIDE.js.map → chunk-EB7QAZLH.js.map} +1 -1
  22. package/dist/{chunk-TP3FAP5J.cjs → chunk-FZGMY6NQ.cjs} +5 -5
  23. package/dist/{chunk-TP3FAP5J.cjs.map → chunk-FZGMY6NQ.cjs.map} +1 -1
  24. package/dist/{chunk-W7NZRZ54.js → chunk-GFYGYME3.js} +3 -3
  25. package/dist/{chunk-W7NZRZ54.js.map → chunk-GFYGYME3.js.map} +1 -1
  26. package/dist/chunk-GYAUJ4KT.js +71 -0
  27. package/dist/chunk-GYAUJ4KT.js.map +1 -0
  28. package/dist/{chunk-XMBYDLZQ.cjs → chunk-HUJLKDWX.cjs} +4 -4
  29. package/dist/{chunk-XMBYDLZQ.cjs.map → chunk-HUJLKDWX.cjs.map} +1 -1
  30. package/dist/{chunk-4GFB5U3K.js → chunk-LHBJJ2GE.js} +4 -3
  31. package/dist/chunk-LHBJJ2GE.js.map +1 -0
  32. package/dist/{chunk-MP6FK43E.cjs → chunk-O5NDG4EA.cjs} +3 -3
  33. package/dist/{chunk-MP6FK43E.cjs.map → chunk-O5NDG4EA.cjs.map} +1 -1
  34. package/dist/{chunk-5YXG342J.cjs → chunk-OR4CO743.cjs} +3 -3
  35. package/dist/{chunk-5YXG342J.cjs.map → chunk-OR4CO743.cjs.map} +1 -1
  36. package/dist/{chunk-LGX6UFYG.js → chunk-P2KPDPFF.js} +8 -3
  37. package/dist/chunk-P2KPDPFF.js.map +1 -0
  38. package/dist/{chunk-DDF5GPIJ.js → chunk-P5ST47VY.js} +3 -3
  39. package/dist/{chunk-DDF5GPIJ.js.map → chunk-P5ST47VY.js.map} +1 -1
  40. package/dist/{chunk-EWSEQEPL.js → chunk-PFA43QNX.js} +3 -3
  41. package/dist/{chunk-EWSEQEPL.js.map → chunk-PFA43QNX.js.map} +1 -1
  42. package/dist/{chunk-7LLLQUM7.cjs → chunk-Q57MEBCK.cjs} +6 -5
  43. package/dist/chunk-Q57MEBCK.cjs.map +1 -0
  44. package/dist/{chunk-LLFW2T6S.cjs → chunk-R6APX23R.cjs} +11 -11
  45. package/dist/{chunk-LLFW2T6S.cjs.map → chunk-R6APX23R.cjs.map} +1 -1
  46. package/dist/{chunk-24ND4S6Y.cjs → chunk-RILMICOP.cjs} +5 -5
  47. package/dist/{chunk-24ND4S6Y.cjs.map → chunk-RILMICOP.cjs.map} +1 -1
  48. package/dist/chunk-S24XEVPS.cjs +73 -0
  49. package/dist/chunk-S24XEVPS.cjs.map +1 -0
  50. package/dist/{chunk-XQDM6T6C.js → chunk-WDD255XQ.js} +3 -3
  51. package/dist/{chunk-XQDM6T6C.js.map → chunk-WDD255XQ.js.map} +1 -1
  52. package/dist/{chunk-Z5VUZ6YD.cjs → chunk-ZV3BKLFY.cjs} +3 -3
  53. package/dist/{chunk-Z5VUZ6YD.cjs.map → chunk-ZV3BKLFY.cjs.map} +1 -1
  54. package/dist/collaboration/index.cjs +15 -15
  55. package/dist/collaboration/index.js +1 -1
  56. package/dist/collaboration/mail/MailMessageView.d.ts +3 -1
  57. package/dist/connect/docx.cjs +7 -6
  58. package/dist/connect/docx.cjs.map +1 -1
  59. package/dist/connect/docx.js +5 -4
  60. package/dist/connect/docx.js.map +1 -1
  61. package/dist/connect/index.cjs +407 -91
  62. package/dist/connect/index.cjs.map +1 -1
  63. package/dist/connect/index.js +361 -46
  64. package/dist/connect/index.js.map +1 -1
  65. package/dist/connection/mail/ConnectedMailComposer.d.ts +12 -2
  66. package/dist/connection/mail/ConnectedMailMessage.d.ts +19 -0
  67. package/dist/connection/mail/ConnectedMailbox.d.ts +7 -3
  68. package/dist/connection/mail/MailComposerForm.d.ts +12 -2
  69. package/dist/connection/mail/index.d.ts +2 -1
  70. package/dist/connection/mail/types.d.ts +15 -0
  71. package/dist/connection/mail/useWordBody.d.ts +48 -0
  72. package/dist/docx/NativeDocxEditor.d.ts +9 -1
  73. package/dist/docx/index.cjs +51 -50
  74. package/dist/docx/index.cjs.map +1 -1
  75. package/dist/docx/index.js +4 -3
  76. package/dist/docx/index.js.map +1 -1
  77. package/dist/facts/index.cjs +2 -2
  78. package/dist/facts/index.js +2 -2
  79. package/dist/files/index.cjs +2 -2
  80. package/dist/files/index.js +2 -2
  81. package/dist/home/index.cjs +17 -17
  82. package/dist/home/index.js +3 -3
  83. package/dist/index.cjs +442 -442
  84. package/dist/index.js +8 -8
  85. package/dist/record/index.cjs +307 -66
  86. package/dist/record/index.cjs.map +1 -1
  87. package/dist/record/index.d.ts +145 -26
  88. package/dist/record/index.js +301 -67
  89. package/dist/record/index.js.map +1 -1
  90. package/dist/retrieval/index.cjs +1 -1
  91. package/dist/retrieval/index.js +1 -1
  92. package/dist/settings/index.cjs +35 -35
  93. package/dist/settings/index.js +2 -2
  94. package/dist/shell/index.cjs +1 -1
  95. package/dist/shell/index.js +1 -1
  96. package/dist/studio/index.cjs +4 -4
  97. package/dist/studio/index.js +3 -3
  98. package/dist/workflow-canvas/index.cjs +11 -11
  99. package/dist/workflow-canvas/index.js +4 -4
  100. package/dist/workspaces/index.cjs +2 -2
  101. package/dist/workspaces/index.js +2 -2
  102. package/node_modules/@arbidocs/coedit/package.json +1 -1
  103. package/package.json +8 -5
  104. package/record/render-overlays.mjs +152 -0
  105. package/record/render-take.py +985 -0
  106. package/src/docx/styles.css +22 -0
  107. package/dist/chunk-3R6XBYL7.cjs.map +0 -1
  108. package/dist/chunk-4GFB5U3K.js.map +0 -1
  109. package/dist/chunk-7LLLQUM7.cjs.map +0 -1
  110. package/dist/chunk-LGX6UFYG.js.map +0 -1
  111. package/dist/chunk-W2RTZOK3.js.map +0 -1
  112. package/dist/chunk-ZWWN4CUL.cjs.map +0 -1
@@ -1,12 +1,13 @@
1
1
  "use client";
2
- import { SettingsSection, ProfileCard, AvatarPicker, IdentityVerificationCard, IdentityVerificationDialog, RequireVerified, SettingsCard, SettingsActionRow, PasswordChangeDialog, EMAIL_NOTIFICATION_KEYS, EMAIL_NOTIFICATION_CATEGORIES, NotificationCategoryList, toNotificationCategories, toActiveSessionViews, SettingsRow, ActiveSessionsDialog, LogoutDialog, toLogoutSessionViews, SettingsDangerZone, DeleteAccountDialog, toDeleteBlockerViews } from '../chunk-W7NZRZ54.js';
3
- import { useWorkspaceDocs } from '../chunk-XQDM6T6C.js';
4
- export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown, useConnectedAiTask, useConnectedChat, useSemanticSearch, useWorkspaceDocs } from '../chunk-XQDM6T6C.js';
5
- import { ConnectedNativeDocxEditor } from '../chunk-4GFB5U3K.js';
2
+ import { SettingsSection, ProfileCard, AvatarPicker, IdentityVerificationCard, IdentityVerificationDialog, RequireVerified, SettingsCard, SettingsActionRow, PasswordChangeDialog, EMAIL_NOTIFICATION_KEYS, EMAIL_NOTIFICATION_CATEGORIES, NotificationCategoryList, toNotificationCategories, toActiveSessionViews, SettingsRow, ActiveSessionsDialog, LogoutDialog, toLogoutSessionViews, SettingsDangerZone, DeleteAccountDialog, toDeleteBlockerViews } from '../chunk-GFYGYME3.js';
3
+ import { useWorkspaceDocs } from '../chunk-WDD255XQ.js';
4
+ export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown, useConnectedAiTask, useConnectedChat, useSemanticSearch, useWorkspaceDocs } from '../chunk-WDD255XQ.js';
5
+ import { ConnectedNativeDocxEditor } from '../chunk-LHBJJ2GE.js';
6
6
  import { ConnectionProvider, useConnection } from '../chunk-OYMWV45N.js';
7
7
  export { ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, useConnection } from '../chunk-OYMWV45N.js';
8
8
  import { SignDocumentDialog } from '../chunk-2YOVK524.js';
9
- import '../chunk-W2RTZOK3.js';
9
+ import '../chunk-GYAUJ4KT.js';
10
+ import '../chunk-2SJTXYRM.js';
10
11
  import '../chunk-HFZ45FD3.js';
11
12
  import { FilesFilterBar, FolderFilterButton, FolderPickerRow } from '../chunk-7RJTEWYM.js';
12
13
  import { WorkspaceActivityList, WorkspaceNotificationMenu, ThreadFollowMenuItem, ThreadFollowButton, TagTable, tagTableDocx } from '../chunk-S55VDTX3.js';
@@ -21,21 +22,21 @@ import { FilterInput } from '../chunk-YU2KUMGE.js';
21
22
  import '../chunk-XD2M6LIG.js';
22
23
  import { StreamedArtifactFrame } from '../chunk-LOJVUGBJ.js';
23
24
  import '../chunk-VILAPTCB.js';
24
- import { ChatPage } from '../chunk-EWSEQEPL.js';
25
+ import { ChatPage } from '../chunk-PFA43QNX.js';
25
26
  import '../chunk-KZJAG7T3.js';
26
27
  import '../chunk-HOXKR26S.js';
27
28
  import '../chunk-AA3PT6KR.js';
28
29
  import '../chunk-NPLYHR2E.js';
29
- import { initials } from '../chunk-OXF2IFXG.js';
30
- import { PlainMailBodyEditor, draftMailCompose, emptyMailCompose, MailGrid, MailMessageView, replyMailCompose, MailDocumentPicker, parseMailRecipients } from '../chunk-LGX6UFYG.js';
30
+ import { PlainMailBodyEditor, draftMailCompose, replyMailCompose, emptyMailCompose, MailGrid, MailMessageView, MailDocumentPicker, parseMailRecipients } from '../chunk-P2KPDPFF.js';
31
31
  import { ModalScaffold } from '../chunk-TX2ZICS7.js';
32
32
  import '../chunk-ZPLG7QYF.js';
33
33
  import '../chunk-SLSBJRWW.js';
34
34
  import '../chunk-7F6CBP3Z.js';
35
+ import { initials } from '../chunk-OXF2IFXG.js';
35
36
  import '../chunk-VUNV25KB.js';
36
37
  import { lazy, useState, useMemo, Suspense, useEffect, useRef, useCallback, useLayoutEffect } from 'react';
37
38
  import { QueryClient, QueryClientProvider, useQueryClient, useQuery } from '@tanstack/react-query';
38
- import { ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, ArtifactCanvas, useArbiChat, useArbi, useDocuments, useTagKindSchema, useTags, useSetTagValues, useUpdateTagKindSchema, useInvalidateTagData, useWorkspaces, useShellWorkspaces, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useLogoutAllSessions, useDeleteAccount, useDeleteAccountFlow, useArbiWebSocket, notify, useWorkNotifications, mailDraftIdFromContent, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailPolicy, useMailAccounts, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailCache, useMailMessage, MailOfflineUnavailableError, accountPath, useAgentSession, useStartAgentSession, useCloseAgentSession, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, ArbiWebSocketProvider, useCreateWork, useActOnStep, useConversationActivity, useConversationThreads, fileAttachment, useSaveMailDraft, useSendMail, AiAnswer } from '@arbidocs/react';
39
+ import { ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, ArtifactCanvas, useArbiChat, useArbi, useDocuments, useTagKindSchema, useTags, useSetTagValues, useUpdateTagKindSchema, useInvalidateTagData, useWorkspaces, useShellWorkspaces, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useLogoutAllSessions, useDeleteAccount, useDeleteAccountFlow, useArbiWebSocket, notify, useWorkNotifications, mailDraftIdFromContent, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailPolicy, useMailAccounts, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailMessage, useMailCache, MailOfflineUnavailableError, accountPath, useAgentSession, useStartAgentSession, useCloseAgentSession, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, ArbiWebSocketProvider, useCreateWork, useActOnStep, useConversationActivity, useConversationThreads, fileAttachment, useMailProjects, useSaveMailDraft, useSendMail, useMailDraftReplaced, AiAnswer, useMailDraftEditor } from '@arbidocs/react';
39
40
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
40
41
  import { Button, Label, Input, Dialog, DialogContent, DialogTitle, DialogDescription, Tooltip, TooltipTrigger, TooltipContent, cn } from '@arbidocs/react/ui';
41
42
  import { useStreamedArtifact } from '@arbidocs/react/artifacts';
@@ -43,6 +44,7 @@ import { ExternalLink, UserCircle, KeyRound, Radio, Mail, Settings, Plus, ArrowL
43
44
  import { useOptionalDocumentTools, fetchDocumentPdf, DocumentToolsProvider } from '@arbidocs/react/document-tools';
44
45
  import { useNotify } from '@arbidocs/react/shell';
45
46
  import { bytesToBase64 } from '@arbidocs/client';
47
+ import { createPortal } from 'react-dom';
46
48
 
47
49
  function ConnectionBoundWebSocket({ children }) {
48
50
  const { status } = useConnection();
@@ -2063,11 +2065,96 @@ function ConnectedMailAccounts({
2063
2065
  }
2064
2066
  );
2065
2067
  }
2068
+ var sessionRooms = /* @__PURE__ */ new Map();
2069
+ function useWordBody({
2070
+ word,
2071
+ client,
2072
+ initial,
2073
+ sessionKey,
2074
+ roomId: givenRoomId,
2075
+ onBodyChange
2076
+ }) {
2077
+ const [documentKey] = useState(
2078
+ () => `mail-${sessionKey ?? initial.draft_id ?? crypto.randomUUID()}`
2079
+ );
2080
+ const roomRef = useRef(sessionRooms.get(documentKey) ?? givenRoomId);
2081
+ const [roomId, setRoomId] = useState(roomRef.current);
2082
+ const [project, setProject] = useState();
2083
+ const stateRef = useRef(null);
2084
+ const changed = useRef(onBodyChange);
2085
+ useEffect(() => {
2086
+ changed.current = onBodyChange;
2087
+ });
2088
+ const opening = useRef(null);
2089
+ const adapter = useMemo(
2090
+ () => ({
2091
+ ...word.adapter,
2092
+ open: async () => {
2093
+ if (!roomRef.current && opening.current) return opening.current;
2094
+ const request = client.openDraftEditor(
2095
+ roomRef.current ? { roomId: roomRef.current } : { draftId: initial.draft_id ?? void 0, text: initial.text, html: initial.html }
2096
+ );
2097
+ if (!roomRef.current) opening.current = request;
2098
+ try {
2099
+ const session = await request;
2100
+ roomRef.current = session.room_id;
2101
+ sessionRooms.set(documentKey, session.room_id);
2102
+ setRoomId(session.room_id);
2103
+ setProject(session.project ?? null);
2104
+ return session;
2105
+ } finally {
2106
+ opening.current = null;
2107
+ }
2108
+ },
2109
+ save: async () => {
2110
+ throw new Error("Save the draft from the composer");
2111
+ },
2112
+ onDirtyChange: (dirty) => {
2113
+ if (dirty) changed.current();
2114
+ }
2115
+ }),
2116
+ // The editor keeps its adapter for the session; the seed matters only when it first opens.
2117
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2118
+ [word.adapter, client, documentKey]
2119
+ );
2120
+ useEffect(() => {
2121
+ if (!roomId) return;
2122
+ return word.onRoomOpened?.(roomId) || void 0;
2123
+ }, [roomId, word]);
2124
+ const editor = useMailDraftEditor();
2125
+ const state = () => stateRef.current?.() ?? null;
2126
+ return {
2127
+ adapter,
2128
+ documentKey,
2129
+ stateRef,
2130
+ ready: !!roomId,
2131
+ save: (compose) => editor.save(roomRef.current, compose, state()),
2132
+ send: async (compose) => {
2133
+ const result = await editor.send(roomRef.current, compose, state());
2134
+ sessionRooms.delete(documentKey);
2135
+ return result;
2136
+ },
2137
+ body: (final) => editor.body(roomRef.current, final, state()),
2138
+ project,
2139
+ chooseProject: async (projectExtId) => {
2140
+ setProject(await editor.setProject(roomRef.current, projectExtId));
2141
+ }
2142
+ };
2143
+ }
2066
2144
  var MailBodyEditor = lazy(
2067
2145
  () => import('../MailBodyEditor-5PIQLYVY.js').catch(() => ({
2068
2146
  default: PlainMailBodyEditor
2069
2147
  }))
2070
2148
  );
2149
+ var NativeDocxEditor = lazy(
2150
+ () => import('../NativeDocxEditor-3VKJT43D.js').then((m) => ({ default: m.NativeDocxEditor }))
2151
+ );
2152
+ var NO_WORD = {
2153
+ adapter: {
2154
+ request: () => Promise.reject(new Error("No Word editor")),
2155
+ loadFonts: () => Promise.reject(new Error("No Word editor"))
2156
+ }
2157
+ };
2071
2158
  function MailComposerForm({
2072
2159
  account,
2073
2160
  initial,
@@ -2076,13 +2163,18 @@ function MailComposerForm({
2076
2163
  drafts,
2077
2164
  onClose,
2078
2165
  onOpenDraft,
2166
+ onReload,
2079
2167
  onChange,
2080
2168
  embedded = false,
2081
2169
  closeRequestRef,
2082
2170
  localDraftId: initialLocalDraftId,
2083
2171
  offline,
2084
2172
  documents,
2085
- notify: notify3
2173
+ notify: notify3,
2174
+ word,
2175
+ sessionKey,
2176
+ roomId,
2177
+ headerSlot
2086
2178
  }) {
2087
2179
  const { client } = useMailClient();
2088
2180
  const [to, setTo] = useState(initial.to.map((r) => r.email).join("; "));
@@ -2099,7 +2191,26 @@ function MailComposerForm({
2099
2191
  const [dirty, setDirty] = useState(false);
2100
2192
  const [attaching, setAttaching] = useState(false);
2101
2193
  const [saved, setSaved] = useState(false);
2194
+ const [replacedBy, setReplacedBy] = useState();
2102
2195
  const isOnline = useMailOnline();
2196
+ const [usesWord] = useState(
2197
+ () => !!word && !!client.openDraftEditor && account?.provider === "native" && isOnline && !initialLocalDraftId
2198
+ );
2199
+ const [bodyChanged, setBodyChanged] = useState(false);
2200
+ const [wordPending, setWordPending] = useState(false);
2201
+ const projects = useMailProjects({ enabled: usesWord });
2202
+ const wordBody = useWordBody({
2203
+ word: word ?? NO_WORD,
2204
+ client,
2205
+ initial,
2206
+ sessionKey,
2207
+ roomId,
2208
+ onBodyChange: () => {
2209
+ setBodyChanged(true);
2210
+ setSaved(false);
2211
+ setDirty(true);
2212
+ }
2213
+ });
2103
2214
  const [localDraftId, setLocalDraftId] = useState(initialLocalDraftId);
2104
2215
  const [savedLocally, setSavedLocally] = useState(false);
2105
2216
  const [secure, setSecure] = useState(initial.secure_delivery ?? false);
@@ -2143,27 +2254,46 @@ function MailComposerForm({
2143
2254
  const send = useSendMail(
2144
2255
  offline && { keep: queueInOutbox, isNetworkFailure: offline.isNetworkFailure }
2145
2256
  );
2146
- const saveDraft = () => save.mutate(
2147
- { accountId: account.id, compose: body() },
2148
- {
2149
- onSuccess: (result) => {
2150
- setSaved(true);
2151
- setDirty(false);
2152
- if ("onDevice" in result) {
2153
- setSavedLocally(true);
2154
- return;
2155
- }
2156
- setSavedLocally(false);
2157
- setDraftId(result.id);
2158
- if (localDraftId) {
2159
- void offline?.deleteDraft(localDraftId);
2160
- setLocalDraftId(void 0);
2161
- }
2162
- },
2163
- onError: (err) => setError(err.message)
2257
+ const onSaved = (result) => {
2258
+ setSaved(true);
2259
+ setDirty(false);
2260
+ if ("onDevice" in result) {
2261
+ setSavedLocally(true);
2262
+ return;
2164
2263
  }
2165
- );
2166
- const sendMessage = () => {
2264
+ setSavedLocally(false);
2265
+ setDraftId(result.id);
2266
+ setReplacedBy(void 0);
2267
+ if (localDraftId) {
2268
+ void offline?.deleteDraft(localDraftId);
2269
+ setLocalDraftId(void 0);
2270
+ }
2271
+ };
2272
+ const saveDraft = () => {
2273
+ if (usesWord) {
2274
+ if (!wordBody.ready) return setError("The editor is still opening");
2275
+ setWordPending(true);
2276
+ void wordBody.save(body()).then(
2277
+ (result) => {
2278
+ onSaved(result);
2279
+ setBodyChanged(false);
2280
+ },
2281
+ (err) => setError(err.message)
2282
+ ).finally(() => setWordPending(false));
2283
+ return;
2284
+ }
2285
+ save.mutate(
2286
+ { accountId: account.id, compose: body() },
2287
+ { onSuccess: onSaved, onError: (err) => setError(err.message) }
2288
+ );
2289
+ };
2290
+ useMailDraftReplaced(draftId, (newId) => {
2291
+ if (save.isPending || wordPending) return;
2292
+ setDraftId(newId);
2293
+ if (!dirty && onReload) onReload(newId);
2294
+ else setReplacedBy(newId);
2295
+ });
2296
+ const sendMessage = async () => {
2167
2297
  let value = attemptedPayload.current;
2168
2298
  if (value === null) {
2169
2299
  value = body();
@@ -2172,18 +2302,41 @@ function MailComposerForm({
2172
2302
  if (problem) return setError(problem);
2173
2303
  if (!(offline && !isOnline)) attemptedPayload.current = value;
2174
2304
  }
2305
+ if (usesWord) {
2306
+ if (!wordBody.ready) return setError("The editor is still opening");
2307
+ const refused = (err) => {
2308
+ setError(err.message);
2309
+ if (err.status === 409) attemptedPayload.current = null;
2310
+ };
2311
+ setWordPending(true);
2312
+ try {
2313
+ if (!secure) {
2314
+ await wordBody.send({ ...value, request_id: requestId.current });
2315
+ notify3("Email queued for delivery", "success");
2316
+ onClose();
2317
+ return;
2318
+ }
2319
+ value = { ...value, ...await wordBody.body(true) };
2320
+ attemptedPayload.current = value;
2321
+ } catch (err) {
2322
+ return refused(err);
2323
+ } finally {
2324
+ setWordPending(false);
2325
+ }
2326
+ }
2175
2327
  const sending = account;
2328
+ const message = value;
2176
2329
  send.mutate(
2177
2330
  {
2178
2331
  accountId: account.id,
2179
- compose: value,
2332
+ compose: message,
2180
2333
  requestId: requestId.current,
2181
2334
  // Secure delivery prepares its encrypted payload once.
2182
2335
  secure: secure ? async () => {
2183
2336
  if (sending?.provider !== "native" || !client.prepareSecureMail)
2184
2337
  throw new Error("Choose your native inbox to send securely");
2185
2338
  securePayload.current ??= await client.prepareSecureMail(
2186
- value,
2339
+ message,
2187
2340
  sending,
2188
2341
  requestId.current
2189
2342
  );
@@ -2207,7 +2360,7 @@ function MailComposerForm({
2207
2360
  }
2208
2361
  );
2209
2362
  };
2210
- const busy = attaching || save.isPending || send.isPending;
2363
+ const busy = attaching || save.isPending || send.isPending || wordPending;
2211
2364
  const locked = busy || attemptedPayload.current !== null;
2212
2365
  const addFiles = async (files) => {
2213
2366
  if (!files) return;
@@ -2246,7 +2399,7 @@ function MailComposerForm({
2246
2399
  };
2247
2400
  const close = () => {
2248
2401
  if (busy) return;
2249
- if (!saved && (dirty || !draftId) && (text || subject || to || cc || bcc || attachments.length) && !window.confirm("Close this message without saving your latest changes?"))
2402
+ if (!saved && (dirty || !draftId) && (text || subject || to || cc || bcc || attachments.length || bodyChanged) && !window.confirm("Close this message without saving your latest changes?"))
2250
2403
  return;
2251
2404
  onClose();
2252
2405
  };
@@ -2256,6 +2409,30 @@ function MailComposerForm({
2256
2409
  useEffect(() => {
2257
2410
  onChange?.(body());
2258
2411
  }, [to, cc, bcc, subject, text, html, attachments, draftId, secure]);
2412
+ const billTo = wordBody.project && /* @__PURE__ */ jsxs(
2413
+ "label",
2414
+ {
2415
+ className: "flex items-center gap-1 text-xs text-muted-foreground",
2416
+ title: "ARBI's work on this email is billed to this project",
2417
+ children: [
2418
+ "Billed to",
2419
+ /* @__PURE__ */ jsx(
2420
+ "select",
2421
+ {
2422
+ "aria-label": "Bill to",
2423
+ className: "max-w-[12rem] truncate rounded bg-transparent py-0.5 text-foreground hover:bg-muted/60 focus:bg-muted/60 focus:outline-none",
2424
+ value: wordBody.project.external_id,
2425
+ disabled: busy,
2426
+ onChange: (e) => {
2427
+ const chosen = projects.data?.find((p) => p.external_id === e.target.value);
2428
+ void wordBody.chooseProject(chosen && !chosen.personal ? chosen.external_id : null).catch((err) => setError(err.message));
2429
+ },
2430
+ children: (projects.data ?? [wordBody.project]).map((p) => /* @__PURE__ */ jsx("option", { value: p.external_id, children: p.personal ? `${p.name} (personal)` : p.name }, p.external_id))
2431
+ }
2432
+ )
2433
+ ]
2434
+ }
2435
+ );
2259
2436
  const form = /* @__PURE__ */ jsxs(
2260
2437
  "form",
2261
2438
  {
@@ -2271,7 +2448,7 @@ function MailComposerForm({
2271
2448
  onSubmit: (e) => {
2272
2449
  e.preventDefault();
2273
2450
  setError("");
2274
- sendMessage();
2451
+ void sendMessage();
2275
2452
  },
2276
2453
  className: embedded ? "flex h-full min-h-0 w-full flex-col bg-background" : "flex max-h-[95dvh] w-full max-w-3xl flex-col rounded-xl border bg-background shadow-2xl",
2277
2454
  children: [
@@ -2289,7 +2466,7 @@ function MailComposerForm({
2289
2466
  }
2290
2467
  )
2291
2468
  ] }),
2292
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-auto px-5 py-3", children: [
2469
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto px-5 py-3", children: [
2293
2470
  sameSubjectDraft && /* @__PURE__ */ jsxs(
2294
2471
  "div",
2295
2472
  {
@@ -2310,10 +2487,36 @@ function MailComposerForm({
2310
2487
  ]
2311
2488
  }
2312
2489
  ),
2490
+ replacedBy && /* @__PURE__ */ jsxs(
2491
+ "div",
2492
+ {
2493
+ role: "status",
2494
+ className: "mb-3 flex items-center justify-between gap-3 rounded-md bg-muted/40 px-3 py-2 text-xs text-muted-foreground",
2495
+ children: [
2496
+ "This draft was changed elsewhere (by ARBI or in another tab). Saving keeps your version.",
2497
+ onReload && /* @__PURE__ */ jsx(
2498
+ Button,
2499
+ {
2500
+ type: "button",
2501
+ variant: "ghost",
2502
+ className: "h-7 shrink-0 text-xs",
2503
+ disabled: busy,
2504
+ onClick: () => {
2505
+ if (window.confirm("Discard your unsaved changes and load the changed draft?"))
2506
+ onReload(replacedBy);
2507
+ },
2508
+ children: "Load changes"
2509
+ }
2510
+ )
2511
+ ]
2512
+ }
2513
+ ),
2313
2514
  /* @__PURE__ */ jsxs("p", { className: "mb-2 flex items-center gap-3 border-b border-border/60 pb-3 text-xs text-muted-foreground", children: [
2314
2515
  /* @__PURE__ */ jsx("span", { className: "w-14 shrink-0", children: "From" }),
2315
2516
  /* @__PURE__ */ jsx("span", { className: "rounded bg-muted/50 px-2 py-1 text-foreground", children: account?.email ?? "Connect a mailbox to send" })
2316
2517
  ] }),
2518
+ usesWord && wordBody.project && !headerSlot && /* @__PURE__ */ jsx("p", { className: "mb-2", children: billTo }),
2519
+ usesWord && wordBody.project && headerSlot && createPortal(billTo, headerSlot),
2317
2520
  [
2318
2521
  ["To", to, setTo],
2319
2522
  ["Cc", cc, setCc],
@@ -2386,7 +2589,24 @@ function MailComposerForm({
2386
2589
  }
2387
2590
  )
2388
2591
  ] }),
2389
- /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("p", { className: "py-4 text-sm text-muted-foreground", children: "Loading editor\u2026" }), children: /* @__PURE__ */ jsx(
2592
+ /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("p", { className: "py-4 text-sm text-muted-foreground", children: "Loading editor\u2026" }), children: usesWord ? /* @__PURE__ */ jsx(
2593
+ "div",
2594
+ {
2595
+ className: "-mx-5 -mb-3 flex min-h-[22rem] flex-1 flex-col border-t border-border/60",
2596
+ "data-testid": "mail-word-body",
2597
+ onSubmit: (event) => event.stopPropagation(),
2598
+ children: /* @__PURE__ */ jsx(
2599
+ NativeDocxEditor,
2600
+ {
2601
+ compact: true,
2602
+ adapter: wordBody.adapter,
2603
+ documentId: wordBody.documentKey,
2604
+ title: "Email",
2605
+ stateRef: wordBody.stateRef
2606
+ }
2607
+ )
2608
+ }
2609
+ ) : /* @__PURE__ */ jsx(
2390
2610
  MailBodyEditor,
2391
2611
  {
2392
2612
  text,
@@ -2553,6 +2773,7 @@ function ConnectedMailComposer({
2553
2773
  drafts: draftsProp,
2554
2774
  notify: notify3 = defaultMailNotify,
2555
2775
  offline,
2776
+ replyTo,
2556
2777
  ...rest
2557
2778
  }) {
2558
2779
  const { client } = useMailClient();
@@ -2567,9 +2788,11 @@ function ConnectedMailComposer({
2567
2788
  const draftsList = useMailMessages(account, draftsFolderId, "", null, {
2568
2789
  enabled: !draftsProp && ready
2569
2790
  });
2791
+ const [reloaded, setReloaded] = useState();
2792
+ const openId = reloaded ?? draftId;
2570
2793
  const draft = useMailDraftCompose(
2571
2794
  account,
2572
- draftId,
2795
+ openId,
2573
2796
  async (from, id) => {
2574
2797
  const message = await client.getMessage(from, id);
2575
2798
  return draftMailCompose(
@@ -2580,14 +2803,20 @@ function ConnectedMailComposer({
2580
2803
  })
2581
2804
  );
2582
2805
  },
2583
- { enabled: !initial && ready }
2584
- );
2585
- const start = useMemo(
2586
- () => initial ?? (draftId ? draft.data : emptyMailCompose()),
2587
- [initial, draftId, draft.data]
2806
+ { enabled: (!initial || !!reloaded) && ready }
2588
2807
  );
2808
+ const original = useMailMessage(account, replyTo, { enabled: !!replyTo && ready });
2809
+ const start = useMemo(() => {
2810
+ if (replyTo)
2811
+ return original.data && account ? {
2812
+ ...replyMailCompose(original.data, { account, folderRole: "inbox" }),
2813
+ text: "",
2814
+ html: ""
2815
+ } : void 0;
2816
+ return reloaded ? draft.data : initial ?? (openId ? draft.data : emptyMailCompose());
2817
+ }, [replyTo, original.data, account, reloaded, initial, openId, draft.data]);
2589
2818
  if (!start)
2590
- return draft.error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "p-4 text-sm text-destructive", children: draft.error.message }) : /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground", children: "Loading draft\u2026" });
2819
+ return draft.error ?? original.error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "p-4 text-sm text-destructive", children: (draft.error ?? original.error)?.message }) : /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground", children: "Loading draft\u2026" });
2591
2820
  return /* @__PURE__ */ jsx(
2592
2821
  MailComposerForm,
2593
2822
  {
@@ -2598,9 +2827,10 @@ function ConnectedMailComposer({
2598
2827
  drafts: draftsProp ?? draftsList.data?.messages ?? [],
2599
2828
  notify: notify3,
2600
2829
  offline,
2830
+ onReload: setReloaded,
2601
2831
  ...rest
2602
2832
  },
2603
- `${account?.id}-${draftId ?? ""}`
2833
+ `${account?.id}-${openId ?? ""}`
2604
2834
  );
2605
2835
  }
2606
2836
  function ConnectedMailSetup({
@@ -2780,6 +3010,8 @@ function ConnectedMailbox({
2780
3010
  offline,
2781
3011
  documents,
2782
3012
  saveToWorkspace,
3013
+ word,
3014
+ onReplyWithArbi,
2783
3015
  renderInvite,
2784
3016
  renderPanel = SidePanel,
2785
3017
  onViewerOpenChange,
@@ -2809,6 +3041,7 @@ function ConnectedMailbox({
2809
3041
  const [compose, setCompose] = useState(null);
2810
3042
  const [composeLocalDraftId, setComposeLocalDraftId] = useState();
2811
3043
  const [composeSession, setComposeSession] = useState(0);
3044
+ const [mailboxKey] = useState(() => crypto.randomUUID());
2812
3045
  const openCompose = (value, localDraftId) => {
2813
3046
  setComposeSession((n) => n + 1);
2814
3047
  setComposeLocalDraftId(localDraftId);
@@ -3231,6 +3464,8 @@ function ConnectedMailbox({
3231
3464
  offline,
3232
3465
  documents,
3233
3466
  notify: notify3,
3467
+ word,
3468
+ sessionKey: `${mailboxKey}-${composeSession}`,
3234
3469
  onChange: setCompose,
3235
3470
  closeRequestRef: composeClose,
3236
3471
  onClose: () => {
@@ -3265,6 +3500,7 @@ function ConnectedMailbox({
3265
3500
  onBack: () => setSelected(null),
3266
3501
  onEditDraft: () => void editDraft(),
3267
3502
  onReply: (forward, all) => void reply(forward, all),
3503
+ onReplyWithArbi: onReplyWithArbi && account.provider === "native" ? () => onReplyWithArbi(message, account) : void 0,
3268
3504
  onUpdate: (body) => void update(body),
3269
3505
  onDownloadAttachment: (a) => void downloadAttachment(a),
3270
3506
  onDownloadOriginal: () => void download(
@@ -3320,6 +3556,85 @@ function ConnectedMailbox({
3320
3556
  )
3321
3557
  ] });
3322
3558
  }
3559
+ function ConnectedMailMessage({
3560
+ messageId,
3561
+ mailbox = "native",
3562
+ folderRole = "inbox",
3563
+ onClose,
3564
+ onCompose,
3565
+ onReplyWithArbi
3566
+ }) {
3567
+ const { client } = useMailClient();
3568
+ const isOnline = useMailOnline();
3569
+ const cache = useMailCache();
3570
+ const accounts = useMailAccounts();
3571
+ const account = resolveMailbox(accounts.data, mailbox);
3572
+ const unlock = useMailUnlock(account);
3573
+ const detail = useMailMessage(account, messageId, { enabled: !!unlock.data || !isOnline });
3574
+ const [error, setError] = useState("");
3575
+ const message = detail.data;
3576
+ if (!account || !message)
3577
+ return /* @__PURE__ */ jsx("p", { role: detail.error ? "alert" : "status", className: "p-4 text-sm text-muted-foreground", children: detail.error?.message ?? "Loading message\u2026" });
3578
+ const update = async (body) => {
3579
+ try {
3580
+ await mailJson(
3581
+ client.fetch,
3582
+ `${accountPath(account.id)}/messages/${encodeURIComponent(message.id)}`,
3583
+ {
3584
+ method: "PATCH",
3585
+ body: JSON.stringify(body)
3586
+ }
3587
+ );
3588
+ await cache.refreshAll();
3589
+ if (body.folder_id || body.action) onClose();
3590
+ } catch (e) {
3591
+ setError(e instanceof Error ? e.message : "Could not update message");
3592
+ }
3593
+ };
3594
+ const download = async (path, name) => {
3595
+ try {
3596
+ saveFile(await (await client.fetch(path)).blob(), name);
3597
+ } catch (e) {
3598
+ setError(e instanceof Error ? e.message : "Download failed");
3599
+ }
3600
+ };
3601
+ const reply = async (forward = false, all = false) => {
3602
+ try {
3603
+ const attachments = forward ? await hydrateMailAttachments(client, account, message.id, message.attachments, {
3604
+ isOnline
3605
+ }) : [];
3606
+ onCompose(replyMailCompose(message, { account, folderRole, forward, all, attachments }));
3607
+ } catch (e) {
3608
+ setError(e instanceof Error ? e.message : "Could not prepare forwarded attachments");
3609
+ }
3610
+ };
3611
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [
3612
+ error && /* @__PURE__ */ jsx("p", { role: "alert", className: "px-4 pt-3 text-sm text-destructive", children: error }),
3613
+ /* @__PURE__ */ jsx(
3614
+ MailMessageView,
3615
+ {
3616
+ account,
3617
+ message,
3618
+ folderRole,
3619
+ folders: void 0,
3620
+ isOnline,
3621
+ loading: detail.isFetching,
3622
+ onBack: onClose,
3623
+ onEditDraft: () => void hydrateMailAttachments(client, account, message.id, message.attachments, {
3624
+ isOnline
3625
+ }).then((attachments) => onCompose(draftMailCompose(message, attachments))).catch((e) => setError(e.message)),
3626
+ onReply: (forward, all) => void reply(forward, all),
3627
+ onReplyWithArbi: onReplyWithArbi && account.provider === "native" ? () => onReplyWithArbi(message, account) : void 0,
3628
+ onUpdate: (body) => void update(body),
3629
+ onDownloadAttachment: (a) => void download(attachmentPath(account.id, message.id, a.id), a.name),
3630
+ onDownloadOriginal: () => void download(
3631
+ `${accountPath(account.id)}/messages/${message.id}/original`,
3632
+ "original.eml"
3633
+ )
3634
+ }
3635
+ )
3636
+ ] });
3637
+ }
3323
3638
  function ActiveChat({ agentId, workspaceId }) {
3324
3639
  const chat = useArbiChat({ agentId, workspaceId });
3325
3640
  return /* @__PURE__ */ jsx(
@@ -3383,6 +3698,6 @@ function ConnectedAgentChatPanel({
3383
3698
  ] });
3384
3699
  }
3385
3700
 
3386
- export { ArbiAppProvider, ConnectedAgentChatPanel, ConnectedDangerZone, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailSetup, ConnectedMailbox, ConnectedNotificationPreferences, ConnectedProfileCard, ConnectedRequireVerified, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSkillUpload, ConnectedTagTable, ConnectedTaskAccessRequest, ConnectedThreadFollow, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceNotificationMenu, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, StreamedArtifact, WorkflowRunThread, defaultMailNotify, defaultUnmetPasswordRequirement, resolveMailbox, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications };
3701
+ export { ArbiAppProvider, ConnectedAgentChatPanel, ConnectedDangerZone, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailMessage, ConnectedMailSetup, ConnectedMailbox, ConnectedNotificationPreferences, ConnectedProfileCard, ConnectedRequireVerified, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSkillUpload, ConnectedTagTable, ConnectedTaskAccessRequest, ConnectedThreadFollow, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceNotificationMenu, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, StreamedArtifact, WorkflowRunThread, defaultMailNotify, defaultUnmetPasswordRequirement, resolveMailbox, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications };
3387
3702
  //# sourceMappingURL=index.js.map
3388
3703
  //# sourceMappingURL=index.js.map