@alpaca-editor/core 1.0.4074 → 1.0.4075

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 (57) hide show
  1. package/dist/components/ui/context-menu.js +1 -1
  2. package/dist/components/ui/context-menu.js.map +1 -1
  3. package/dist/config/types.d.ts +1 -0
  4. package/dist/editor/ContextMenu.js +25 -14
  5. package/dist/editor/ContextMenu.js.map +1 -1
  6. package/dist/editor/FieldListField.js +1 -1
  7. package/dist/editor/FieldListField.js.map +1 -1
  8. package/dist/editor/ai/Agents.js +7 -0
  9. package/dist/editor/ai/Agents.js.map +1 -1
  10. package/dist/editor/client/EditorShell.js +48 -6
  11. package/dist/editor/client/EditorShell.js.map +1 -1
  12. package/dist/editor/client/hooks/useEditorWebSocket.js +34 -18
  13. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  14. package/dist/editor/commands/componentCommands.js +6 -1
  15. package/dist/editor/commands/componentCommands.js.map +1 -1
  16. package/dist/editor/control-center/Info.js +1 -1
  17. package/dist/editor/control-center/Info.js.map +1 -1
  18. package/dist/editor/menubar/ActiveUsers.js +3 -3
  19. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  20. package/dist/editor/menubar/User.js +4 -2
  21. package/dist/editor/menubar/User.js.map +1 -1
  22. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +5 -7
  23. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  24. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +11 -3
  25. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  26. package/dist/editor/reviews/CommentPopover.d.ts +9 -2
  27. package/dist/editor/reviews/CommentPopover.js +12 -10
  28. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  29. package/dist/editor/reviews/PreviewInfo.js +1 -1
  30. package/dist/editor/reviews/PreviewInfo.js.map +1 -1
  31. package/dist/editor/reviews/Reviews.js +2 -2
  32. package/dist/editor/reviews/Reviews.js.map +1 -1
  33. package/dist/editor/ui/Icons.js +1 -1
  34. package/dist/editor/ui/Icons.js.map +1 -1
  35. package/dist/revision.d.ts +2 -2
  36. package/dist/revision.js +2 -2
  37. package/dist/types.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/src/components/ui/context-menu.tsx +1 -1
  40. package/src/config/types.ts +1 -0
  41. package/src/editor/ContextMenu.tsx +54 -34
  42. package/src/editor/FieldListField.tsx +1 -1
  43. package/src/editor/ai/Agents.tsx +6 -0
  44. package/src/editor/client/EditorShell.tsx +48 -5
  45. package/src/editor/client/hooks/useEditorWebSocket.ts +39 -18
  46. package/src/editor/commands/componentCommands.tsx +6 -1
  47. package/src/editor/control-center/Info.tsx +9 -7
  48. package/src/editor/menubar/ActiveUsers.tsx +7 -5
  49. package/src/editor/menubar/User.tsx +5 -2
  50. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +9 -8
  51. package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +14 -3
  52. package/src/editor/reviews/CommentPopover.tsx +20 -9
  53. package/src/editor/reviews/PreviewInfo.tsx +6 -6
  54. package/src/editor/reviews/Reviews.tsx +8 -2
  55. package/src/editor/ui/Icons.tsx +4 -4
  56. package/src/revision.ts +2 -2
  57. package/src/types.ts +1 -1
