@liveblocks/react-tiptap 2.16.1-ai2 → 2.16.1-ai3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LiveblocksExtension.js +5 -4
- package/dist/LiveblocksExtension.js.map +1 -1
- package/dist/LiveblocksExtension.mjs +5 -4
- package/dist/LiveblocksExtension.mjs.map +1 -1
- package/dist/ai/AiExtension.js +174 -104
- package/dist/ai/AiExtension.js.map +1 -1
- package/dist/ai/AiExtension.mjs +175 -106
- package/dist/ai/AiExtension.mjs.map +1 -1
- package/dist/ai/AiToolbar.js +176 -84
- package/dist/ai/AiToolbar.js.map +1 -1
- package/dist/ai/AiToolbar.mjs +181 -89
- package/dist/ai/AiToolbar.mjs.map +1 -1
- package/dist/comments/FloatingComposer.js +1 -4
- package/dist/comments/FloatingComposer.js.map +1 -1
- package/dist/comments/FloatingComposer.mjs +2 -5
- package/dist/comments/FloatingComposer.mjs.map +1 -1
- package/dist/index.d.mts +42 -10
- package/dist/index.d.ts +42 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/toolbar/Toolbar.js +1 -1
- package/dist/toolbar/Toolbar.js.map +1 -1
- package/dist/toolbar/Toolbar.mjs +1 -1
- package/dist/toolbar/Toolbar.mjs.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils.js +85 -3
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +84 -3
- package/dist/utils.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +6 -6
- package/src/styles/index.css +115 -59
- package/src/styles/utils.css +6 -0
- package/styles.css +1 -1
- package/styles.css.map +1 -1
package/dist/ai/AiToolbar.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { hide, offset, autoUpdate, useFloating } from '@floating-ui/react-dom';
|
|
2
|
+
import { hide, offset, shift, limitShift, autoUpdate, useFloating } from '@floating-ui/react-dom';
|
|
3
3
|
import { useLayoutEffect } from '@liveblocks/react/_private';
|
|
4
|
-
import { CheckIcon, UndoIcon, CrossIcon, SparklesIcon, ShortcutTooltip, Button, SendIcon, EditIcon, ShortenIcon, LengthenIcon, QuestionMarkIcon, useRefs, TooltipProvider } from '@liveblocks/react-ui/_private';
|
|
4
|
+
import { CheckIcon, UndoIcon, CrossIcon, ArrowCornerDownRightIcon, SparklesIcon, ShortcutTooltip, Button, SendIcon, WarningIcon, EditIcon, ShortenIcon, LengthenIcon, SparklesTextIcon, QuestionMarkIcon, useRefs, TooltipProvider } from '@liveblocks/react-ui/_private';
|
|
5
5
|
import { useEditorState } from '@tiptap/react';
|
|
6
6
|
import { Command, useCommandState } from 'cmdk';
|
|
7
|
-
import { createContext, useContext, forwardRef, useCallback, useRef, useMemo, useEffect } from 'react';
|
|
7
|
+
import { createContext, useContext, forwardRef, useCallback, useRef, useMemo, useEffect, useState } from 'react';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import { classNames } from '../classnames.mjs';
|
|
10
10
|
import { useCurrentEditor, EditorProvider } from '../context.mjs';
|
|
11
|
-
import {
|
|
12
|
-
import { DEFAULT_STATE } from './AiExtension.mjs';
|
|
11
|
+
import { getDomRange } from '../utils.mjs';
|
|
12
|
+
import { isAiToolbarDiffOutput, DEFAULT_STATE } from './AiExtension.mjs';
|
|
13
13
|
|
|
14
14
|
const AI_TOOLBAR_COLLISION_PADDING = 10;
|
|
15
15
|
const AiToolbarContext = createContext(null);
|
|
@@ -43,26 +43,36 @@ function tiptapFloating(editor) {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
function flipToolbar() {
|
|
47
|
+
return {
|
|
48
|
+
name: "flipToolbar",
|
|
49
|
+
fn({ elements, middlewareData, rects }) {
|
|
50
|
+
const shiftOffsetY = middlewareData.shift?.y ?? 0;
|
|
51
|
+
if (Math.abs(shiftOffsetY) >= rects.floating.height) {
|
|
52
|
+
elements.floating.setAttribute("data-liveblocks-ai-toolbar-flip", "");
|
|
53
|
+
} else {
|
|
54
|
+
elements.floating.removeAttribute("data-liveblocks-ai-toolbar-flip");
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const AiToolbarDropdownSeparator = forwardRef(({ className, ...props }, forwardedRef) => {
|
|
61
|
+
return /* @__PURE__ */ jsx(Command.Separator, {
|
|
62
|
+
className: classNames("lb-dropdown-separator", className),
|
|
52
63
|
...props,
|
|
53
|
-
ref: forwardedRef
|
|
54
|
-
children
|
|
64
|
+
ref: forwardedRef
|
|
55
65
|
});
|
|
56
66
|
});
|
|
57
|
-
const
|
|
58
|
-
return /* @__PURE__ */ jsx(
|
|
67
|
+
const AiToolbarSuggestionsSeparator = forwardRef((props, forwardedRef) => {
|
|
68
|
+
return /* @__PURE__ */ jsx(AiToolbarDropdownSeparator, {
|
|
59
69
|
ref: forwardedRef,
|
|
60
70
|
...props
|
|
61
71
|
});
|
|
62
72
|
});
|
|
63
|
-
const AiToolbarDropdownItem = forwardRef(({ children, onSelect, icon, ...props }, forwardedRef) => {
|
|
73
|
+
const AiToolbarDropdownItem = forwardRef(({ children, onSelect, icon, className, ...props }, forwardedRef) => {
|
|
64
74
|
return /* @__PURE__ */ jsxs(Command.Item, {
|
|
65
|
-
className: "lb-dropdown-item",
|
|
75
|
+
className: classNames("lb-dropdown-item", className),
|
|
66
76
|
onSelect,
|
|
67
77
|
...props,
|
|
68
78
|
ref: forwardedRef,
|
|
@@ -78,6 +88,14 @@ const AiToolbarDropdownItem = forwardRef(({ children, onSelect, icon, ...props }
|
|
|
78
88
|
]
|
|
79
89
|
});
|
|
80
90
|
});
|
|
91
|
+
const AiToolbarSuggestionsLabel = forwardRef(({ children, className, ...props }, forwardedRef) => {
|
|
92
|
+
return /* @__PURE__ */ jsx("span", {
|
|
93
|
+
ref: forwardedRef,
|
|
94
|
+
className: classNames("lb-dropdown-label", className),
|
|
95
|
+
...props,
|
|
96
|
+
children
|
|
97
|
+
});
|
|
98
|
+
});
|
|
81
99
|
const AiToolbarSuggestion = forwardRef(({ prompt: manualPrompt, ...props }, forwardedRef) => {
|
|
82
100
|
const editor = useCurrentEditor("Suggestion", "AiToolbar");
|
|
83
101
|
const handleSelect = useCallback(
|
|
@@ -97,62 +115,56 @@ const AiToolbarSuggestion = forwardRef(({ prompt: manualPrompt, ...props }, forw
|
|
|
97
115
|
function AiToolbarReviewingSuggestions() {
|
|
98
116
|
const editor = useCurrentEditor("ReviewingSuggestions", "AiToolbar");
|
|
99
117
|
const { state } = useAiToolbarContext();
|
|
100
|
-
const { output } = state;
|
|
101
|
-
const
|
|
102
|
-
editor.commands.$
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}, [editor]);
|
|
107
|
-
if (output
|
|
118
|
+
const { prompt, output } = state;
|
|
119
|
+
const retry = useCallback(() => {
|
|
120
|
+
editor.commands.$startAiToolbarThinking(
|
|
121
|
+
prompt,
|
|
122
|
+
false
|
|
123
|
+
);
|
|
124
|
+
}, [editor, prompt]);
|
|
125
|
+
if (isAiToolbarDiffOutput(output)) {
|
|
108
126
|
return /* @__PURE__ */ jsxs(Fragment, {
|
|
109
127
|
children: [
|
|
110
128
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
111
129
|
icon: /* @__PURE__ */ jsx(CheckIcon, {}),
|
|
112
|
-
onSelect:
|
|
130
|
+
onSelect: editor.commands.$acceptAiToolbarOutput,
|
|
113
131
|
children: "Accept"
|
|
114
132
|
}),
|
|
115
133
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
116
134
|
icon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
117
|
-
|
|
135
|
+
onSelect: retry,
|
|
118
136
|
children: "Try again"
|
|
119
137
|
}),
|
|
120
138
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
121
139
|
icon: /* @__PURE__ */ jsx(CrossIcon, {}),
|
|
122
|
-
onSelect:
|
|
140
|
+
onSelect: editor.commands.$closeAiToolbar,
|
|
123
141
|
children: "Discard"
|
|
124
142
|
})
|
|
125
143
|
]
|
|
126
144
|
});
|
|
127
|
-
} else
|
|
145
|
+
} else {
|
|
128
146
|
return /* @__PURE__ */ jsxs(Fragment, {
|
|
129
147
|
children: [
|
|
130
148
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
131
|
-
icon: /* @__PURE__ */ jsx(
|
|
132
|
-
|
|
133
|
-
children: "Replace selection"
|
|
134
|
-
}),
|
|
135
|
-
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
136
|
-
icon: /* @__PURE__ */ jsx(CheckIcon, {}),
|
|
137
|
-
disabled: true,
|
|
149
|
+
icon: /* @__PURE__ */ jsx(ArrowCornerDownRightIcon, {}),
|
|
150
|
+
onSelect: editor.commands.$acceptAiToolbarOutput,
|
|
138
151
|
children: "Insert below"
|
|
139
152
|
}),
|
|
140
153
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
141
154
|
icon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
142
|
-
|
|
155
|
+
onSelect: retry,
|
|
143
156
|
children: "Try again"
|
|
144
157
|
}),
|
|
145
158
|
/* @__PURE__ */ jsx(AiToolbarDropdownItem, {
|
|
146
159
|
icon: /* @__PURE__ */ jsx(CrossIcon, {}),
|
|
147
|
-
onSelect:
|
|
160
|
+
onSelect: editor.commands.$closeAiToolbar,
|
|
148
161
|
children: "Discard"
|
|
149
162
|
})
|
|
150
163
|
]
|
|
151
164
|
});
|
|
152
165
|
}
|
|
153
|
-
return null;
|
|
154
166
|
}
|
|
155
|
-
function AiToolbarCustomPromptContent(
|
|
167
|
+
function AiToolbarCustomPromptContent() {
|
|
156
168
|
const editor = useCurrentEditor("CustomPromptContent", "AiToolbar");
|
|
157
169
|
const aiName = editor.storage.liveblocksAi.name;
|
|
158
170
|
const textAreaRef = useRef(null);
|
|
@@ -194,7 +206,8 @@ function AiToolbarCustomPromptContent({ disabled }) {
|
|
|
194
206
|
selectedDropdownItem.click();
|
|
195
207
|
} else if (!isCustomPromptEmpty) {
|
|
196
208
|
editor.commands.$startAiToolbarThinking(
|
|
197
|
-
customPrompt
|
|
209
|
+
customPrompt,
|
|
210
|
+
state.phase === "reviewing"
|
|
198
211
|
);
|
|
199
212
|
}
|
|
200
213
|
}
|
|
@@ -213,9 +226,10 @@ function AiToolbarCustomPromptContent({ disabled }) {
|
|
|
213
226
|
return;
|
|
214
227
|
}
|
|
215
228
|
editor.commands.$startAiToolbarThinking(
|
|
216
|
-
customPrompt
|
|
229
|
+
customPrompt,
|
|
230
|
+
state.phase === "reviewing"
|
|
217
231
|
);
|
|
218
|
-
}, [editor, customPrompt, isCustomPromptEmpty]);
|
|
232
|
+
}, [editor, customPrompt, isCustomPromptEmpty, state.phase]);
|
|
219
233
|
return /* @__PURE__ */ jsxs("div", {
|
|
220
234
|
className: "lb-tiptap-ai-toolbar-content",
|
|
221
235
|
children: [
|
|
@@ -236,8 +250,7 @@ function AiToolbarCustomPromptContent({ disabled }) {
|
|
|
236
250
|
placeholder: `Ask ${aiName} anything\u2026`,
|
|
237
251
|
onKeyDown: handlePromptKeyDown,
|
|
238
252
|
rows: 1,
|
|
239
|
-
autoFocus: true
|
|
240
|
-
disabled
|
|
253
|
+
autoFocus: true
|
|
241
254
|
})
|
|
242
255
|
})
|
|
243
256
|
}),
|
|
@@ -251,7 +264,7 @@ function AiToolbarCustomPromptContent({ disabled }) {
|
|
|
251
264
|
variant: "primary",
|
|
252
265
|
"aria-label": `Ask ${aiName}`,
|
|
253
266
|
icon: /* @__PURE__ */ jsx(SendIcon, {}),
|
|
254
|
-
disabled: isCustomPromptEmpty
|
|
267
|
+
disabled: isCustomPromptEmpty,
|
|
255
268
|
onClick: handleSendClick
|
|
256
269
|
})
|
|
257
270
|
})
|
|
@@ -260,7 +273,23 @@ function AiToolbarCustomPromptContent({ disabled }) {
|
|
|
260
273
|
});
|
|
261
274
|
}
|
|
262
275
|
function AiToolbarAsking() {
|
|
263
|
-
|
|
276
|
+
const { state } = useAiToolbarContext();
|
|
277
|
+
const { error } = state;
|
|
278
|
+
return /* @__PURE__ */ jsxs(Fragment, {
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ jsx(AiToolbarCustomPromptContent, {}),
|
|
281
|
+
error ? /* @__PURE__ */ jsxs("div", {
|
|
282
|
+
className: "lb-tiptap-ai-toolbar-error",
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ jsx("span", {
|
|
285
|
+
className: "lb-icon-container",
|
|
286
|
+
children: /* @__PURE__ */ jsx(WarningIcon, {})
|
|
287
|
+
}),
|
|
288
|
+
"There was a problem with your request."
|
|
289
|
+
]
|
|
290
|
+
}) : null
|
|
291
|
+
]
|
|
292
|
+
});
|
|
264
293
|
}
|
|
265
294
|
function AiToolbarThinking() {
|
|
266
295
|
const editor = useCurrentEditor("AiToolbarThinking", "AiToolbar");
|
|
@@ -320,9 +349,7 @@ function AiToolbarReviewing() {
|
|
|
320
349
|
children: output.text
|
|
321
350
|
})
|
|
322
351
|
}) : null,
|
|
323
|
-
/* @__PURE__ */ jsx(AiToolbarCustomPromptContent, {
|
|
324
|
-
disabled: true
|
|
325
|
-
})
|
|
352
|
+
/* @__PURE__ */ jsx(AiToolbarCustomPromptContent, {})
|
|
326
353
|
]
|
|
327
354
|
});
|
|
328
355
|
}
|
|
@@ -363,7 +390,12 @@ function AiToolbarContainer({
|
|
|
363
390
|
};
|
|
364
391
|
}, [editor, state.phase]);
|
|
365
392
|
return /* @__PURE__ */ jsxs(AiToolbarContext.Provider, {
|
|
366
|
-
value: {
|
|
393
|
+
value: {
|
|
394
|
+
state,
|
|
395
|
+
toolbarRef,
|
|
396
|
+
dropdownRef,
|
|
397
|
+
isDropdownHidden
|
|
398
|
+
},
|
|
367
399
|
children: [
|
|
368
400
|
/* @__PURE__ */ jsxs("div", {
|
|
369
401
|
className: "lb-tiptap-ai-toolbar-container",
|
|
@@ -398,40 +430,42 @@ function AiToolbarContainer({
|
|
|
398
430
|
}
|
|
399
431
|
const defaultSuggestions = /* @__PURE__ */ jsxs(Fragment, {
|
|
400
432
|
children: [
|
|
401
|
-
/* @__PURE__ */
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
icon: /* @__PURE__ */ jsx(EditIcon, {}),
|
|
406
|
-
children: "Improve writing"
|
|
407
|
-
}),
|
|
408
|
-
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
409
|
-
icon: /* @__PURE__ */ jsx(CheckIcon, {}),
|
|
410
|
-
children: "Fix mistakes"
|
|
411
|
-
}),
|
|
412
|
-
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
413
|
-
icon: /* @__PURE__ */ jsx(ShortenIcon, {}),
|
|
414
|
-
children: "Simplify"
|
|
415
|
-
}),
|
|
416
|
-
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
417
|
-
icon: /* @__PURE__ */ jsx(LengthenIcon, {}),
|
|
418
|
-
children: "Add more detail"
|
|
419
|
-
})
|
|
420
|
-
]
|
|
433
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
434
|
+
icon: /* @__PURE__ */ jsx(EditIcon, {}),
|
|
435
|
+
prompt: "Improve the quality of the text",
|
|
436
|
+
children: "Improve writing"
|
|
421
437
|
}),
|
|
422
|
-
/* @__PURE__ */ jsx(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
438
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
439
|
+
icon: /* @__PURE__ */ jsx(CheckIcon, {}),
|
|
440
|
+
prompt: "Fix spelling & grammar errors in the text",
|
|
441
|
+
children: "Fix mistakes"
|
|
442
|
+
}),
|
|
443
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
444
|
+
icon: /* @__PURE__ */ jsx(ShortenIcon, {}),
|
|
445
|
+
prompt: "Shorten the text, simplifying it",
|
|
446
|
+
children: "Simplify"
|
|
447
|
+
}),
|
|
448
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
449
|
+
icon: /* @__PURE__ */ jsx(LengthenIcon, {}),
|
|
450
|
+
prompt: "Lengthen the text, going into more detail",
|
|
451
|
+
children: "Add more detail"
|
|
452
|
+
}),
|
|
453
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestionsSeparator, {}),
|
|
454
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
455
|
+
icon: /* @__PURE__ */ jsx(SparklesTextIcon, {}),
|
|
456
|
+
prompt: "Continue writing from the text's end",
|
|
457
|
+
children: "Continue writing"
|
|
458
|
+
}),
|
|
459
|
+
/* @__PURE__ */ jsx(AiToolbarSuggestion, {
|
|
460
|
+
icon: /* @__PURE__ */ jsx(QuestionMarkIcon, {}),
|
|
461
|
+
prompt: "Explain what the text is about",
|
|
462
|
+
children: "Explain"
|
|
428
463
|
})
|
|
429
464
|
]
|
|
430
465
|
});
|
|
431
466
|
const AiToolbar = Object.assign(
|
|
432
467
|
forwardRef(
|
|
433
468
|
({
|
|
434
|
-
position = "bottom",
|
|
435
469
|
offset: sideOffset = 6,
|
|
436
470
|
editor,
|
|
437
471
|
className,
|
|
@@ -444,18 +478,25 @@ const AiToolbar = Object.assign(
|
|
|
444
478
|
return ctx.editor?.storage.liveblocksAi?.state;
|
|
445
479
|
}
|
|
446
480
|
}) ?? DEFAULT_STATE;
|
|
447
|
-
const selection =
|
|
481
|
+
const selection = editor?.state.selection;
|
|
448
482
|
const floatingOptions = useMemo(() => {
|
|
449
483
|
const detectOverflowOptions = {
|
|
450
484
|
padding: AI_TOOLBAR_COLLISION_PADDING
|
|
451
485
|
};
|
|
452
486
|
return {
|
|
453
487
|
strategy: "fixed",
|
|
454
|
-
placement:
|
|
488
|
+
placement: "bottom",
|
|
455
489
|
middleware: [
|
|
456
490
|
tiptapFloating(editor),
|
|
457
491
|
hide(detectOverflowOptions),
|
|
458
|
-
offset(sideOffset)
|
|
492
|
+
offset(sideOffset),
|
|
493
|
+
shift({
|
|
494
|
+
...detectOverflowOptions,
|
|
495
|
+
mainAxis: false,
|
|
496
|
+
crossAxis: true,
|
|
497
|
+
limiter: limitShift()
|
|
498
|
+
}),
|
|
499
|
+
flipToolbar()
|
|
459
500
|
],
|
|
460
501
|
whileElementsMounted: (...args) => {
|
|
461
502
|
return autoUpdate(...args, {
|
|
@@ -463,7 +504,7 @@ const AiToolbar = Object.assign(
|
|
|
463
504
|
});
|
|
464
505
|
}
|
|
465
506
|
};
|
|
466
|
-
}, [editor,
|
|
507
|
+
}, [editor, sideOffset]);
|
|
467
508
|
const isOpen = selection !== void 0 && state.phase !== "closed";
|
|
468
509
|
const {
|
|
469
510
|
refs: { setReference, setFloating },
|
|
@@ -478,6 +519,28 @@ const AiToolbar = Object.assign(
|
|
|
478
519
|
const toolbarRef = useRef(null);
|
|
479
520
|
const mergedRefs = useRefs(forwardedRef, toolbarRef, setFloating);
|
|
480
521
|
const dropdownRef = useRef(null);
|
|
522
|
+
const [selectedDropdownValue, setSelectedDropdownValue] = useState("");
|
|
523
|
+
useEffect(() => {
|
|
524
|
+
if (state.phase === "closed") {
|
|
525
|
+
setSelectedDropdownValue("");
|
|
526
|
+
}
|
|
527
|
+
}, [state.phase]);
|
|
528
|
+
useEffect(() => {
|
|
529
|
+
if (state.phase === "closed") {
|
|
530
|
+
setSelectedDropdownValue("");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const selectedDropdownItem = dropdownRef.current?.querySelector(
|
|
534
|
+
"[role='option'][data-selected='true']"
|
|
535
|
+
);
|
|
536
|
+
if (selectedDropdownItem) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const firstDropdownItem = dropdownRef.current?.querySelector("[role='option']");
|
|
540
|
+
setSelectedDropdownValue(
|
|
541
|
+
firstDropdownItem?.dataset.value ?? ""
|
|
542
|
+
);
|
|
543
|
+
}, [state.phase, dropdownRef, setSelectedDropdownValue]);
|
|
481
544
|
useEffect(() => {
|
|
482
545
|
if (!editor) {
|
|
483
546
|
return;
|
|
@@ -492,14 +555,40 @@ const AiToolbar = Object.assign(
|
|
|
492
555
|
}
|
|
493
556
|
setReference(null);
|
|
494
557
|
setTimeout(() => {
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
558
|
+
if (state.phase === "reviewing" && isAiToolbarDiffOutput(state.output)) {
|
|
559
|
+
const changes = editor.view.dom.querySelectorAll(
|
|
560
|
+
"ychange[data-liveblocks]"
|
|
561
|
+
);
|
|
562
|
+
setReference({
|
|
563
|
+
getBoundingClientRect: () => {
|
|
564
|
+
const rects = [];
|
|
565
|
+
changes.forEach((change) => {
|
|
566
|
+
rects.push(change.getBoundingClientRect());
|
|
567
|
+
});
|
|
568
|
+
const minX = Math.min(...rects.map((rect) => rect.left));
|
|
569
|
+
const minY = Math.min(...rects.map((rect) => rect.top));
|
|
570
|
+
const maxX = Math.max(...rects.map((rect) => rect.right));
|
|
571
|
+
const maxY = Math.max(...rects.map((rect) => rect.bottom));
|
|
572
|
+
return {
|
|
573
|
+
x: minX,
|
|
574
|
+
y: minY,
|
|
575
|
+
width: maxX - minX,
|
|
576
|
+
height: maxY - minY,
|
|
577
|
+
top: minY,
|
|
578
|
+
left: minX,
|
|
579
|
+
bottom: maxY,
|
|
580
|
+
right: maxX
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
} else if (selection) {
|
|
585
|
+
const domRange = getDomRange(editor, selection);
|
|
499
586
|
setReference(domRange);
|
|
587
|
+
} else {
|
|
588
|
+
setReference(null);
|
|
500
589
|
}
|
|
501
590
|
}, 0);
|
|
502
|
-
}, [selection, editor, isOpen, setReference]);
|
|
591
|
+
}, [selection, editor, isOpen, setReference, state.phase, state.output]);
|
|
503
592
|
useEffect(() => {
|
|
504
593
|
if (!editor || !isOpen) {
|
|
505
594
|
return;
|
|
@@ -541,6 +630,8 @@ const AiToolbar = Object.assign(
|
|
|
541
630
|
left: 0,
|
|
542
631
|
transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)"
|
|
543
632
|
},
|
|
633
|
+
value: selectedDropdownValue,
|
|
634
|
+
onValueChange: setSelectedDropdownValue,
|
|
544
635
|
...props,
|
|
545
636
|
children: /* @__PURE__ */ jsx(AiToolbarContainer, {
|
|
546
637
|
state,
|
|
@@ -558,8 +649,9 @@ const AiToolbar = Object.assign(
|
|
|
558
649
|
}
|
|
559
650
|
),
|
|
560
651
|
{
|
|
561
|
-
|
|
562
|
-
|
|
652
|
+
Suggestion: AiToolbarSuggestion,
|
|
653
|
+
SuggestionsLabel: AiToolbarSuggestionsLabel,
|
|
654
|
+
SuggestionsSeparator: AiToolbarSuggestionsSeparator
|
|
563
655
|
}
|
|
564
656
|
);
|
|
565
657
|
|