@arbidocs/blocks 0.4.4 → 0.4.6
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/README.md +71 -6
- package/dist/agents/index.cjs +163 -162
- package/dist/agents/index.js +6 -5
- package/dist/artifacts/ArtifactCompareBar.d.ts +33 -0
- package/dist/artifacts/ArtifactEditor.d.ts +64 -0
- package/dist/artifacts/ArtifactEditorBody.d.ts +34 -0
- package/dist/artifacts/ArtifactRedlineView.d.ts +57 -0
- package/dist/artifacts/ArtifactToolbar.d.ts +53 -0
- package/dist/artifacts/ArtifactVersionBar.d.ts +42 -0
- package/dist/{files → artifacts}/RedlineParts.d.ts +4 -0
- package/dist/artifacts/editor/ArtifactMarkdownEditor.d.ts +39 -0
- package/dist/artifacts/editor/index.cjs +89 -0
- package/dist/artifacts/editor/index.cjs.map +1 -0
- package/dist/artifacts/editor/index.d.ts +1 -0
- package/dist/artifacts/editor/index.js +86 -0
- package/dist/artifacts/editor/index.js.map +1 -0
- package/dist/artifacts/index.cjs +807 -0
- package/dist/artifacts/index.cjs.map +1 -0
- package/dist/artifacts/index.d.ts +8 -0
- package/dist/artifacts/index.js +787 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/chat/ChatMessageContent.d.ts +16 -0
- package/dist/chat/ChatPage.d.ts +11 -8
- package/dist/chat/ChatThreads.d.ts +3 -1
- package/dist/chat/CitationMark.d.ts +18 -0
- package/dist/chat/chat-page-types.d.ts +75 -1
- package/dist/chat/index.cjs +125 -116
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.js +4 -3
- package/dist/{chunk-XKKGGUU2.cjs → chunk-23XL6SVZ.cjs} +39 -19
- package/dist/chunk-23XL6SVZ.cjs.map +1 -0
- package/dist/{chunk-ULWRNGGE.cjs → chunk-2ZTONLT5.cjs} +4 -25
- package/dist/chunk-2ZTONLT5.cjs.map +1 -0
- package/dist/{chunk-O637DUKS.cjs → chunk-56BUD74K.cjs} +28 -28
- package/dist/{chunk-O637DUKS.cjs.map → chunk-56BUD74K.cjs.map} +1 -1
- package/dist/{chunk-ATXPAD6S.cjs → chunk-5SLLMK5K.cjs} +420 -114
- package/dist/chunk-5SLLMK5K.cjs.map +1 -0
- package/dist/{chunk-SS23F63U.cjs → chunk-5ZBP7TCD.cjs} +55 -3
- package/dist/chunk-5ZBP7TCD.cjs.map +1 -0
- package/dist/{chunk-DGVKBOY5.cjs → chunk-67HGXV4I.cjs} +6 -6
- package/dist/{chunk-DGVKBOY5.cjs.map → chunk-67HGXV4I.cjs.map} +1 -1
- package/dist/{chunk-RCP6THBS.js → chunk-7OCFHEE6.js} +3 -3
- package/dist/{chunk-RCP6THBS.js.map → chunk-7OCFHEE6.js.map} +1 -1
- package/dist/{chunk-TX2ZICS7.js → chunk-CPMNAJLF.js} +4 -24
- package/dist/chunk-CPMNAJLF.js.map +1 -0
- package/dist/{chunk-BBEMPS6C.js → chunk-CVFBSUXL.js} +5 -5
- package/dist/{chunk-BBEMPS6C.js.map → chunk-CVFBSUXL.js.map} +1 -1
- package/dist/chunk-DCDVWPMB.cjs +29 -0
- package/dist/chunk-DCDVWPMB.cjs.map +1 -0
- package/dist/{chunk-TI4UTYRL.js → chunk-EY2RJW27.js} +258 -10
- package/dist/chunk-EY2RJW27.js.map +1 -0
- package/dist/{chunk-7YADKWZT.js → chunk-G2E3O3HI.js} +3 -3
- package/dist/{chunk-7YADKWZT.js.map → chunk-G2E3O3HI.js.map} +1 -1
- package/dist/{chunk-JPO3F4J7.js → chunk-G2T3KTZ3.js} +418 -114
- package/dist/chunk-G2T3KTZ3.js.map +1 -0
- package/dist/{chunk-2NNTQLBY.js → chunk-IAKUTBPX.js} +4 -4
- package/dist/{chunk-2NNTQLBY.js.map → chunk-IAKUTBPX.js.map} +1 -1
- package/dist/{chunk-IZHWP6WQ.cjs → chunk-JG4TFS3Z.cjs} +6 -6
- package/dist/{chunk-IZHWP6WQ.cjs.map → chunk-JG4TFS3Z.cjs.map} +1 -1
- package/dist/chunk-JLOYWXRB.js +1156 -0
- package/dist/chunk-JLOYWXRB.js.map +1 -0
- package/dist/{chunk-WJZBJUPX.js → chunk-KXMDOTGW.js} +56 -4
- package/dist/chunk-KXMDOTGW.js.map +1 -0
- package/dist/{chunk-2VBJ763Y.js → chunk-KZJAG7T3.js} +37 -7
- package/dist/chunk-KZJAG7T3.js.map +1 -0
- package/dist/{chunk-RIL7G7AS.cjs → chunk-MGUHR3JI.cjs} +4 -4
- package/dist/{chunk-RIL7G7AS.cjs.map → chunk-MGUHR3JI.cjs.map} +1 -1
- package/dist/{chunk-DN3NFNIA.cjs → chunk-QMDLPO3M.cjs} +266 -10
- package/dist/chunk-QMDLPO3M.cjs.map +1 -0
- package/dist/chunk-TJMKFY5P.js +27 -0
- package/dist/chunk-TJMKFY5P.js.map +1 -0
- package/dist/{chunk-B673CCW5.js → chunk-UHVNYKV2.js} +38 -18
- package/dist/chunk-UHVNYKV2.js.map +1 -0
- package/dist/{chunk-6CHMNKXC.cjs → chunk-UTYQPVIW.cjs} +11 -11
- package/dist/chunk-UTYQPVIW.cjs.map +1 -0
- package/dist/{chunk-KHX2BX3S.cjs → chunk-VGOFFCMT.cjs} +37 -5
- package/dist/chunk-VGOFFCMT.cjs.map +1 -0
- package/dist/{chunk-67MKU5U7.js → chunk-YMHGF6R5.js} +8 -8
- package/dist/chunk-YMHGF6R5.js.map +1 -0
- package/dist/chunk-YVIOCITQ.cjs +1186 -0
- package/dist/chunk-YVIOCITQ.cjs.map +1 -0
- package/dist/collaboration/index.cjs +38 -37
- package/dist/collaboration/index.js +3 -2
- package/dist/connect/index.cjs +358 -19
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.ts +1 -0
- package/dist/connect/index.js +341 -9
- package/dist/connect/index.js.map +1 -1
- package/dist/connection/ConnectedSettings.d.ts +64 -0
- package/dist/facts/index.cjs +1 -1
- package/dist/facts/index.js +1 -1
- package/dist/files/ViewerChrome.d.ts +0 -45
- package/dist/files/ViewerParts.d.ts +0 -19
- package/dist/files/index.cjs +15 -446
- package/dist/files/index.cjs.map +1 -1
- package/dist/files/index.d.ts +2 -4
- package/dist/files/index.js +9 -428
- package/dist/files/index.js.map +1 -1
- package/dist/home/index.cjs +1 -1
- package/dist/home/index.js +1 -1
- package/dist/index.cjs +634 -585
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -18
- package/dist/index.js.map +1 -1
- package/dist/lib/bottom-inset.d.ts +16 -0
- package/dist/lib/useElementSize.d.ts +13 -0
- package/dist/lib/useFillHeight.d.ts +10 -0
- package/dist/settings/account-views.d.ts +55 -0
- package/dist/settings/index.cjs +178 -1147
- package/dist/settings/index.cjs.map +1 -1
- package/dist/settings/index.d.ts +1 -0
- package/dist/settings/index.js +9 -1072
- package/dist/settings/index.js.map +1 -1
- package/dist/shell/InstallPrompt.d.ts +28 -0
- package/dist/shell/MobileBars.d.ts +1 -1
- package/dist/shell/index.cjs +92 -52
- package/dist/shell/index.d.ts +4 -0
- package/dist/shell/index.js +4 -4
- package/dist/shell/install-instructions.d.ts +19 -0
- package/dist/shell/types.d.ts +6 -0
- package/dist/shell/use-install-prompt.d.ts +49 -0
- package/dist/site/index.cjs +67 -67
- package/dist/site/index.js +3 -3
- package/dist/studio/index.cjs +79 -78
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.js +9 -8
- package/dist/studio/index.js.map +1 -1
- package/dist/theme/ThemeEditor.d.ts +4 -2
- package/dist/workspaces/index.cjs +4 -3
- package/dist/workspaces/index.cjs.map +1 -1
- package/dist/workspaces/index.js +3 -2
- package/dist/workspaces/index.js.map +1 -1
- package/package.json +30 -5
- package/src/blocks.css +30 -0
- package/dist/chunk-2VBJ763Y.js.map +0 -1
- package/dist/chunk-67MKU5U7.js.map +0 -1
- package/dist/chunk-6CHMNKXC.cjs.map +0 -1
- package/dist/chunk-ATXPAD6S.cjs.map +0 -1
- package/dist/chunk-B673CCW5.js.map +0 -1
- package/dist/chunk-DN3NFNIA.cjs.map +0 -1
- package/dist/chunk-JPO3F4J7.js.map +0 -1
- package/dist/chunk-KHX2BX3S.cjs.map +0 -1
- package/dist/chunk-SS23F63U.cjs.map +0 -1
- package/dist/chunk-TI4UTYRL.js.map +0 -1
- package/dist/chunk-TX2ZICS7.js.map +0 -1
- package/dist/chunk-ULWRNGGE.cjs.map +0 -1
- package/dist/chunk-WJZBJUPX.js.map +0 -1
- package/dist/chunk-XKKGGUU2.cjs.map +0 -1
- /package/dist/{files → artifacts}/VersionTimeline.d.ts +0 -0
package/dist/connect/index.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
2
|
+
import { SettingsSection, ProfileCard, AvatarPicker, SettingsCard, SettingsActionRow, PasswordChangeDialog, EMAIL_NOTIFICATION_KEYS, EMAIL_NOTIFICATION_CATEGORIES, NotificationCategoryList, toNotificationCategories, toActiveSessionViews, SettingsRow, ActiveSessionsDialog, LogoutDialog, toLogoutSessionViews, SettingsDangerZone, DeleteAccountDialog, toDeleteBlockerViews } from '../chunk-JLOYWXRB.js';
|
|
3
|
+
import { ConnectionProvider, useConnection } from '../chunk-UHVNYKV2.js';
|
|
4
|
+
export { ConnectedAssistantDock, ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, redlineStats, textToArtifact, toRedlineMarkdown, useConnectedAiTask, useConnectedChat, useConnection, useSemanticSearch, useWorkspaceDocs } from '../chunk-UHVNYKV2.js';
|
|
5
|
+
import '../chunk-VILAPTCB.js';
|
|
6
|
+
import '../chunk-G2T3KTZ3.js';
|
|
7
|
+
import '../chunk-KZJAG7T3.js';
|
|
8
|
+
import '../chunk-NPLYHR2E.js';
|
|
9
|
+
import { initials } from '../chunk-OXF2IFXG.js';
|
|
10
|
+
import '../chunk-TJMKFY5P.js';
|
|
7
11
|
import '../chunk-7F6CBP3Z.js';
|
|
8
|
-
import { useState } from 'react';
|
|
12
|
+
import { useState, useMemo } from 'react';
|
|
9
13
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
|
-
import { ArbiProvider, useWorkspaces, useShellWorkspaces, ArbiWebSocketProvider } from '@arbidocs/react';
|
|
11
|
-
import { jsx } from 'react/jsx-runtime';
|
|
14
|
+
import { ArbiProvider, useWorkspaces, useShellWorkspaces, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useLogoutAllSessions, useDeleteAccount, useDeleteAccountFlow, ArbiWebSocketProvider } from '@arbidocs/react';
|
|
15
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
16
|
+
import { UserCircle, KeyRound, Radio } from 'lucide-react';
|
|
17
|
+
import { Button } from '@arbidocs/react/ui';
|
|
12
18
|
|
|
13
19
|
function ConnectionBoundWebSocket({ children }) {
|
|
14
20
|
const { status } = useConnection();
|
|
@@ -38,7 +44,333 @@ function useConnectedShellWorkspaces(options = {}) {
|
|
|
38
44
|
select: (workspace) => setWorkspace(workspace.id)
|
|
39
45
|
});
|
|
40
46
|
}
|
|
47
|
+
function errorText(error, fallback) {
|
|
48
|
+
const body = error?.apiError;
|
|
49
|
+
return typeof body?.detail === "string" ? body.detail : fallback;
|
|
50
|
+
}
|
|
51
|
+
var AVATAR_PROBLEMS = {
|
|
52
|
+
"not-image": "Please choose an image file",
|
|
53
|
+
"too-large": "Image must be 5 MB or smaller",
|
|
54
|
+
unreadable: "Couldn't read that image \u2014 try a different file"
|
|
55
|
+
};
|
|
56
|
+
function ConnectedProfileCard({ notify, title = "Profile" }) {
|
|
57
|
+
const { data: profile } = useProfile();
|
|
58
|
+
const updateProfile = useUpdateProfile();
|
|
59
|
+
const requestEmailChange = useRequestEmailChange();
|
|
60
|
+
const confirmEmailChange = useConfirmEmailChange();
|
|
61
|
+
const { user } = useConnection();
|
|
62
|
+
const name = useNameEditor({
|
|
63
|
+
givenName: profile?.givenName,
|
|
64
|
+
familyName: profile?.familyName,
|
|
65
|
+
save: async (next) => {
|
|
66
|
+
try {
|
|
67
|
+
await updateProfile.mutateAsync(next);
|
|
68
|
+
notify?.("Name updated", "success");
|
|
69
|
+
} catch (e) {
|
|
70
|
+
notify?.("Failed to update name", "error");
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const avatar = useAvatarCrop({
|
|
76
|
+
upload: async (picture) => {
|
|
77
|
+
try {
|
|
78
|
+
await updateProfile.mutateAsync({ picture });
|
|
79
|
+
notify?.("Profile picture updated", "success");
|
|
80
|
+
} catch (e) {
|
|
81
|
+
notify?.("Failed to update picture", "error");
|
|
82
|
+
throw e;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
onProblem: (problem) => notify?.(AVATAR_PROBLEMS[problem], "error")
|
|
86
|
+
});
|
|
87
|
+
const email = useEmailChange({
|
|
88
|
+
requestCode: async (address) => {
|
|
89
|
+
try {
|
|
90
|
+
await requestEmailChange.mutateAsync(address);
|
|
91
|
+
notify?.("Verification code sent to your new email", "success");
|
|
92
|
+
} catch (e) {
|
|
93
|
+
notify?.(errorText(e, "Failed to send verification code"), "error");
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
confirm: async (args) => {
|
|
98
|
+
try {
|
|
99
|
+
await confirmEmailChange.mutateAsync(args);
|
|
100
|
+
notify?.("Email address updated", "success");
|
|
101
|
+
} catch (e) {
|
|
102
|
+
notify?.(errorText(e, "Invalid or expired verification code"), "error");
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
const display = profile?.fullName || user?.name || "Unnamed";
|
|
108
|
+
return /* @__PURE__ */ jsx(SettingsSection, { icon: /* @__PURE__ */ jsx(UserCircle, {}), title, children: /* @__PURE__ */ jsx(
|
|
109
|
+
ProfileCard,
|
|
110
|
+
{
|
|
111
|
+
avatar: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
112
|
+
/* @__PURE__ */ jsx(
|
|
113
|
+
AvatarPicker,
|
|
114
|
+
{
|
|
115
|
+
pictureUrl: profile?.picture ?? user?.picture,
|
|
116
|
+
initials: initials(display),
|
|
117
|
+
fallbackClassName: "bg-primary text-primary-foreground",
|
|
118
|
+
isUploading: avatar.isUploading,
|
|
119
|
+
onFileChosen: avatar.onFileChosen
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
/* @__PURE__ */ jsx(
|
|
123
|
+
ImageCropModal,
|
|
124
|
+
{
|
|
125
|
+
open: avatar.pendingImage !== null,
|
|
126
|
+
image: avatar.pendingImage,
|
|
127
|
+
title: "Crop your photo",
|
|
128
|
+
aspect: 1,
|
|
129
|
+
cropShape: "round",
|
|
130
|
+
output: { maxSize: 128, mimeType: "image/jpeg", quality: 0.85 },
|
|
131
|
+
onComplete: avatar.onCropComplete,
|
|
132
|
+
onCancel: avatar.onCropCancel
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
] }),
|
|
136
|
+
name: { ...name, display, isSaving: name.isSaving },
|
|
137
|
+
email: profile?.email ?? user?.email ?? "",
|
|
138
|
+
isSso: profile?.isSso ?? false,
|
|
139
|
+
onStartEmailChange: email.onStart,
|
|
140
|
+
emailChange: email.isOpen ? email : void 0
|
|
141
|
+
}
|
|
142
|
+
) });
|
|
143
|
+
}
|
|
144
|
+
function defaultUnmetPasswordRequirement(password) {
|
|
145
|
+
if (password.length < 8) return "Minimum 8 characters";
|
|
146
|
+
if (!/[A-Z!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
147
|
+
return "At least one uppercase letter or special character";
|
|
148
|
+
}
|
|
149
|
+
if (!/[0-9]/.test(password)) return "At least one number";
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function ConnectedSecuritySection({
|
|
153
|
+
notify,
|
|
154
|
+
getUnmetRequirement = defaultUnmetPasswordRequirement,
|
|
155
|
+
renderRecoveryKey,
|
|
156
|
+
children
|
|
157
|
+
}) {
|
|
158
|
+
const [open, setOpen] = useState(false);
|
|
159
|
+
const change = usePasswordChange();
|
|
160
|
+
const { data: workspaces } = useWorkspaces({ enabled: open });
|
|
161
|
+
const workspaceCount = (workspaces ?? []).filter((w) => w.is_member).length;
|
|
162
|
+
const form = usePasswordChangeForm({
|
|
163
|
+
getUnmetRequirement,
|
|
164
|
+
submit: async (vars) => {
|
|
165
|
+
const result = await change.mutateAsync(vars);
|
|
166
|
+
notify?.("Password changed successfully", "success");
|
|
167
|
+
return result;
|
|
168
|
+
},
|
|
169
|
+
onClose: () => setOpen(false)
|
|
170
|
+
});
|
|
171
|
+
return /* @__PURE__ */ jsxs(SettingsSection, { icon: /* @__PURE__ */ jsx(KeyRound, {}), title: "Security", children: [
|
|
172
|
+
/* @__PURE__ */ jsxs(SettingsCard, { padding: "sm", children: [
|
|
173
|
+
/* @__PURE__ */ jsx(
|
|
174
|
+
SettingsActionRow,
|
|
175
|
+
{
|
|
176
|
+
title: "Change password",
|
|
177
|
+
description: "Used to sign in and to encrypt your data. Changing it re-encrypts everything in your account.",
|
|
178
|
+
onClick: () => setOpen(true),
|
|
179
|
+
testId: "change-password-button"
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
children
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ jsx(
|
|
185
|
+
PasswordChangeDialog,
|
|
186
|
+
{
|
|
187
|
+
open,
|
|
188
|
+
onClose: form.onCancel,
|
|
189
|
+
recoveryMode: false,
|
|
190
|
+
currentPassword: form.currentPassword,
|
|
191
|
+
onCurrentPasswordChange: form.onCurrentPasswordChange,
|
|
192
|
+
newPassword: form.newPassword,
|
|
193
|
+
onNewPasswordChange: form.onNewPasswordChange,
|
|
194
|
+
confirmPassword: form.confirmPassword,
|
|
195
|
+
onConfirmPasswordChange: form.onConfirmPasswordChange,
|
|
196
|
+
getUnmetRequirement,
|
|
197
|
+
workspaceCount,
|
|
198
|
+
error: form.error,
|
|
199
|
+
isProcessing: form.isProcessing,
|
|
200
|
+
isSubmitDisabled: form.isSubmitDisabled,
|
|
201
|
+
onSubmit: form.onSubmit,
|
|
202
|
+
success: form.result ? renderRecoveryKey?.(form.result) ?? /* @__PURE__ */ jsx(Fragment, {}) : null,
|
|
203
|
+
onDone: form.onDone
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
] });
|
|
207
|
+
}
|
|
208
|
+
function ConnectedNotificationPreferences({
|
|
209
|
+
categories = EMAIL_NOTIFICATION_CATEGORIES,
|
|
210
|
+
title = "Notifications"
|
|
211
|
+
}) {
|
|
212
|
+
const keys = useMemo(
|
|
213
|
+
() => categories === EMAIL_NOTIFICATION_CATEGORIES ? EMAIL_NOTIFICATION_KEYS : categories.map((c) => c.key),
|
|
214
|
+
[categories]
|
|
215
|
+
);
|
|
216
|
+
const prefs = useNotificationPreferences(keys);
|
|
217
|
+
return /* @__PURE__ */ jsx(SettingsSection, { title, children: /* @__PURE__ */ jsx(SettingsCard, { className: "space-y-0", children: /* @__PURE__ */ jsx(
|
|
218
|
+
NotificationCategoryList,
|
|
219
|
+
{
|
|
220
|
+
masterId: "emailNotifications",
|
|
221
|
+
masterTitle: "Email Notifications",
|
|
222
|
+
masterDescription: "Email me about activity when I\u2019m away",
|
|
223
|
+
masterChecked: prefs.masterChecked,
|
|
224
|
+
onMasterChange: prefs.setAll,
|
|
225
|
+
categories: toNotificationCategories(prefs.checked, categories),
|
|
226
|
+
onCategoryChange: prefs.setCategory
|
|
227
|
+
}
|
|
228
|
+
) }) });
|
|
229
|
+
}
|
|
230
|
+
function ConnectedSessionsRow({
|
|
231
|
+
notify,
|
|
232
|
+
confirm,
|
|
233
|
+
renderSessionIcon
|
|
234
|
+
}) {
|
|
235
|
+
const [open, setOpen] = useState(false);
|
|
236
|
+
const sessions = useSessions({ enabled: open });
|
|
237
|
+
const currentSessionId = useCurrentSessionId();
|
|
238
|
+
const terminate = useTerminateSession();
|
|
239
|
+
const views = toActiveSessionViews(sessions.data ?? [], {
|
|
240
|
+
currentSessionId,
|
|
241
|
+
terminatingId: terminate.isPending ? terminate.variables : null,
|
|
242
|
+
renderIcon: renderSessionIcon
|
|
243
|
+
});
|
|
244
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
245
|
+
/* @__PURE__ */ jsx(
|
|
246
|
+
SettingsRow,
|
|
247
|
+
{
|
|
248
|
+
label: "Active sessions",
|
|
249
|
+
description: "Devices and agents currently signed in to your account.",
|
|
250
|
+
children: /* @__PURE__ */ jsxs(
|
|
251
|
+
Button,
|
|
252
|
+
{
|
|
253
|
+
type: "button",
|
|
254
|
+
variant: "outline",
|
|
255
|
+
size: "sm",
|
|
256
|
+
onClick: () => setOpen(true),
|
|
257
|
+
className: "flex items-center gap-2",
|
|
258
|
+
"data-testid": "active-sessions-button",
|
|
259
|
+
children: [
|
|
260
|
+
/* @__PURE__ */ jsx(Radio, { className: "h-4 w-4" }),
|
|
261
|
+
"Manage"
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
),
|
|
267
|
+
/* @__PURE__ */ jsx(
|
|
268
|
+
ActiveSessionsDialog,
|
|
269
|
+
{
|
|
270
|
+
open,
|
|
271
|
+
onOpenChange: setOpen,
|
|
272
|
+
isLoading: sessions.isLoading,
|
|
273
|
+
sessions: views,
|
|
274
|
+
onTerminate: (id) => {
|
|
275
|
+
void (async () => {
|
|
276
|
+
const view = views.find((v) => v.id === id);
|
|
277
|
+
const label = view?.detail.split(" \xB7 ")[0];
|
|
278
|
+
const ok = await confirm?.(
|
|
279
|
+
`End the session on ${label}? It will be signed out immediately.`
|
|
280
|
+
) ?? true;
|
|
281
|
+
if (!ok) return;
|
|
282
|
+
try {
|
|
283
|
+
await terminate.mutateAsync(id);
|
|
284
|
+
} catch {
|
|
285
|
+
notify?.("Failed to end session. Please try again.", "error");
|
|
286
|
+
}
|
|
287
|
+
})();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
] });
|
|
292
|
+
}
|
|
293
|
+
function ConnectedLogoutDialog({
|
|
294
|
+
open,
|
|
295
|
+
onClose,
|
|
296
|
+
isOnline = true,
|
|
297
|
+
renderSessionIcon
|
|
298
|
+
}) {
|
|
299
|
+
const { logout } = useConnection();
|
|
300
|
+
const sessions = useSessions({ enabled: open });
|
|
301
|
+
const logoutAll = useLogoutAllSessions();
|
|
302
|
+
return /* @__PURE__ */ jsx(
|
|
303
|
+
LogoutDialog,
|
|
304
|
+
{
|
|
305
|
+
open,
|
|
306
|
+
onClose,
|
|
307
|
+
onLogoutThis: () => {
|
|
308
|
+
onClose();
|
|
309
|
+
logout();
|
|
310
|
+
},
|
|
311
|
+
onLogoutAll: () => {
|
|
312
|
+
onClose();
|
|
313
|
+
void logoutAll.mutateAsync().finally(logout);
|
|
314
|
+
},
|
|
315
|
+
canLogoutAll: true,
|
|
316
|
+
isOnline,
|
|
317
|
+
sessions: toLogoutSessionViews(sessions.data ?? []),
|
|
318
|
+
renderSessionIcon
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
function ConnectedDangerZone({ notify, renderSubscriptionLink }) {
|
|
323
|
+
const [zoneOpen, setZoneOpen] = useState(false);
|
|
324
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
325
|
+
const { user, logout } = useConnection();
|
|
326
|
+
const remove = useDeleteAccount();
|
|
327
|
+
const flow = useDeleteAccountFlow({
|
|
328
|
+
userEmail: user?.email ?? "",
|
|
329
|
+
remove: () => remove.mutateAsync(),
|
|
330
|
+
onClose: () => setDialogOpen(false),
|
|
331
|
+
onDeleted: logout,
|
|
332
|
+
onError: () => notify?.("Failed to delete account. Please try again.", "error")
|
|
333
|
+
});
|
|
334
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
335
|
+
/* @__PURE__ */ jsx(
|
|
336
|
+
SettingsDangerZone,
|
|
337
|
+
{
|
|
338
|
+
isOpen: zoneOpen,
|
|
339
|
+
onToggle: () => setZoneOpen((o) => !o),
|
|
340
|
+
title: "Delete your account",
|
|
341
|
+
description: "Once deleted, your account will be immediately closed and you will be logged out. This action cannot be undone.",
|
|
342
|
+
action: /* @__PURE__ */ jsx(
|
|
343
|
+
Button,
|
|
344
|
+
{
|
|
345
|
+
variant: "destructive",
|
|
346
|
+
size: "sm",
|
|
347
|
+
disabled: !user?.email,
|
|
348
|
+
onClick: () => setDialogOpen(true),
|
|
349
|
+
"data-testid": "delete-account-button",
|
|
350
|
+
children: "Delete Account"
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
),
|
|
355
|
+
/* @__PURE__ */ jsx(
|
|
356
|
+
DeleteAccountDialog,
|
|
357
|
+
{
|
|
358
|
+
open: dialogOpen,
|
|
359
|
+
onClose: flow.onClose,
|
|
360
|
+
step: flow.step,
|
|
361
|
+
blockers: toDeleteBlockerViews(flow.blockers, renderSubscriptionLink),
|
|
362
|
+
onContinue: flow.onContinue,
|
|
363
|
+
onBack: flow.onBack,
|
|
364
|
+
userEmail: user?.email ?? "",
|
|
365
|
+
confirmEmail: flow.confirmEmail,
|
|
366
|
+
onConfirmEmailChange: flow.onConfirmEmailChange,
|
|
367
|
+
isDeleting: flow.isDeleting,
|
|
368
|
+
onSubmit: flow.onSubmit
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
] });
|
|
372
|
+
}
|
|
41
373
|
|
|
42
|
-
export { ArbiAppProvider, useConnectedShellWorkspaces };
|
|
374
|
+
export { ArbiAppProvider, ConnectedDangerZone, ConnectedLogoutDialog, ConnectedNotificationPreferences, ConnectedProfileCard, ConnectedSecuritySection, ConnectedSessionsRow, defaultUnmetPasswordRequirement, useConnectedShellWorkspaces };
|
|
43
375
|
//# sourceMappingURL=index.js.map
|
|
44
376
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/connection/ArbiAppProvider.tsx","../../src/connection/useConnectedShellWorkspaces.ts"],"names":[],"mappings":";;;;;;;;;;;AAqBA,SAAS,wBAAA,CAAyB,EAAE,QAAA,EAAS,EAA4B;AACvE,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,aAAA,EAAc;AACjC,EAAA,uBACE,GAAA,CAAC,qBAAA,EAAA,EAAsB,OAAA,EAAS,MAAA,KAAW,iBAAkB,QAAA,EAAS,CAAA;AAE1E;AAEO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,MAAM,CAAC,WAAW,CAAA,GAAI,QAAA;AAAA,IACpB,MACE,IAAI,WAAA,CAAY;AAAA,MACd,cAAA,EAAgB,EAAE,OAAA,EAAS,EAAE,SAAA,EAAW,KAAQ,KAAA,EAAO,CAAA,EAAG,oBAAA,EAAsB,KAAA,EAAM;AAAE,KACzF;AAAA,GACL;AAEA,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,UAAA,IAAc,yBAAA;AAE1D,EAAA,2BACG,mBAAA,EAAA,EAAoB,MAAA,EAAQ,WAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,gBAAa,GAAA,EAAK,WAAA,EACjB,QAAA,kBAAA,GAAA,CAAC,kBAAA,EAAA,EAAmB,QAClB,QAAA,kBAAA,GAAA,CAAC,wBAAA,EAAA,EAA0B,QAAA,EAAS,CAAA,EACtC,GACF,CAAA,EACF,CAAA;AAEJ;AC3BO,SAAS,2BAAA,CACd,OAAA,GAA8C,EAAC,EACrB;AAC1B,EAAA,MAAM,EAAE,WAAA,EAAa,YAAA,EAAa,GAAI,aAAA,EAAc;AACpD,EAAA,MAAM,QAAQ,aAAA,EAAc;AAC5B,EAAA,OAAO,kBAAA,CAAmB;AAAA,IACxB,GAAG,OAAA;AAAA,IACH,YAAY,KAAA,CAAM,OAAA,CAAQ,MAAM,IAAI,CAAA,GAAK,MAAM,IAAA,GAAkC,MAAA;AAAA,IACjF,WAAW,KAAA,CAAM,SAAA;AAAA,IACjB,WAAW,KAAA,CAAM,KAAA;AAAA,IACjB,UAAA,EAAY,WAAA;AAAA,IACZ,MAAA,EAAQ,CAAC,SAAA,KAAc,YAAA,CAAa,UAAU,EAAE;AAAA,GACjD,CAAA;AACH","file":"index.js","sourcesContent":["/**\n * Composition root for a host app on the ARBI React SDK.\n *\n * QueryClientProvider → react-query cache shared with the SDK\n * ArbiProvider → the SDK's Arbi instance (no network until login)\n * ConnectionProvider → demo/live auth + workspace layer\n *\n * With no live `apiUrl`, ArbiProvider is still mounted (cheap, inert) against\n * `demoApiUrl` and the app runs entirely in seeded demo mode.\n */\nimport { useState, type ReactNode } from 'react'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ArbiProvider, ArbiWebSocketProvider } from '@arbidocs/react'\nimport { ConnectionProvider, useConnection } from './ConnectionProvider'\nimport type { ArbiConnectionConfig } from './types'\n\n/**\n * Opens the shared SDK WebSocket once the app is live-authenticated (demo /\n * logged-out sessions have no backend, so they stay disconnected). Lives inside\n * ConnectionProvider so it can read the live status.\n */\nfunction ConnectionBoundWebSocket({ children }: { children: ReactNode }) {\n const { status } = useConnection()\n return (\n <ArbiWebSocketProvider enabled={status === 'authenticated'}>{children}</ArbiWebSocketProvider>\n )\n}\n\nexport function ArbiAppProvider({\n config,\n children,\n}: {\n config: ArbiConnectionConfig\n children: ReactNode\n}) {\n const [queryClient] = useState(\n () =>\n new QueryClient({\n defaultOptions: { queries: { staleTime: 30_000, retry: 1, refetchOnWindowFocus: false } },\n })\n )\n\n const providerUrl = config.apiUrl || config.demoApiUrl || 'https://demo.arbi.local'\n\n return (\n <QueryClientProvider client={queryClient}>\n <ArbiProvider url={providerUrl}>\n <ConnectionProvider config={config}>\n <ConnectionBoundWebSocket>{children}</ConnectionBoundWebSocket>\n </ConnectionProvider>\n </ArbiProvider>\n </QueryClientProvider>\n )\n}\n","import {\n useShellWorkspaces,\n useWorkspaces,\n type ShellWorkspaceSource,\n type UseShellWorkspacesOptions,\n type UseShellWorkspacesResult,\n} from '@arbidocs/react'\nimport { useConnection } from './ConnectionProvider'\n\nexport type UseConnectedShellWorkspacesOptions = Omit<\n UseShellWorkspacesOptions,\n 'workspaces' | 'isLoading' | 'loadError' | 'selectedId' | 'select'\n>\n\n/**\n * `useShellWorkspaces` wired to the connection layer: the account's live workspaces, the\n * connection's single selection as the active workspace, and `open` = `setWorkspace` (opens it on\n * the server, remembers it for the next login) then `onOpened`.\n *\n * ```tsx\n * const { active, recent, open } = useConnectedShellWorkspaces({\n * seed: { workspaces: demoWorkspaces, activeId: 'demo-1' },\n * onOpened: () => navigate('/app/documents'),\n * })\n * ```\n */\nexport function useConnectedShellWorkspaces(\n options: UseConnectedShellWorkspacesOptions = {}\n): UseShellWorkspacesResult {\n const { workspaceId, setWorkspace } = useConnection()\n const query = useWorkspaces()\n return useShellWorkspaces({\n ...options,\n workspaces: Array.isArray(query.data) ? (query.data as ShellWorkspaceSource[]) : undefined,\n isLoading: query.isLoading,\n loadError: query.error,\n selectedId: workspaceId,\n select: (workspace) => setWorkspace(workspace.id),\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/connection/ArbiAppProvider.tsx","../../src/connection/useConnectedShellWorkspaces.ts","../../src/connection/ConnectedSettings.tsx"],"names":["jsx","useState","useWorkspaces"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Composition root for a host app on the ARBI React SDK.\n *\n * QueryClientProvider → react-query cache shared with the SDK\n * ArbiProvider → the SDK's Arbi instance (no network until login)\n * ConnectionProvider → demo/live auth + workspace layer\n *\n * With no live `apiUrl`, ArbiProvider is still mounted (cheap, inert) against\n * `demoApiUrl` and the app runs entirely in seeded demo mode.\n */\nimport { useState, type ReactNode } from 'react'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { ArbiProvider, ArbiWebSocketProvider } from '@arbidocs/react'\nimport { ConnectionProvider, useConnection } from './ConnectionProvider'\nimport type { ArbiConnectionConfig } from './types'\n\n/**\n * Opens the shared SDK WebSocket once the app is live-authenticated (demo /\n * logged-out sessions have no backend, so they stay disconnected). Lives inside\n * ConnectionProvider so it can read the live status.\n */\nfunction ConnectionBoundWebSocket({ children }: { children: ReactNode }) {\n const { status } = useConnection()\n return (\n <ArbiWebSocketProvider enabled={status === 'authenticated'}>{children}</ArbiWebSocketProvider>\n )\n}\n\nexport function ArbiAppProvider({\n config,\n children,\n}: {\n config: ArbiConnectionConfig\n children: ReactNode\n}) {\n const [queryClient] = useState(\n () =>\n new QueryClient({\n defaultOptions: { queries: { staleTime: 30_000, retry: 1, refetchOnWindowFocus: false } },\n })\n )\n\n const providerUrl = config.apiUrl || config.demoApiUrl || 'https://demo.arbi.local'\n\n return (\n <QueryClientProvider client={queryClient}>\n <ArbiProvider url={providerUrl}>\n <ConnectionProvider config={config}>\n <ConnectionBoundWebSocket>{children}</ConnectionBoundWebSocket>\n </ConnectionProvider>\n </ArbiProvider>\n </QueryClientProvider>\n )\n}\n","import {\n useShellWorkspaces,\n useWorkspaces,\n type ShellWorkspaceSource,\n type UseShellWorkspacesOptions,\n type UseShellWorkspacesResult,\n} from '@arbidocs/react'\nimport { useConnection } from './ConnectionProvider'\n\nexport type UseConnectedShellWorkspacesOptions = Omit<\n UseShellWorkspacesOptions,\n 'workspaces' | 'isLoading' | 'loadError' | 'selectedId' | 'select'\n>\n\n/**\n * `useShellWorkspaces` wired to the connection layer: the account's live workspaces, the\n * connection's single selection as the active workspace, and `open` = `setWorkspace` (opens it on\n * the server, remembers it for the next login) then `onOpened`.\n *\n * ```tsx\n * const { active, recent, open } = useConnectedShellWorkspaces({\n * seed: { workspaces: demoWorkspaces, activeId: 'demo-1' },\n * onOpened: () => navigate('/app/documents'),\n * })\n * ```\n */\nexport function useConnectedShellWorkspaces(\n options: UseConnectedShellWorkspacesOptions = {}\n): UseShellWorkspacesResult {\n const { workspaceId, setWorkspace } = useConnection()\n const query = useWorkspaces()\n return useShellWorkspaces({\n ...options,\n workspaces: Array.isArray(query.data) ? (query.data as ShellWorkspaceSource[]) : undefined,\n isLoading: query.isLoading,\n loadError: query.error,\n selectedId: workspaceId,\n select: (workspace) => setWorkspace(workspace.id),\n })\n}\n","/**\n * The account area of a settings page, bound to the ARBI SDK: each section is a blocks part wired\n * to its `@arbidocs/react` hook, so a host drops them in a `SettingsPanel` and owns only the\n * layout (see the \"Settings page recipe\" in docs/design/blocks-package.md).\n *\n * Every section takes an optional `notify` for toasts and renders the default test ids of its\n * part. Anything the hooks cannot know (the recovery-key download, the billing link of a blocked\n * deletion, the password policy) is a prop.\n */\nimport { useMemo, useState, type ReactNode } from 'react'\nimport { KeyRound, Radio, UserCircle } from 'lucide-react'\nimport {\n ImageCropModal,\n useAvatarCrop,\n useConfirmEmailChange,\n useCurrentSessionId,\n useDeleteAccount,\n useDeleteAccountFlow,\n useEmailChange,\n useLogoutAllSessions,\n useNameEditor,\n useNotificationPreferences,\n usePasswordChange,\n usePasswordChangeForm,\n useProfile,\n useRequestEmailChange,\n useSessions,\n useTerminateSession,\n useUpdateProfile,\n useWorkspaces,\n type AvatarFileProblem,\n type PasswordChangeResult,\n} from '@arbidocs/react'\nimport { Button } from '@arbidocs/react/ui'\nimport { initials } from '../lib/format'\nimport { AvatarPicker } from '../settings/AvatarPicker'\nimport { ProfileCard } from '../settings/ProfileCard'\nimport {\n SettingsActionRow,\n SettingsCard,\n SettingsDangerZone,\n SettingsRow,\n SettingsSection,\n} from '../settings/SettingsPanel'\nimport { PasswordChangeDialog } from '../settings/PasswordChangeDialog'\nimport { ActiveSessionsDialog, DeleteAccountDialog } from '../settings/AccountDialogs'\nimport { LogoutDialog } from '../settings/LogoutDialog'\nimport { NotificationCategoryList } from '../settings/NotificationCategoryList'\nimport {\n EMAIL_NOTIFICATION_CATEGORIES,\n EMAIL_NOTIFICATION_KEYS,\n toActiveSessionViews,\n toDeleteBlockerViews,\n toLogoutSessionViews,\n toNotificationCategories,\n type EmailNotificationCategoryDef,\n} from '../settings/account-views'\nimport { useConnection } from './ConnectionProvider'\n\nexport type SettingsNotify = (message: string, kind: 'success' | 'error') => void\n\n/** The error text of a failed call: the backend `detail` when it sent one. */\nfunction errorText(error: unknown, fallback: string): string {\n const body = (error as { apiError?: { detail?: unknown } } | null)?.apiError\n return typeof body?.detail === 'string' ? body.detail : fallback\n}\n\nconst AVATAR_PROBLEMS: Record<AvatarFileProblem, string> = {\n 'not-image': 'Please choose an image file',\n 'too-large': 'Image must be 5 MB or smaller',\n unreadable: \"Couldn't read that image — try a different file\",\n}\n\n// ── Profile ─────────────────────────────────────────────────────────────────\n\nexport interface ConnectedProfileCardProps {\n notify?: SettingsNotify\n /** Section heading; default `Profile`. */\n title?: ReactNode\n}\n\n/** Avatar, editable name and email change of the signed-in user (`ProfileCard`). */\nexport function ConnectedProfileCard({ notify, title = 'Profile' }: ConnectedProfileCardProps) {\n const { data: profile } = useProfile()\n const updateProfile = useUpdateProfile()\n const requestEmailChange = useRequestEmailChange()\n const confirmEmailChange = useConfirmEmailChange()\n const { user } = useConnection()\n\n const name = useNameEditor({\n givenName: profile?.givenName,\n familyName: profile?.familyName,\n save: async (next) => {\n try {\n await updateProfile.mutateAsync(next)\n notify?.('Name updated', 'success')\n } catch (e) {\n notify?.('Failed to update name', 'error')\n throw e\n }\n },\n })\n const avatar = useAvatarCrop({\n upload: async (picture) => {\n try {\n await updateProfile.mutateAsync({ picture })\n notify?.('Profile picture updated', 'success')\n } catch (e) {\n notify?.('Failed to update picture', 'error')\n throw e\n }\n },\n onProblem: (problem) => notify?.(AVATAR_PROBLEMS[problem], 'error'),\n })\n const email = useEmailChange({\n requestCode: async (address) => {\n try {\n await requestEmailChange.mutateAsync(address)\n notify?.('Verification code sent to your new email', 'success')\n } catch (e) {\n notify?.(errorText(e, 'Failed to send verification code'), 'error')\n throw e\n }\n },\n confirm: async (args) => {\n try {\n await confirmEmailChange.mutateAsync(args)\n notify?.('Email address updated', 'success')\n } catch (e) {\n notify?.(errorText(e, 'Invalid or expired verification code'), 'error')\n throw e\n }\n },\n })\n\n const display = profile?.fullName || user?.name || 'Unnamed'\n return (\n <SettingsSection icon={<UserCircle />} title={title}>\n <ProfileCard\n avatar={\n <>\n <AvatarPicker\n pictureUrl={profile?.picture ?? user?.picture}\n initials={initials(display)}\n fallbackClassName=\"bg-primary text-primary-foreground\"\n isUploading={avatar.isUploading}\n onFileChosen={avatar.onFileChosen}\n />\n <ImageCropModal\n open={avatar.pendingImage !== null}\n image={avatar.pendingImage}\n title=\"Crop your photo\"\n aspect={1}\n cropShape=\"round\"\n output={{ maxSize: 128, mimeType: 'image/jpeg', quality: 0.85 }}\n onComplete={avatar.onCropComplete}\n onCancel={avatar.onCropCancel}\n />\n </>\n }\n name={{ ...name, display, isSaving: name.isSaving }}\n email={profile?.email ?? user?.email ?? ''}\n isSso={profile?.isSso ?? false}\n onStartEmailChange={email.onStart}\n emailChange={email.isOpen ? email : undefined}\n />\n </SettingsSection>\n )\n}\n\n// ── Security: password ──────────────────────────────────────────────────────\n\n/** The default password policy: 8 characters, an uppercase letter or symbol, a digit. */\nexport function defaultUnmetPasswordRequirement(password: string): string | null {\n if (password.length < 8) return 'Minimum 8 characters'\n if (!/[A-Z!@#$%^&*()_+\\-=[\\]{};':\"\\\\|,.<>/?]/.test(password)) {\n return 'At least one uppercase letter or special character'\n }\n if (!/[0-9]/.test(password)) return 'At least one number'\n return null\n}\n\nexport interface ConnectedSecuritySectionProps {\n notify?: SettingsNotify\n /** The host's password policy; default is {@link defaultUnmetPasswordRequirement}. */\n getUnmetRequirement?: (password: string) => string | null\n /** The recovery-key download for the key the new password produced (shown on success). */\n renderRecoveryKey?: (result: PasswordChangeResult) => ReactNode\n /** Extra rows under \"Change password\" (e.g. \"Save recovery key\"). */\n children?: ReactNode\n}\n\n/** The Security card: \"Change password\" and the dialog behind it (`PasswordChangeDialog`). */\nexport function ConnectedSecuritySection({\n notify,\n getUnmetRequirement = defaultUnmetPasswordRequirement,\n renderRecoveryKey,\n children,\n}: ConnectedSecuritySectionProps) {\n const [open, setOpen] = useState(false)\n const change = usePasswordChange()\n // Only workspaces the user belongs to are re-keyed.\n const { data: workspaces } = useWorkspaces({ enabled: open })\n const workspaceCount = (workspaces ?? []).filter((w) => w.is_member).length\n const form = usePasswordChangeForm<PasswordChangeResult>({\n getUnmetRequirement,\n submit: async (vars) => {\n const result = await change.mutateAsync(vars)\n notify?.('Password changed successfully', 'success')\n return result\n },\n onClose: () => setOpen(false),\n })\n\n return (\n <SettingsSection icon={<KeyRound />} title=\"Security\">\n <SettingsCard padding=\"sm\">\n <SettingsActionRow\n title=\"Change password\"\n description=\"Used to sign in and to encrypt your data. Changing it re-encrypts everything in your account.\"\n onClick={() => setOpen(true)}\n testId=\"change-password-button\"\n />\n {children}\n </SettingsCard>\n <PasswordChangeDialog\n open={open}\n onClose={form.onCancel}\n recoveryMode={false}\n currentPassword={form.currentPassword}\n onCurrentPasswordChange={form.onCurrentPasswordChange}\n newPassword={form.newPassword}\n onNewPasswordChange={form.onNewPasswordChange}\n confirmPassword={form.confirmPassword}\n onConfirmPasswordChange={form.onConfirmPasswordChange}\n getUnmetRequirement={getUnmetRequirement}\n workspaceCount={workspaceCount}\n error={form.error}\n isProcessing={form.isProcessing}\n isSubmitDisabled={form.isSubmitDisabled}\n onSubmit={form.onSubmit}\n success={form.result ? (renderRecoveryKey?.(form.result) ?? <></>) : null}\n onDone={form.onDone}\n />\n </SettingsSection>\n )\n}\n\n// ── Notifications ───────────────────────────────────────────────────────────\n\nexport interface ConnectedNotificationPreferencesProps {\n /** Categories to offer; default {@link EMAIL_NOTIFICATION_CATEGORIES}. */\n categories?: readonly EmailNotificationCategoryDef[]\n title?: ReactNode\n}\n\n/** The email-notification switches (`NotificationCategoryList`) over the user settings. */\nexport function ConnectedNotificationPreferences({\n categories = EMAIL_NOTIFICATION_CATEGORIES,\n title = 'Notifications',\n}: ConnectedNotificationPreferencesProps) {\n const keys = useMemo(\n () =>\n categories === EMAIL_NOTIFICATION_CATEGORIES\n ? EMAIL_NOTIFICATION_KEYS\n : categories.map((c) => c.key),\n [categories]\n )\n const prefs = useNotificationPreferences(keys)\n return (\n <SettingsSection title={title}>\n <SettingsCard className=\"space-y-0\">\n <NotificationCategoryList\n masterId=\"emailNotifications\"\n masterTitle=\"Email Notifications\"\n masterDescription=\"Email me about activity when I’m away\"\n masterChecked={prefs.masterChecked}\n onMasterChange={prefs.setAll}\n categories={toNotificationCategories(prefs.checked, categories)}\n onCategoryChange={prefs.setCategory}\n />\n </SettingsCard>\n </SettingsSection>\n )\n}\n\n// ── Sessions ────────────────────────────────────────────────────────────────\n\nexport interface ConnectedSessionsRowProps {\n notify?: SettingsNotify\n /** Ask before ending a session; resolve `false` to cancel. Default: end at once. */\n confirm?: (message: string) => Promise<boolean> | boolean\n /** Glyph per session status. */\n renderSessionIcon?: (status: string) => ReactNode\n}\n\n/** \"Active sessions\" row with the manage dialog (`ActiveSessionsDialog`); end any but this device. */\nexport function ConnectedSessionsRow({\n notify,\n confirm,\n renderSessionIcon,\n}: ConnectedSessionsRowProps) {\n const [open, setOpen] = useState(false)\n const sessions = useSessions({ enabled: open })\n const currentSessionId = useCurrentSessionId()\n const terminate = useTerminateSession()\n const views = toActiveSessionViews(sessions.data ?? [], {\n currentSessionId,\n terminatingId: terminate.isPending ? terminate.variables : null,\n renderIcon: renderSessionIcon,\n })\n return (\n <>\n <SettingsRow\n label=\"Active sessions\"\n description=\"Devices and agents currently signed in to your account.\"\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setOpen(true)}\n className=\"flex items-center gap-2\"\n data-testid=\"active-sessions-button\"\n >\n <Radio className=\"h-4 w-4\" />\n Manage\n </Button>\n </SettingsRow>\n <ActiveSessionsDialog\n open={open}\n onOpenChange={setOpen}\n isLoading={sessions.isLoading}\n sessions={views}\n onTerminate={(id) => {\n void (async () => {\n const view = views.find((v) => v.id === id)\n const label = view?.detail.split(' · ')[0]\n const ok =\n (await confirm?.(\n `End the session on ${label}? It will be signed out immediately.`\n )) ?? true\n if (!ok) return\n try {\n await terminate.mutateAsync(id)\n } catch {\n notify?.('Failed to end session. Please try again.', 'error')\n }\n })()\n }}\n />\n </>\n )\n}\n\nexport interface ConnectedLogoutDialogProps {\n open: boolean\n onClose: () => void\n isOnline?: boolean\n renderSessionIcon?: (status: string) => ReactNode\n}\n\n/** Sign out of this device or all devices (`LogoutDialog`), listing the active sessions. */\nexport function ConnectedLogoutDialog({\n open,\n onClose,\n isOnline = true,\n renderSessionIcon,\n}: ConnectedLogoutDialogProps) {\n const { logout } = useConnection()\n const sessions = useSessions({ enabled: open })\n const logoutAll = useLogoutAllSessions()\n return (\n <LogoutDialog\n open={open}\n onClose={onClose}\n onLogoutThis={() => {\n onClose()\n logout()\n }}\n onLogoutAll={() => {\n onClose()\n // The server drops every session first; the connection then forgets this one.\n void logoutAll.mutateAsync().finally(logout)\n }}\n canLogoutAll\n isOnline={isOnline}\n sessions={toLogoutSessionViews(sessions.data ?? [])}\n renderSessionIcon={renderSessionIcon}\n />\n )\n}\n\n// ── Danger zone ─────────────────────────────────────────────────────────────\n\nexport interface ConnectedDangerZoneProps {\n notify?: SettingsNotify\n /** Link to the billing page for a deletion blocked by an active subscription. */\n renderSubscriptionLink?: () => ReactNode\n}\n\n/** \"Delete your account\": the collapsed danger zone and the two-step dialog (`DeleteAccountDialog`). */\nexport function ConnectedDangerZone({ notify, renderSubscriptionLink }: ConnectedDangerZoneProps) {\n const [zoneOpen, setZoneOpen] = useState(false)\n const [dialogOpen, setDialogOpen] = useState(false)\n const { user, logout } = useConnection()\n const remove = useDeleteAccount()\n const flow = useDeleteAccountFlow({\n userEmail: user?.email ?? '',\n remove: () => remove.mutateAsync(),\n onClose: () => setDialogOpen(false),\n onDeleted: logout,\n onError: () => notify?.('Failed to delete account. Please try again.', 'error'),\n })\n return (\n <>\n <SettingsDangerZone\n isOpen={zoneOpen}\n onToggle={() => setZoneOpen((o) => !o)}\n title=\"Delete your account\"\n description=\"Once deleted, your account will be immediately closed and you will be logged out. This action cannot be undone.\"\n action={\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={!user?.email}\n onClick={() => setDialogOpen(true)}\n data-testid=\"delete-account-button\"\n >\n Delete Account\n </Button>\n }\n />\n <DeleteAccountDialog\n open={dialogOpen}\n onClose={flow.onClose}\n step={flow.step}\n blockers={toDeleteBlockerViews(flow.blockers, renderSubscriptionLink)}\n onContinue={flow.onContinue}\n onBack={flow.onBack}\n userEmail={user?.email ?? ''}\n confirmEmail={flow.confirmEmail}\n onConfirmEmailChange={flow.onConfirmEmailChange}\n isDeleting={flow.isDeleting}\n onSubmit={flow.onSubmit}\n />\n </>\n )\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The account area of a settings page, bound to the ARBI SDK: each section is a blocks part wired
|
|
3
|
+
* to its `@arbidocs/react` hook, so a host drops them in a `SettingsPanel` and owns only the
|
|
4
|
+
* layout (see the "Settings page recipe" in docs/design/blocks-package.md).
|
|
5
|
+
*
|
|
6
|
+
* Every section takes an optional `notify` for toasts and renders the default test ids of its
|
|
7
|
+
* part. Anything the hooks cannot know (the recovery-key download, the billing link of a blocked
|
|
8
|
+
* deletion, the password policy) is a prop.
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactNode } from 'react';
|
|
11
|
+
import { type PasswordChangeResult } from '@arbidocs/react';
|
|
12
|
+
import { type EmailNotificationCategoryDef } from '../settings/account-views';
|
|
13
|
+
export type SettingsNotify = (message: string, kind: 'success' | 'error') => void;
|
|
14
|
+
export interface ConnectedProfileCardProps {
|
|
15
|
+
notify?: SettingsNotify;
|
|
16
|
+
/** Section heading; default `Profile`. */
|
|
17
|
+
title?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
/** Avatar, editable name and email change of the signed-in user (`ProfileCard`). */
|
|
20
|
+
export declare function ConnectedProfileCard({ notify, title }: ConnectedProfileCardProps): import("react").JSX.Element;
|
|
21
|
+
/** The default password policy: 8 characters, an uppercase letter or symbol, a digit. */
|
|
22
|
+
export declare function defaultUnmetPasswordRequirement(password: string): string | null;
|
|
23
|
+
export interface ConnectedSecuritySectionProps {
|
|
24
|
+
notify?: SettingsNotify;
|
|
25
|
+
/** The host's password policy; default is {@link defaultUnmetPasswordRequirement}. */
|
|
26
|
+
getUnmetRequirement?: (password: string) => string | null;
|
|
27
|
+
/** The recovery-key download for the key the new password produced (shown on success). */
|
|
28
|
+
renderRecoveryKey?: (result: PasswordChangeResult) => ReactNode;
|
|
29
|
+
/** Extra rows under "Change password" (e.g. "Save recovery key"). */
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/** The Security card: "Change password" and the dialog behind it (`PasswordChangeDialog`). */
|
|
33
|
+
export declare function ConnectedSecuritySection({ notify, getUnmetRequirement, renderRecoveryKey, children, }: ConnectedSecuritySectionProps): import("react").JSX.Element;
|
|
34
|
+
export interface ConnectedNotificationPreferencesProps {
|
|
35
|
+
/** Categories to offer; default {@link EMAIL_NOTIFICATION_CATEGORIES}. */
|
|
36
|
+
categories?: readonly EmailNotificationCategoryDef[];
|
|
37
|
+
title?: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
/** The email-notification switches (`NotificationCategoryList`) over the user settings. */
|
|
40
|
+
export declare function ConnectedNotificationPreferences({ categories, title, }: ConnectedNotificationPreferencesProps): import("react").JSX.Element;
|
|
41
|
+
export interface ConnectedSessionsRowProps {
|
|
42
|
+
notify?: SettingsNotify;
|
|
43
|
+
/** Ask before ending a session; resolve `false` to cancel. Default: end at once. */
|
|
44
|
+
confirm?: (message: string) => Promise<boolean> | boolean;
|
|
45
|
+
/** Glyph per session status. */
|
|
46
|
+
renderSessionIcon?: (status: string) => ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/** "Active sessions" row with the manage dialog (`ActiveSessionsDialog`); end any but this device. */
|
|
49
|
+
export declare function ConnectedSessionsRow({ notify, confirm, renderSessionIcon, }: ConnectedSessionsRowProps): import("react").JSX.Element;
|
|
50
|
+
export interface ConnectedLogoutDialogProps {
|
|
51
|
+
open: boolean;
|
|
52
|
+
onClose: () => void;
|
|
53
|
+
isOnline?: boolean;
|
|
54
|
+
renderSessionIcon?: (status: string) => ReactNode;
|
|
55
|
+
}
|
|
56
|
+
/** Sign out of this device or all devices (`LogoutDialog`), listing the active sessions. */
|
|
57
|
+
export declare function ConnectedLogoutDialog({ open, onClose, isOnline, renderSessionIcon, }: ConnectedLogoutDialogProps): import("react").JSX.Element;
|
|
58
|
+
export interface ConnectedDangerZoneProps {
|
|
59
|
+
notify?: SettingsNotify;
|
|
60
|
+
/** Link to the billing page for a deletion blocked by an active subscription. */
|
|
61
|
+
renderSubscriptionLink?: () => ReactNode;
|
|
62
|
+
}
|
|
63
|
+
/** "Delete your account": the collapsed danger zone and the two-step dialog (`DeleteAccountDialog`). */
|
|
64
|
+
export declare function ConnectedDangerZone({ notify, renderSubscriptionLink }: ConnectedDangerZoneProps): import("react").JSX.Element;
|
package/dist/facts/index.cjs
CHANGED
|
@@ -6,8 +6,8 @@ var chunkOUW6G4O2_cjs = require('../chunk-OUW6G4O2.cjs');
|
|
|
6
6
|
var chunkVPPX5XUF_cjs = require('../chunk-VPPX5XUF.cjs');
|
|
7
7
|
var chunkV4ISCZJ7_cjs = require('../chunk-V4ISCZJ7.cjs');
|
|
8
8
|
var chunk5B4PXAKI_cjs = require('../chunk-5B4PXAKI.cjs');
|
|
9
|
-
var chunkPPCATBOY_cjs = require('../chunk-PPCATBOY.cjs');
|
|
10
9
|
require('../chunk-C54XEN5T.cjs');
|
|
10
|
+
var chunkPPCATBOY_cjs = require('../chunk-PPCATBOY.cjs');
|
|
11
11
|
var chunkKDG6FER6_cjs = require('../chunk-KDG6FER6.cjs');
|
|
12
12
|
var chunkMHVNT4J3_cjs = require('../chunk-MHVNT4J3.cjs');
|
|
13
13
|
var lucideReact = require('lucide-react');
|
package/dist/facts/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import { SearchHit } from '../chunk-A2YAC22G.js';
|
|
|
4
4
|
import { formatSpan, TimelineCalendar, TimelineGantt } from '../chunk-4EZVCGRL.js';
|
|
5
5
|
import { SelectionBar, SelectionBarAction } from '../chunk-NUNKSZKU.js';
|
|
6
6
|
import { TabHeader } from '../chunk-UBNVIIBT.js';
|
|
7
|
-
import { observeResize } from '../chunk-NPLYHR2E.js';
|
|
8
7
|
import '../chunk-76JDDMSP.js';
|
|
8
|
+
import { observeResize } from '../chunk-NPLYHR2E.js';
|
|
9
9
|
import { initials } from '../chunk-OXF2IFXG.js';
|
|
10
10
|
import { testIdOr } from '../chunk-7F6CBP3Z.js';
|
|
11
11
|
import { PencilLine, Check, MoreVertical, Pencil, Trash2, CalendarDays, X, Filter, SlidersHorizontal, Columns3, ChevronDown, RotateCcw, Milestone, CalendarClock, Scale, Lightbulb, ArrowRight, Loader2, Info, PinOff, Pin, Tags, MoreHorizontal, FolderUp, BookMarked, Plus, Search, CheckCircle2, CornerDownLeft, Quote, ChevronLeft, ChevronRight, Table2, CalendarRange, GanttChartSquare } from 'lucide-react';
|
|
@@ -2,17 +2,6 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
/** Every fixed string of the viewer chrome and parts. Pass a partial `labels` to translate or reword. */
|
|
3
3
|
export interface ViewerLabels {
|
|
4
4
|
shared: string;
|
|
5
|
-
edit: string;
|
|
6
|
-
cancel: string;
|
|
7
|
-
cancelEditing: string;
|
|
8
|
-
compare: string;
|
|
9
|
-
compareTitle: string;
|
|
10
|
-
saveDocument: string;
|
|
11
|
-
saveAsVersion: string;
|
|
12
|
-
noChanges: string;
|
|
13
|
-
saving: string;
|
|
14
|
-
save: string;
|
|
15
|
-
saveVersion: string;
|
|
16
5
|
useAsReference: string;
|
|
17
6
|
useAsReferenceTitle: string;
|
|
18
7
|
crop: string;
|
|
@@ -24,12 +13,6 @@ export interface ViewerLabels {
|
|
|
24
13
|
pageInput: string;
|
|
25
14
|
pageOfTitle: string;
|
|
26
15
|
pageOf: (page: number, total: number) => string;
|
|
27
|
-
compareVs: string;
|
|
28
|
-
trackChanges: string;
|
|
29
|
-
trackChangesBack: string;
|
|
30
|
-
trackChangesView: string;
|
|
31
|
-
flipComparison: string;
|
|
32
|
-
stopComparing: string;
|
|
33
16
|
previewUnavailable: string;
|
|
34
17
|
cannotPreview: (extension: string) => string;
|
|
35
18
|
downloadToOpen: string;
|
|
@@ -77,34 +60,6 @@ export interface ViewerTitleProps {
|
|
|
77
60
|
}
|
|
78
61
|
/** Read-only document name pill with the shared marker and version label. */
|
|
79
62
|
export declare function ViewerTitle({ title, shared, versionLabel, shrink, labels, sharedTestId, }: ViewerTitleProps): import("react").JSX.Element;
|
|
80
|
-
export interface ViewerEditActionsProps {
|
|
81
|
-
isEditing: boolean;
|
|
82
|
-
isDraft: boolean;
|
|
83
|
-
/** Narrow panel: icon-only Edit/Compare. Save keeps its label. */
|
|
84
|
-
compact: boolean;
|
|
85
|
-
hasChanges: boolean;
|
|
86
|
-
isSaving: boolean;
|
|
87
|
-
/** Offer "Compare against the previous version". */
|
|
88
|
-
canCompare: boolean;
|
|
89
|
-
onStartEdit: () => void;
|
|
90
|
-
onCancel: () => void;
|
|
91
|
-
onCompare: () => void;
|
|
92
|
-
onSave: () => void;
|
|
93
|
-
labels?: Partial<ViewerLabels>;
|
|
94
|
-
testIds?: Partial<ViewerEditActionsTestIds>;
|
|
95
|
-
}
|
|
96
|
-
export interface ViewerEditActionsTestIds {
|
|
97
|
-
edit: string;
|
|
98
|
-
cancel: string;
|
|
99
|
-
compare: string;
|
|
100
|
-
save: string;
|
|
101
|
-
unsavedDot: string;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Edit / Cancel / Compare / Save. Save is the ONE filled button and only once there is something
|
|
105
|
-
* to save; a dot marks unsaved changes as a second glanceable signal.
|
|
106
|
-
*/
|
|
107
|
-
export declare function ViewerEditActions({ isEditing, isDraft, compact, hasChanges, isSaving, canCompare, onStartEdit, onCancel, onCompare, onSave, labels, testIds, }: ViewerEditActionsProps): import("react").JSX.Element;
|
|
108
63
|
export interface ViewerImageActionsProps {
|
|
109
64
|
compact: boolean;
|
|
110
65
|
onUseAsReference: () => void;
|
|
@@ -27,25 +27,6 @@ export interface ViewerPageCounterProps {
|
|
|
27
27
|
}
|
|
28
28
|
/** "3 / 12": click the number to type a page; Enter or blur commits, Escape cancels. */
|
|
29
29
|
export declare function ViewerPageCounter({ page, total, onGoTo, labels }: ViewerPageCounterProps): import("react").JSX.Element | null;
|
|
30
|
-
export interface ViewerCompareBarProps {
|
|
31
|
-
/** The compared-against document pill (the host renders its entity). */
|
|
32
|
-
compareTarget: ReactNode;
|
|
33
|
-
versionLabel?: string | null;
|
|
34
|
-
/** Artifacts toggle "Track changes"; plain documents get a flip-direction arrow instead. */
|
|
35
|
-
mode: 'track-changes' | 'flip';
|
|
36
|
-
showTrackChanges: boolean;
|
|
37
|
-
onToggleTrackChanges: () => void;
|
|
38
|
-
onFlip: () => void;
|
|
39
|
-
onStop: () => void;
|
|
40
|
-
labels?: Partial<ViewerLabels>;
|
|
41
|
-
/** Overrides the track-changes toggle test id (default `track-changes-toggle`). */
|
|
42
|
-
trackChangesTestId?: string;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* "Compare vs. [doc]" row contents: always an explicit text label (an icon-only pill was unclear),
|
|
46
|
-
* with Track changes (artifacts) or Flip (plain documents), and Stop comparing.
|
|
47
|
-
*/
|
|
48
|
-
export declare function ViewerCompareBar({ compareTarget, versionLabel, mode, showTrackChanges, onToggleTrackChanges, onFlip, onStop, labels, trackChangesTestId, }: ViewerCompareBarProps): import("react").JSX.Element;
|
|
49
30
|
/** A format that cannot be previewed in the browser: offer the original instead. */
|
|
50
31
|
export declare function ViewerPreviewUnavailable({ extension, onDownload, labels, }: {
|
|
51
32
|
extension: string;
|