@@ -204,6 +204,7 @@ export function EditorShell({
204
204
  const [contentEditorItem, setContentEditorItem] = useState<FullItem>();
205
205
 
206
206
  const [focusedField, setFocusedField] = useState<FieldDescriptor>();
207
+ const focusedFieldRef = useRef<FieldDescriptor | undefined>(undefined);
207
208
  const [selectedRange, setSelectedRange] = useState<SelectionRange>();
208
209
 
209
210
  const [validating, setValidating] = useState(false);
@@ -426,6 +427,10 @@ export function EditorShell({
426
427
  }
427
428
  }, [selection]);
428
429
 
430
+ useEffect(() => {
431
+ focusedFieldRef.current = focusedField;
432
+ }, [focusedField]);
433
+
429
434
  const itemsRepository = useItemsRepository(setModifiedFields, addRecentEdit);
430
435
 
431
436
  const pageViewContext = usePageViewContext({
@@ -460,6 +465,13 @@ export function EditorShell({
460
465
  setSelectedForInsertion("");
461
466
  }, [selection]);
462
467
 
468
+ // Clear any selected insertion template when insert mode is disabled
469
+ useEffect(() => {
470
+ if (!insertMode) {
471
+ setSelectedForInsertion("");
472
+ }
473
+ }, [insertMode]);
474
+
463
475
  useEffect(() => {
464
476
  if (focusedField?.fieldId !== selectedRange?.fieldId) {
465
477
  setSelectedRange(undefined);
@@ -1269,6 +1281,37 @@ export function EditorShell({
1269
1281
  validate,
1270
1282
  });
1271
1283
 
1284
+ // WebSocket message handler and connection
1285
+ const messageHandler = useSocketMessageHandler({
1286
+ sessionId,
1287
+ setWebSocketMessages,
1288
+ setActiveSessions,
1289
+ sendClientInfo,
1290
+ itemsRepository,
1291
+ currentItemDescriptor,
1292
+ loadItem,
1293
+ loadItemVersions,
1294
+ setComments,
1295
+ setSuggestedEdits,
1296
+ setActiveFieldActions,
1297
+ setQuotaInfo,
1298
+ requestRefresh,
1299
+ currentItemRef,
1300
+ loadHistory,
1301
+ addRecentEdit,
1302
+ socketMessageListeners,
1303
+ });
1304
+
1305
+ const { socketRef: socketInstanceRef } = useEditorWebSocket({
1306
+ sessionId,
1307
+ onMessage: messageHandler,
1308
+ onOpen: () => console.log("Connected!"),
1309
+ onError: (error) => console.error("WebSocket error:", error),
1310
+ connectSocket,
1311
+ requestQuota,
1312
+ sendClientInfo,
1313
+ });
1314
+
1272
1315
  const switchView = (
1273
1316
  viewName: string,
1274
1317
  options?: { skipConfirmation?: boolean },
@@ -1778,7 +1821,7 @@ export function EditorShell({
1778
1821
  if (!descriptor) return;
1779
1822
 
1780
1823
  const itemId =
1781
- focusedField?.item.id ||
1824
+ focusedFieldRef.current?.item.id ||
1782
1825
  (selection.length > 0 ? selection[0] : undefined) ||
1783
1826
  descriptor.id;
1784
1827
 
@@ -1788,8 +1831,8 @@ export function EditorShell({
1788
1831
  const version = descriptor.version;
1789
1832
 
1790
1833
  const getFieldName = async () => {
1791
- if (!focusedField) return "";
1792
- const field = await itemsRepository.getField(focusedField);
1834
+ if (!focusedFieldRef.current) return "";
1835
+ const field = await itemsRepository.getField(focusedFieldRef.current);
1793
1836
  return field?.name;
1794
1837
  };
1795
1838
 
@@ -1807,7 +1850,7 @@ export function EditorShell({
1807
1850
  isNew: true,
1808
1851
  itemId,
1809
1852
  itemName: await getItemName(),
1810
- fieldId: focusedField?.fieldId,
1853
+ fieldId: focusedFieldRef.current?.fieldId,
1811
1854
  fieldName: await getFieldName(),
1812
1855
  mainItemId: descriptor.id,
1813
1856
  language,
@@ -1816,7 +1859,7 @@ export function EditorShell({
1816
1859
  rangeStart: selectedRange?.startOffset || 0,
1817
1860
  rangeEnd: selectedRange?.endOffset || 0,
1818
1861
  author: user?.name,
1819
- authorDisplayName: user?.displayName,
1862
+ authorDisplayName: user?.fullName,
1820
1863
  date: new Date().toISOString(),
1821
1864
  };
1822
1865
 
@@ -25,9 +25,35 @@ export function useEditorWebSocket(params: {
25
25
  );
26
26
  const reconnectAttemptsRef = useRef<number>(0);
27
27
 
28
+ // Stable refs for callbacks to avoid re-connecting on every render
29
+ const onMessageRef = useRef(onMessage);
30
+ const onOpenRef = useRef(onOpen);
31
+ const onErrorRef = useRef(onError);
32
+ const requestQuotaRef = useRef(requestQuota);
33
+ const sendClientInfoRef = useRef(sendClientInfo);
34
+
35
+ useEffect(() => {
36
+ onMessageRef.current = onMessage;
37
+ }, [onMessage]);
38
+ useEffect(() => {
39
+ onOpenRef.current = onOpen;
40
+ }, [onOpen]);
41
+ useEffect(() => {
42
+ onErrorRef.current = onError;
43
+ }, [onError]);
44
+ useEffect(() => {
45
+ requestQuotaRef.current = requestQuota;
46
+ }, [requestQuota]);
47
+ useEffect(() => {
48
+ sendClientInfoRef.current = sendClientInfo;
49
+ }, [sendClientInfo]);
50
+
28
51
  useEffect(() => {
29
- const attach = (socket: WebSocket) => {
30
- socket.addEventListener("message", onMessage as any);
52
+ const attach = (
53
+ socket: WebSocket,
54
+ handler: (e: MessageEvent<any>) => void,
55
+ ) => {
56
+ socket.addEventListener("message", handler as any);
31
57
  socketInstanceRef.current = socket;
32
58
  };
33
59
 
@@ -37,13 +63,16 @@ export function useEditorWebSocket(params: {
37
63
  !socket ||
38
64
  socket.readyState === WebSocket.CLOSING ||
39
65
  socket.readyState === WebSocket.CLOSED;
66
+ const handleMessage = (event: MessageEvent<any>) => {
67
+ onMessageRef.current?.(event);
68
+ };
40
69
  if (needsNewSocket) {
41
70
  socket = connectSocket(sessionId);
42
71
  socket.addEventListener("open", () => {
43
72
  reconnectAttemptsRef.current = 0;
44
- onOpen?.();
45
- sendClientInfo();
46
- requestQuota?.();
73
+ onOpenRef.current?.();
74
+ sendClientInfoRef.current?.();
75
+ requestQuotaRef.current?.();
47
76
  });
48
77
  socket.addEventListener("close", (event) => {
49
78
  if (event.code !== 1000) {
@@ -59,15 +88,15 @@ export function useEditorWebSocket(params: {
59
88
  }
60
89
  });
61
90
  socket.addEventListener("error", (error) => {
62
- onError?.(error);
91
+ onErrorRef.current?.(error);
63
92
  });
64
93
  (globalThis as any).editorSocket = socket;
65
94
  }
66
95
  if (socket) {
67
- attach(socket);
96
+ attach(socket, handleMessage);
68
97
  if (socket.readyState === WebSocket.OPEN) {
69
- sendClientInfo();
70
- requestQuota?.();
98
+ sendClientInfoRef.current?.();
99
+ requestQuotaRef.current?.();
71
100
  }
72
101
  }
73
102
  };
@@ -87,15 +116,7 @@ export function useEditorWebSocket(params: {
87
116
  socketInstanceRef.current = null;
88
117
  }
89
118
  };
90
- }, [
91
- sessionId,
92
- onMessage,
93
- onOpen,
94
- onError,
95
- connectSocket,
96
- requestQuota,
97
- sendClientInfo,
98
- ]);
119
+ }, [sessionId, connectSocket]);
99
120
 
100
121
  return { socketRef: socketInstanceRef } as const;
101
122
  }
@@ -479,7 +479,12 @@ function getCreateCommentCommand(
479
479
  const y = ev?.clientY ?? ev?.pageY ?? window.innerHeight / 2;
480
480
  setTimeout(() => {
481
481
  requestAnimationFrame(() => {
482
- showCommentPopoverAt({ x, y }, context.editContext);
482
+ const snapshotField = context.editContext.focusedField;
483
+ const snapshotRange = context.editContext.selectedRange;
484
+ showCommentPopoverAt({ x, y }, context.editContext, {
485
+ field: snapshotField,
486
+ range: snapshotRange,
487
+ });
483
488
  });
484
489
  }, 0);
485
490
  },
@@ -23,14 +23,16 @@ export function Info() {
23
23
  <label className="text-sm font-medium text-gray-500">
24
24
  Name
25
25
  </label>
26
- <p className="text-gray-900">{user.displayName || user.name}</p>
27
- </div>
28
- <div>
29
- <label className="text-sm font-medium text-gray-500">
30
- Email
31
- </label>
32
- <p className="text-gray-900">{user.email || "Not available"}</p>
26
+ <p className="text-gray-900">{user.fullName || user.name}</p>
33
27
  </div>
28
+ {user.email?.trim() && (
29
+ <div>
30
+ <label className="text-sm font-medium text-gray-500">
31
+ Email
32
+ </label>
33
+ <p className="text-gray-900">{user.email.trim()}</p>
34
+ </div>
35
+ )}
34
36
  <div>
35
37
  <label className="text-sm font-medium text-gray-500">
36
38
  User Type
@@ -105,7 +105,6 @@ export function ActiveUsers() {
105
105
  const allUsers = [...activeUsers, ...recentVisitors];
106
106
  const visibleUsers = activeSessions.slice(0, 3); // Only show active users in the avatar stack
107
107
  const remainingActiveCount = Math.max(0, activeSessions.length - 3);
108
- const totalCount = activeSessions.length + recentVisitors.length;
109
108
 
110
109
  const handleAbout = () => {
111
110
  editContext?.openDialog(AboutDialog, {});
@@ -161,12 +160,13 @@ export function ActiveUsers() {
161
160
  {activeSessions.length > 0 && (
162
161
  <>
163
162
  {activeSessions.map((session) => {
164
- const userName = session.user.displayName || session.user.name;
163
+ const userName = session.user.fullName || session.user.name;
165
164
  const idxBackslash = userName.indexOf("\\");
166
165
  const displayName =
167
166
  idxBackslash >= 0
168
167
  ? userName.substring(idxBackslash + 1)
169
168
  : userName;
169
+ const userEmail = session.user.email?.trim();
170
170
 
171
171
  return (
172
172
  <div
@@ -181,9 +181,11 @@ export function ActiveUsers() {
181
181
  <div className="truncate text-sm font-medium text-gray-900">
182
182
  {displayName}
183
183
  </div>
184
- <div className="truncate text-xs text-gray-500">
185
- {session.user.email}
186
- </div>
184
+ {userEmail && (
185
+ <div className="truncate text-xs text-gray-500">
186
+ {userEmail}
187
+ </div>
188
+ )}
187
189
  </div>
188
190
  {session.sessionId === editContext?.sessionId && (
189
191
  <div className="text-xs font-medium text-blue-600">
@@ -5,7 +5,7 @@ export function User({ session }: { session: EditSession }) {
5
5
  const editContext = useEditContext();
6
6
  if (!editContext) return;
7
7
 
8
- const userName = session.user.displayName || session.user.name;
8
+ const userName = session.user.fullName || session.user.name;
9
9
 
10
10
  const idxBackslash = userName.indexOf("\\");
11
11
  const letter =
@@ -15,11 +15,14 @@ export function User({ session }: { session: EditSession }) {
15
15
  ? userName[0].toUpperCase()
16
16
  : "";
17
17
 
18
+ const userEmail = session.user.email?.trim();
19
+ const userTitle = userEmail ? `${userName} (${userEmail})` : userName;
20
+
18
21
  return (
19
22
  <div
20
23
  className="grid h-7 w-7 items-center justify-center rounded-full text-xs font-normal text-white"
21
24
  style={{ background: session.color }}
22
- title={userName + " (" + session.user.email + ")"}
25
+ title={userTitle}
23
26
  >
24
27
  {letter?.toUpperCase()}
25
28
  </div>
@@ -3,7 +3,8 @@
3
3
  import { useEffect, useState } from "react";
4
4
  import { useEditContext } from "../client/editContext";
5
5
 
6
- import { MenuItem } from "primereact/menuitem";
6
+ import { MenuItem } from "../../config/types";
7
+ import { Plus } from "lucide-react";
7
8
 
8
9
  import { getAbsoluteIconUrl } from "../utils";
9
10
  import { Placeholder } from "../pageModel";
@@ -118,12 +119,12 @@ export function PlaceholderDropZone({
118
119
  src={getAbsoluteIconUrl(x.icon)}
119
120
  width="16"
120
121
  height="16"
121
- className="m-1"
122
+ className="shrink-0"
122
123
  />
123
124
  ),
124
125
  label: x.name,
125
- command: () => {
126
- editContext.operations.addComponent(
126
+ command: async (_e: any) => {
127
+ await editContext.operations.addComponent(
127
128
  x.typeId,
128
129
  placeholder.key,
129
130
  index,
@@ -135,12 +136,12 @@ export function PlaceholderDropZone({
135
136
  if (insertOptions.length > 0)
136
137
  editContext.showContextMenu(e, [
137
138
  {
139
+ id: "header",
138
140
  label: insertMenuTitle || "Insert",
139
141
  disabled: true,
140
- className: "border-b border-gray-400 pb-2 pl-2 text-xs",
141
- template: () => {
142
- return <span className="text-sm text-black">Insert</span>;
143
- },
142
+ className:
143
+ "bg-gray-100 text-gray-900 font-medium py-1.5 text-sm",
144
+
144
145
  },
145
146
  ...insertOptions,
146
147
  ]);
@@ -26,16 +26,27 @@ export function PlaceholderDropZones({
26
26
 
27
27
  if (
28
28
  !pageViewContext ||
29
- (!editContext?.dragObject && !editContext.selectedForInsertion) ||
29
+ (!editContext?.dragObject &&
30
+ !editContext?.selectedForInsertion &&
31
+ !editContext?.insertMode) ||
30
32
  !placeholders
31
33
  )
32
34
  return null;
33
35
 
34
- const matchingPlaceholders = placeholders.filter(
36
+ const showAllDueToInsertMode =
37
+ !!editContext?.insertMode &&
38
+ !editContext?.dragObject &&
39
+ !editContext?.selectedForInsertion;
40
+
41
+ const matchingPlaceholders = (showAllDueToInsertMode
42
+ ? placeholders
43
+ : placeholders
44
+ ).filter(
35
45
  (x) =>
36
46
  // Only include editable placeholders
37
47
  x.editable &&
38
- (x.parentComponent?.id === editContext.selectedForInsertion ||
48
+ (showAllDueToInsertMode ||
49
+ x.parentComponent?.id === editContext.selectedForInsertion ||
39
50
  x.insertOptions?.find(
40
51
  (y) =>
41
52
  y.typeId === editContext.selectedForInsertion ||
@@ -9,6 +9,8 @@ import {
9
9
  EditContextType,
10
10
  useFieldsEditContext,
11
11
  } from "../client/editContext";
12
+ import type { SelectionRange } from "../client/editContext";
13
+ import type { FieldDescriptor } from "../../types";
12
14
  import {
13
15
  createOrUpdateComment,
14
16
  getAvailableCommentTags,
@@ -27,11 +29,15 @@ export function CommentPopover({
27
29
  editContext: externalEditContext,
28
30
  position,
29
31
  onClose,
32
+ initialField,
33
+ initialRange,
30
34
  }: {
31
35
  children?: React.ReactNode;
32
36
  editContext?: EditContextType;
33
37
  position?: { x: number; y: number };
34
38
  onClose?: () => void;
39
+ initialField?: FieldDescriptor;
40
+ initialRange?: SelectionRange;
35
41
  }) {
36
42
  const [isOpen, setIsOpen] = useState(!!position);
37
43
  const [saving, setSaving] = useState(false);
@@ -70,8 +76,12 @@ export function CommentPopover({
70
76
  const descriptor = editContext.contentEditorItem?.descriptor;
71
77
  if (!descriptor) return;
72
78
 
79
+ const snapshotField = initialField || fieldsContext?.focusedField;
80
+ const snapshotRange: SelectionRange | undefined =
81
+ initialRange || editContext.selectedRange;
82
+
73
83
  const itemId =
74
- fieldsContext?.focusedField?.item.id ||
84
+ snapshotField?.item.id ||
75
85
  (editContext.selection.length > 0
76
86
  ? editContext.selection[0]!
77
87
  : undefined) ||
@@ -82,10 +92,8 @@ export function CommentPopover({
82
92
  const version = descriptor.version;
83
93
 
84
94
  const getFieldName = async (): Promise<string | undefined> => {
85
- if (!fieldsContext?.focusedField) return undefined;
86
- const field = await editContext.itemsRepository.getField(
87
- fieldsContext.focusedField,
88
- );
95
+ if (!snapshotField) return undefined;
96
+ const field = await editContext.itemsRepository.getField(snapshotField);
89
97
  return field?.name;
90
98
  };
91
99
 
@@ -105,16 +113,16 @@ export function CommentPopover({
105
113
  isNew: false,
106
114
  itemId,
107
115
  itemName: await getItemName(),
108
- fieldId: fieldsContext?.focusedField?.fieldId,
116
+ fieldId: snapshotField?.fieldId,
109
117
  fieldName: await getFieldName(),
110
118
  mainItemId: descriptor.id,
111
119
  language,
112
120
  version,
113
121
  position: 0,
114
- rangeStart: editContext.selectedRange?.startOffset || 0,
115
- rangeEnd: editContext.selectedRange?.endOffset || 0,
122
+ rangeStart: snapshotRange?.startOffset || 0,
123
+ rangeEnd: snapshotRange?.endOffset || 0,
116
124
  author: editContext.user?.name,
117
- authorDisplayName: editContext.user?.displayName,
125
+ authorDisplayName: editContext.user?.fullName,
118
126
  date: new Date().toISOString(),
119
127
  text: text,
120
128
  tags: tags.join(","),
@@ -231,6 +239,7 @@ export function CommentPopover({
231
239
  export function showCommentPopoverAt(
232
240
  position: { x: number; y: number },
233
241
  editContext: EditContextType,
242
+ options?: { field?: FieldDescriptor; range?: SelectionRange },
234
243
  ) {
235
244
  const container = document.createElement("div");
236
245
  document.body.appendChild(container);
@@ -244,6 +253,8 @@ export function showCommentPopoverAt(
244
253
  editContext={editContext}
245
254
  position={position}
246
255
  onClose={handleClose}
256
+ initialField={options?.field}
257
+ initialRange={options?.range}
247
258
  />,
248
259
  );
249
260
  }
@@ -5,14 +5,14 @@ export function PreviewInfo() {
5
5
  const editContext = useEditContext();
6
6
  if (!editContext) return null;
7
7
  const review = editContext.reviews.reviews.find(
8
- (x) => x.reviewerEmail === editContext.user?.email
8
+ (x) => x.reviewerEmail === editContext.user?.email,
9
9
  );
10
10
 
11
11
  return (
12
- <div className="flex gap-3 items-center">
12
+ <div className="flex items-center gap-3">
13
13
  <div className="text-white">
14
- Welcome to your page <b>preview</b>, {editContext.user?.displayName}!
15
- <div className="text-gray-300 text-xs">
14
+ Welcome to your page <b>preview</b>, {editContext.user?.fullName}!
15
+ <div className="text-xs text-gray-300">
16
16
  {editContext.contentEditorItem?.path}{" "}
17
17
  {editContext.contentEditorItem?.language}{" "}
18
18
  {editContext.contentEditorItem?.version}
@@ -20,13 +20,13 @@ export function PreviewInfo() {
20
20
  </div>
21
21
  {review?.approvalDate && (
22
22
  <i
23
- className="pi pi-check text-green-400 text-2xl font-bold"
23
+ className="pi pi-check text-2xl font-bold text-green-400"
24
24
  title={"Approved " + formatDate(new Date(review.approvalDate))}
25
25
  ></i>
26
26
  )}
27
27
  {review?.rejectedDate && (
28
28
  <i
29
- className="pi pi-times text-red-400 text-2xl font-bold"
29
+ className="pi pi-times text-2xl font-bold text-red-400"
30
30
  title={"Rejected " + formatDate(new Date(review.rejectedDate))}
31
31
  ></i>
32
32
  )}
@@ -185,7 +185,10 @@ export function Reviews() {
185
185
  />
186
186
  </SimpleToolbar>
187
187
  <OverlayPanel ref={overlayPanelRef}>
188
- <div className="flex flex-col gap-2 p-2 text-xs">
188
+ <div
189
+ className="flex flex-col gap-2 p-2 text-xs"
190
+ data-testid="add-reviewer-popover"
191
+ >
189
192
  <div className="flex flex-col gap-2">
190
193
  <label htmlFor="name">Name</label>
191
194
 
@@ -265,7 +268,10 @@ export function Reviews() {
265
268
  </div>
266
269
  </OverlayPanel>
267
270
  <div className="relative flex-1">
268
- <div className="absolute inset-0 overflow-auto">
271
+ <div
272
+ className="absolute inset-0 overflow-auto"
273
+ data-testid="reviews-table"
274
+ >
269
275
  <SimpleTable
270
276
  columns={[
271
277
  {
@@ -403,14 +403,14 @@ export function RotateDeviceIcon({ className }: { className?: string }) {
403
403
  <path
404
404
  d="M10.24 9C10.8561 9 11.1641 9 11.3994 9.12456C11.6064 9.23413 11.7746 9.40897 11.8801 9.62401C12 9.86848 12 10.1885 12 10.8286L12 15.1714C12 15.8115 12 16.1315 11.8801 16.376C11.7746 16.591 11.6064 16.7659 11.3994 16.8754C11.1641 17 10.8561 17 10.24 17L2.76 17C2.14394 17 1.83591 17 1.60061 16.8754C1.39363 16.7659 1.22535 16.591 1.11989 16.376C0.999999 16.1315 0.999999 15.8115 0.999999 15.1714L1 10.8286C1 10.1885 1 9.86848 1.11989 9.62401C1.22535 9.40897 1.39363 9.23413 1.60061 9.12456C1.83591 9 2.14394 9 2.76 9L10.24 9Z"
405
405
  stroke="#111111"
406
- stroke-linecap="round"
407
- stroke-linejoin="round"
406
+ strokeLinecap="round"
407
+ strokeLinejoin="round"
408
408
  />
409
409
  <path
410
410
  d="M15 11L15 9.11111C15 7.24427 15 6.31085 14.6321 5.59781C14.3086 4.9706 13.7923 4.46067 13.1572 4.14109C12.4353 3.77778 11.4902 3.77778 9.6 3.77778L6 3.77778M6 3.77778L8.8125 6.55556M6 3.77778L8.8125 1"
411
411
  stroke="#111111"
412
- stroke-linecap="round"
413
- stroke-linejoin="round"
412
+ strokeLinecap="round"
413
+ strokeLinejoin="round"
414
414
  />
415
415
  </svg>
416
416
  );
package/src/revision.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const version = "1.0.4074";
2
- export const buildDate = "2025-09-05 09:17:08";
1
+ export const version = "1.0.4075";
2
+ export const buildDate = "2025-09-08 01:46:37";
package/src/types.ts CHANGED
@@ -158,7 +158,7 @@ export type UserInfo = {
158
158
 
159
159
  export type User = {
160
160
  name: string;
161
- displayName?: string;
161
+ fullName?: string;
162
162
  ai: boolean;
163
163
  email?: string;
164
164
  isLimitedPreviewUser?: boolean;