@liveblocks/react-tiptap 2.17.0-usrnotsettings3 → 2.17.0

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 (47) hide show
  1. package/dist/LiveblocksExtension.js +116 -21
  2. package/dist/LiveblocksExtension.js.map +1 -1
  3. package/dist/LiveblocksExtension.mjs +114 -19
  4. package/dist/LiveblocksExtension.mjs.map +1 -1
  5. package/dist/ai/AiExtension.js +382 -0
  6. package/dist/ai/AiExtension.js.map +1 -0
  7. package/dist/ai/AiExtension.mjs +378 -0
  8. package/dist/ai/AiExtension.mjs.map +1 -0
  9. package/dist/ai/AiToolbar.js +663 -0
  10. package/dist/ai/AiToolbar.js.map +1 -0
  11. package/dist/ai/AiToolbar.mjs +660 -0
  12. package/dist/ai/AiToolbar.mjs.map +1 -0
  13. package/dist/comments/CommentsExtension.js.map +1 -1
  14. package/dist/comments/CommentsExtension.mjs.map +1 -1
  15. package/dist/comments/FloatingComposer.js +2 -2
  16. package/dist/comments/FloatingComposer.js.map +1 -1
  17. package/dist/comments/FloatingComposer.mjs +2 -2
  18. package/dist/comments/FloatingComposer.mjs.map +1 -1
  19. package/dist/index.d.mts +107 -15
  20. package/dist/index.d.ts +107 -15
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +1 -0
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/toolbar/FloatingToolbar.js +7 -0
  26. package/dist/toolbar/FloatingToolbar.js.map +1 -1
  27. package/dist/toolbar/FloatingToolbar.mjs +7 -0
  28. package/dist/toolbar/FloatingToolbar.mjs.map +1 -1
  29. package/dist/toolbar/Toolbar.js +35 -1
  30. package/dist/toolbar/Toolbar.js.map +1 -1
  31. package/dist/toolbar/Toolbar.mjs +36 -2
  32. package/dist/toolbar/Toolbar.mjs.map +1 -1
  33. package/dist/types.js.map +1 -1
  34. package/dist/types.mjs.map +1 -1
  35. package/dist/utils.js +118 -6
  36. package/dist/utils.js.map +1 -1
  37. package/dist/utils.mjs +116 -7
  38. package/dist/utils.mjs.map +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/version.js.map +1 -1
  41. package/dist/version.mjs +1 -1
  42. package/dist/version.mjs.map +1 -1
  43. package/package.json +7 -6
  44. package/src/styles/index.css +380 -3
  45. package/src/styles/utils.css +6 -0
  46. package/styles.css +1 -1
  47. package/styles.css.map +1 -1
@@ -0,0 +1,663 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var reactDom = require('@floating-ui/react-dom');
5
+ var _private$1 = require('@liveblocks/react/_private');
6
+ var _private = require('@liveblocks/react-ui/_private');
7
+ var react$1 = require('@tiptap/react');
8
+ var cmdk = require('cmdk');
9
+ var react = require('react');
10
+ var reactDom$1 = require('react-dom');
11
+ var classnames = require('../classnames.js');
12
+ var context = require('../context.js');
13
+ var utils = require('../utils.js');
14
+ var AiExtension = require('./AiExtension.js');
15
+
16
+ const AI_TOOLBAR_COLLISION_PADDING = 10;
17
+ const AiToolbarContext = react.createContext(null);
18
+ function useAiToolbarContext() {
19
+ const context = react.useContext(AiToolbarContext);
20
+ if (!context) {
21
+ throw new Error("useAiToolbarContext must be used within an AiToolbar");
22
+ }
23
+ return context;
24
+ }
25
+ function tiptapFloating(editor) {
26
+ return {
27
+ name: "tiptap",
28
+ options: editor,
29
+ fn({ elements }) {
30
+ if (!editor) {
31
+ return {};
32
+ }
33
+ const editorRect = editor.view.dom.getBoundingClientRect();
34
+ elements.floating.style.setProperty(
35
+ "--lb-tiptap-editor-width",
36
+ `${editorRect.width}px`
37
+ );
38
+ elements.floating.style.setProperty(
39
+ "--lb-tiptap-editor-height",
40
+ `${editorRect.height}px`
41
+ );
42
+ return {
43
+ x: editorRect.x
44
+ };
45
+ }
46
+ };
47
+ }
48
+ function flipToolbar() {
49
+ return {
50
+ name: "flipToolbar",
51
+ fn({ elements, middlewareData, rects }) {
52
+ const shiftOffsetY = middlewareData.shift?.y ?? 0;
53
+ if (Math.abs(shiftOffsetY) >= rects.floating.height) {
54
+ elements.floating.setAttribute("data-liveblocks-ai-toolbar-flip", "");
55
+ } else {
56
+ elements.floating.removeAttribute("data-liveblocks-ai-toolbar-flip");
57
+ }
58
+ return {};
59
+ }
60
+ };
61
+ }
62
+ const AiToolbarDropdownSeparator = react.forwardRef(({ className, ...props }, forwardedRef) => {
63
+ return /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.Separator, {
64
+ className: classnames.classNames("lb-dropdown-separator", className),
65
+ ...props,
66
+ ref: forwardedRef
67
+ });
68
+ });
69
+ const AiToolbarSuggestionsSeparator = react.forwardRef((props, forwardedRef) => {
70
+ return /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownSeparator, {
71
+ ref: forwardedRef,
72
+ ...props
73
+ });
74
+ });
75
+ const AiToolbarDropdownItem = react.forwardRef(({ children, onSelect, icon, className, ...props }, forwardedRef) => {
76
+ return /* @__PURE__ */ jsxRuntime.jsxs(cmdk.Command.Item, {
77
+ className: classnames.classNames("lb-dropdown-item", className),
78
+ onSelect,
79
+ ...props,
80
+ ref: forwardedRef,
81
+ children: [
82
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("span", {
83
+ className: "lb-icon-container",
84
+ children: icon
85
+ }) : null,
86
+ children ? /* @__PURE__ */ jsxRuntime.jsx("span", {
87
+ className: "lb-dropdown-item-label",
88
+ children
89
+ }) : null
90
+ ]
91
+ });
92
+ });
93
+ const AiToolbarSuggestionsLabel = react.forwardRef(({ children, className, ...props }, forwardedRef) => {
94
+ return /* @__PURE__ */ jsxRuntime.jsx("span", {
95
+ ref: forwardedRef,
96
+ className: classnames.classNames("lb-dropdown-label", className),
97
+ ...props,
98
+ children
99
+ });
100
+ });
101
+ const AiToolbarSuggestion = react.forwardRef(({ prompt: manualPrompt, ...props }, forwardedRef) => {
102
+ const editor = context.useCurrentEditor("Suggestion", "AiToolbar");
103
+ const handleSelect = react.useCallback(
104
+ (prompt) => {
105
+ editor.commands.$startAiToolbarThinking(
106
+ manualPrompt ?? prompt
107
+ );
108
+ },
109
+ [editor, manualPrompt]
110
+ );
111
+ return /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
112
+ ...props,
113
+ onSelect: handleSelect,
114
+ ref: forwardedRef
115
+ });
116
+ });
117
+ function AiToolbarReviewingSuggestions() {
118
+ const editor = context.useCurrentEditor("ReviewingSuggestions", "AiToolbar");
119
+ const { state } = useAiToolbarContext();
120
+ const { response } = state;
121
+ if (AiExtension.isContextualPromptDiffResponse(response)) {
122
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
123
+ children: [
124
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
125
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
126
+ onSelect: editor.commands.$acceptAiToolbarResponse,
127
+ children: "Accept"
128
+ }),
129
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
130
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
131
+ onSelect: editor.commands.$startAiToolbarThinking,
132
+ children: "Try again"
133
+ }),
134
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
135
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CrossIcon, {}),
136
+ onSelect: editor.commands.$closeAiToolbar,
137
+ children: "Discard"
138
+ })
139
+ ]
140
+ });
141
+ } else {
142
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
143
+ children: [
144
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
145
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.ArrowCornerDownRightIcon, {}),
146
+ onSelect: editor.commands.$acceptAiToolbarResponse,
147
+ children: "Insert below"
148
+ }),
149
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
150
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
151
+ onSelect: editor.commands.$startAiToolbarThinking,
152
+ children: "Try again"
153
+ }),
154
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
155
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CrossIcon, {}),
156
+ onSelect: editor.commands.$closeAiToolbar,
157
+ children: "Discard"
158
+ })
159
+ ]
160
+ });
161
+ }
162
+ }
163
+ function AiToolbarCustomPromptContent() {
164
+ const editor = context.useCurrentEditor("CustomPromptContent", "AiToolbar");
165
+ const aiName = editor.storage.liveblocksAi.name;
166
+ const textAreaRef = react.useRef(null);
167
+ const { state, dropdownRef, isDropdownHidden } = useAiToolbarContext();
168
+ const { customPrompt } = state;
169
+ const isCustomPromptEmpty = react.useMemo(
170
+ () => customPrompt.trim() === "",
171
+ [customPrompt]
172
+ );
173
+ _private$1.useLayoutEffect(
174
+ () => {
175
+ setTimeout(() => {
176
+ const textArea = textAreaRef.current;
177
+ if (!textArea) {
178
+ return;
179
+ }
180
+ textArea.focus();
181
+ textArea.setSelectionRange(
182
+ textArea.value.length,
183
+ textArea.value.length
184
+ );
185
+ }, 0);
186
+ },
187
+ []
188
+ );
189
+ const handlePromptKeyDown = (event) => {
190
+ if (event.key === "Enter") {
191
+ event.preventDefault();
192
+ event.stopPropagation();
193
+ if (event.shiftKey) {
194
+ editor.commands._updateAiToolbarCustomPrompt(
195
+ (customPrompt2) => customPrompt2 + "\n"
196
+ );
197
+ } else {
198
+ const selectedDropdownItem = dropdownRef.current?.querySelector(
199
+ "[role='option'][data-selected='true']"
200
+ );
201
+ if (!isDropdownHidden && selectedDropdownItem) {
202
+ selectedDropdownItem.click();
203
+ } else if (!isCustomPromptEmpty) {
204
+ editor.commands.$startAiToolbarThinking(
205
+ customPrompt,
206
+ state.phase === "reviewing"
207
+ );
208
+ }
209
+ }
210
+ }
211
+ };
212
+ const handleCustomPromptChange = react.useCallback(
213
+ (customPrompt2) => {
214
+ editor.commands._updateAiToolbarCustomPrompt(
215
+ customPrompt2
216
+ );
217
+ },
218
+ [editor]
219
+ );
220
+ const handleSendClick = react.useCallback(() => {
221
+ if (isCustomPromptEmpty) {
222
+ return;
223
+ }
224
+ editor.commands.$startAiToolbarThinking(
225
+ customPrompt,
226
+ state.phase === "reviewing"
227
+ );
228
+ }, [editor, customPrompt, isCustomPromptEmpty, state.phase]);
229
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", {
230
+ className: "lb-tiptap-ai-toolbar-content",
231
+ children: [
232
+ /* @__PURE__ */ jsxRuntime.jsx("span", {
233
+ className: "lb-icon-container lb-tiptap-ai-toolbar-icon-container",
234
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.SparklesIcon, {})
235
+ }),
236
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
237
+ className: "lb-tiptap-ai-toolbar-custom-prompt-container",
238
+ "data-value": customPrompt,
239
+ children: /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.Input, {
240
+ value: customPrompt,
241
+ onValueChange: handleCustomPromptChange,
242
+ asChild: true,
243
+ children: /* @__PURE__ */ jsxRuntime.jsx("textarea", {
244
+ ref: textAreaRef,
245
+ className: "lb-tiptap-ai-toolbar-custom-prompt",
246
+ placeholder: `Ask ${aiName} anything\u2026`,
247
+ onKeyDown: handlePromptKeyDown,
248
+ rows: 1,
249
+ autoFocus: true
250
+ })
251
+ })
252
+ }),
253
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
254
+ className: "lb-tiptap-ai-toolbar-actions",
255
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortcutTooltip, {
256
+ content: `Ask ${aiName}`,
257
+ shortcut: "Enter",
258
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.Button, {
259
+ className: "lb-tiptap-ai-toolbar-action",
260
+ variant: "primary",
261
+ "aria-label": `Ask ${aiName}`,
262
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.SendIcon, {}),
263
+ disabled: isCustomPromptEmpty,
264
+ onClick: handleSendClick
265
+ })
266
+ })
267
+ })
268
+ ]
269
+ });
270
+ }
271
+ function AiToolbarAsking() {
272
+ const { state } = useAiToolbarContext();
273
+ const { error } = state;
274
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
275
+ children: [
276
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarCustomPromptContent, {}),
277
+ error ? /* @__PURE__ */ jsxRuntime.jsxs("div", {
278
+ className: "lb-tiptap-ai-toolbar-error",
279
+ children: [
280
+ /* @__PURE__ */ jsxRuntime.jsx("span", {
281
+ className: "lb-icon-container",
282
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.WarningIcon, {})
283
+ }),
284
+ "There was a problem with your request."
285
+ ]
286
+ }) : null
287
+ ]
288
+ });
289
+ }
290
+ function AiToolbarThinking() {
291
+ const editor = context.useCurrentEditor("AiToolbarThinking", "AiToolbar");
292
+ const contentRef = react.useRef(null);
293
+ const aiName = editor.storage.liveblocksAi.name;
294
+ const handleCancel = react.useCallback(() => {
295
+ editor.commands.$cancelAiToolbarThinking();
296
+ }, [editor]);
297
+ _private$1.useLayoutEffect(() => {
298
+ contentRef.current?.focus();
299
+ window.getSelection()?.removeAllRanges();
300
+ }, []);
301
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {
302
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
303
+ className: "lb-tiptap-ai-toolbar-content",
304
+ tabIndex: 0,
305
+ ref: contentRef,
306
+ children: [
307
+ /* @__PURE__ */ jsxRuntime.jsx("span", {
308
+ className: "lb-icon-container lb-tiptap-ai-toolbar-icon-container",
309
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.SparklesIcon, {})
310
+ }),
311
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
312
+ className: "lb-tiptap-ai-toolbar-thinking",
313
+ children: [
314
+ aiName,
315
+ " is thinking\u2026"
316
+ ]
317
+ }),
318
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
319
+ className: "lb-tiptap-ai-toolbar-actions",
320
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortcutTooltip, {
321
+ content: "Cancel",
322
+ shortcut: "Escape",
323
+ children: /* @__PURE__ */ jsxRuntime.jsx(_private.Button, {
324
+ className: "lb-tiptap-ai-toolbar-action",
325
+ variant: "secondary",
326
+ "aria-label": "Cancel",
327
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
328
+ onClick: handleCancel
329
+ })
330
+ })
331
+ })
332
+ ]
333
+ })
334
+ });
335
+ }
336
+ function AiToolbarReviewing() {
337
+ const { state } = useAiToolbarContext();
338
+ const { response } = state;
339
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
340
+ children: [
341
+ response.type === "other" ? /* @__PURE__ */ jsxRuntime.jsx("div", {
342
+ className: "lb-tiptap-ai-toolbar-response-container",
343
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", {
344
+ className: "lb-tiptap-ai-toolbar-response",
345
+ children: response.text
346
+ })
347
+ }) : null,
348
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarCustomPromptContent, {})
349
+ ]
350
+ });
351
+ }
352
+ function AiToolbarContainer({
353
+ state,
354
+ toolbarRef,
355
+ dropdownRef,
356
+ children
357
+ }) {
358
+ const editor = context.useCurrentEditor("AiToolbarContainer", "AiToolbar");
359
+ const customPrompt = state.customPrompt;
360
+ const isCustomPromptMultiline = react.useMemo(
361
+ () => customPrompt?.includes("\n"),
362
+ [customPrompt]
363
+ );
364
+ const hasDropdownItems = cmdk.useCommandState(
365
+ (state2) => state2.filtered.count > 0
366
+ );
367
+ const isDropdownHidden = isCustomPromptMultiline || !hasDropdownItems;
368
+ react.useEffect(() => {
369
+ if (!editor) {
370
+ return;
371
+ }
372
+ const handleKeyDown = (event) => {
373
+ if (!event.defaultPrevented && event.key === "Escape") {
374
+ event.preventDefault();
375
+ event.stopPropagation();
376
+ if (state.phase === "thinking") {
377
+ editor.commands.$cancelAiToolbarThinking();
378
+ } else {
379
+ editor.chain().$closeAiToolbar().focus().run();
380
+ }
381
+ }
382
+ };
383
+ document.addEventListener("keydown", handleKeyDown);
384
+ return () => {
385
+ document.removeEventListener("keydown", handleKeyDown);
386
+ };
387
+ }, [editor, state.phase]);
388
+ return /* @__PURE__ */ jsxRuntime.jsxs(AiToolbarContext.Provider, {
389
+ value: {
390
+ state,
391
+ toolbarRef,
392
+ dropdownRef,
393
+ isDropdownHidden
394
+ },
395
+ children: [
396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
397
+ className: "lb-tiptap-ai-toolbar-container",
398
+ children: [
399
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
400
+ className: "lb-elevation lb-tiptap-ai-toolbar",
401
+ children: state.phase === "asking" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarAsking, {}) : state.phase === "thinking" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarThinking, {}) : state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarReviewing, {}) : null
402
+ }),
403
+ /* @__PURE__ */ jsxRuntime.jsxs("div", {
404
+ className: "lb-tiptap-ai-toolbar-halo",
405
+ "data-active": state.phase === "thinking" ? "" : void 0,
406
+ "aria-hidden": true,
407
+ children: [
408
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
409
+ className: "lb-tiptap-ai-toolbar-halo-horizontal"
410
+ }),
411
+ /* @__PURE__ */ jsxRuntime.jsx("div", {
412
+ className: "lb-tiptap-ai-toolbar-halo-vertical"
413
+ })
414
+ ]
415
+ })
416
+ ]
417
+ }),
418
+ state.phase === "asking" || state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.List, {
419
+ className: "lb-elevation lb-dropdown lb-tiptap-ai-toolbar-dropdown",
420
+ "data-hidden": isDropdownHidden ? "" : void 0,
421
+ ref: dropdownRef,
422
+ children: state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarReviewingSuggestions, {}) : children
423
+ }) : null
424
+ ]
425
+ });
426
+ }
427
+ const defaultSuggestions = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
428
+ children: [
429
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
430
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.EditIcon, {}),
431
+ prompt: "Improve the quality of the text",
432
+ children: "Improve writing"
433
+ }),
434
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
435
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
436
+ prompt: "Fix spelling & grammar errors in the text",
437
+ children: "Fix mistakes"
438
+ }),
439
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
440
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortenIcon, {}),
441
+ prompt: "Shorten the text, simplifying it",
442
+ children: "Simplify"
443
+ }),
444
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
445
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.LengthenIcon, {}),
446
+ prompt: "Lengthen the text, going into more detail",
447
+ children: "Add more detail"
448
+ }),
449
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestionsSeparator, {}),
450
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
451
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.SparklesTextIcon, {}),
452
+ prompt: "Continue writing from the text's end",
453
+ children: "Continue writing"
454
+ }),
455
+ /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
456
+ icon: /* @__PURE__ */ jsxRuntime.jsx(_private.QuestionMarkIcon, {}),
457
+ prompt: "Explain what the text is about",
458
+ children: "Explain"
459
+ })
460
+ ]
461
+ });
462
+ const AiToolbar = Object.assign(
463
+ react.forwardRef(
464
+ ({
465
+ offset: sideOffset = 6,
466
+ editor,
467
+ className,
468
+ suggestions: Suggestions = defaultSuggestions,
469
+ ...props
470
+ }, forwardedRef) => {
471
+ const state = react$1.useEditorState({
472
+ editor,
473
+ selector: (ctx) => {
474
+ return ctx.editor?.storage.liveblocksAi?.state;
475
+ }
476
+ }) ?? AiExtension.DEFAULT_STATE;
477
+ const selection = editor?.state.selection;
478
+ const floatingOptions = react.useMemo(() => {
479
+ const detectOverflowOptions = {
480
+ padding: AI_TOOLBAR_COLLISION_PADDING
481
+ };
482
+ return {
483
+ strategy: "fixed",
484
+ placement: "bottom",
485
+ middleware: [
486
+ tiptapFloating(editor),
487
+ reactDom.hide(detectOverflowOptions),
488
+ reactDom.offset(sideOffset),
489
+ reactDom.shift({
490
+ ...detectOverflowOptions,
491
+ mainAxis: false,
492
+ crossAxis: true,
493
+ limiter: reactDom.limitShift()
494
+ }),
495
+ flipToolbar()
496
+ ],
497
+ whileElementsMounted: (...args) => {
498
+ return reactDom.autoUpdate(...args, {
499
+ animationFrame: true
500
+ });
501
+ }
502
+ };
503
+ }, [editor, sideOffset]);
504
+ const isOpen = selection !== void 0 && state.phase !== "closed";
505
+ const {
506
+ refs: { setReference, setFloating },
507
+ strategy,
508
+ x,
509
+ y,
510
+ isPositioned
511
+ } = reactDom.useFloating({
512
+ ...floatingOptions,
513
+ open: isOpen
514
+ });
515
+ const toolbarRef = react.useRef(null);
516
+ const mergedRefs = _private.useRefs(forwardedRef, toolbarRef, setFloating);
517
+ const dropdownRef = react.useRef(null);
518
+ const [selectedDropdownValue, setSelectedDropdownValue] = react.useState("");
519
+ react.useEffect(() => {
520
+ if (state.phase === "closed") {
521
+ setSelectedDropdownValue("");
522
+ }
523
+ }, [state.phase]);
524
+ react.useEffect(() => {
525
+ if (state.phase === "closed") {
526
+ setSelectedDropdownValue("");
527
+ return;
528
+ }
529
+ const selectedDropdownItem = dropdownRef.current?.querySelector(
530
+ "[role='option'][data-selected='true']"
531
+ );
532
+ if (selectedDropdownItem) {
533
+ return;
534
+ }
535
+ const firstDropdownItem = dropdownRef.current?.querySelector("[role='option']");
536
+ setSelectedDropdownValue(
537
+ firstDropdownItem?.dataset.value ?? ""
538
+ );
539
+ }, [state.phase, dropdownRef, setSelectedDropdownValue]);
540
+ react.useEffect(() => {
541
+ if (!editor) {
542
+ return;
543
+ }
544
+ if (!selection && state.phase !== "closed") {
545
+ editor.commands.$closeAiToolbar();
546
+ }
547
+ }, [state.phase, editor, selection]);
548
+ _private$1.useLayoutEffect(() => {
549
+ if (!editor || !isOpen) {
550
+ return;
551
+ }
552
+ setReference(null);
553
+ setTimeout(() => {
554
+ if (state.phase === "reviewing" && AiExtension.isContextualPromptDiffResponse(state.response)) {
555
+ const changes = editor.view.dom.querySelectorAll(
556
+ "ychange[data-liveblocks]"
557
+ );
558
+ setReference({
559
+ getBoundingClientRect: () => {
560
+ const rects = [];
561
+ changes.forEach((change) => {
562
+ rects.push(change.getBoundingClientRect());
563
+ });
564
+ const minX = Math.min(...rects.map((rect) => rect.left));
565
+ const minY = Math.min(...rects.map((rect) => rect.top));
566
+ const maxX = Math.max(...rects.map((rect) => rect.right));
567
+ const maxY = Math.max(...rects.map((rect) => rect.bottom));
568
+ return {
569
+ x: minX,
570
+ y: minY,
571
+ width: maxX - minX,
572
+ height: maxY - minY,
573
+ top: minY,
574
+ left: minX,
575
+ bottom: maxY,
576
+ right: maxX
577
+ };
578
+ }
579
+ });
580
+ } else if (selection) {
581
+ const domRange = utils.getDomRangeFromSelection(editor, selection);
582
+ setReference(domRange);
583
+ } else {
584
+ setReference(null);
585
+ }
586
+ }, 0);
587
+ }, [
588
+ selection,
589
+ editor,
590
+ isOpen,
591
+ setReference,
592
+ state.phase,
593
+ state.response
594
+ ]);
595
+ react.useEffect(() => {
596
+ if (!editor || !isOpen) {
597
+ return;
598
+ }
599
+ const handleOutsideEvent = (event) => {
600
+ if (!toolbarRef.current) {
601
+ return;
602
+ }
603
+ if (event.target && !toolbarRef.current.contains(event.target) && (dropdownRef.current ? !dropdownRef.current.contains(event.target) : true)) {
604
+ editor.commands.$closeAiToolbar();
605
+ }
606
+ };
607
+ setTimeout(() => {
608
+ document.addEventListener("pointerdown", handleOutsideEvent);
609
+ }, 0);
610
+ return () => {
611
+ document.removeEventListener("pointerdown", handleOutsideEvent);
612
+ };
613
+ }, [editor, isOpen]);
614
+ if (!editor || !isOpen) {
615
+ return null;
616
+ }
617
+ return reactDom$1.createPortal(
618
+ /* @__PURE__ */ jsxRuntime.jsx(_private.TooltipProvider, {
619
+ children: /* @__PURE__ */ jsxRuntime.jsx(context.EditorProvider, {
620
+ editor,
621
+ children: /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command, {
622
+ role: "toolbar",
623
+ label: "AI toolbar",
624
+ "aria-orientation": "horizontal",
625
+ className: classnames.classNames(
626
+ "lb-root lb-portal lb-tiptap-ai-toolbar-portal",
627
+ className
628
+ ),
629
+ ref: mergedRefs,
630
+ style: {
631
+ position: strategy,
632
+ top: 0,
633
+ left: 0,
634
+ transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)"
635
+ },
636
+ value: selectedDropdownValue,
637
+ onValueChange: setSelectedDropdownValue,
638
+ ...props,
639
+ children: /* @__PURE__ */ jsxRuntime.jsx(AiToolbarContainer, {
640
+ state,
641
+ dropdownRef,
642
+ toolbarRef,
643
+ children: typeof Suggestions === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Suggestions, {
644
+ children: defaultSuggestions
645
+ }) : Suggestions
646
+ })
647
+ })
648
+ })
649
+ }),
650
+ document.body
651
+ );
652
+ }
653
+ ),
654
+ {
655
+ Suggestion: AiToolbarSuggestion,
656
+ SuggestionsLabel: AiToolbarSuggestionsLabel,
657
+ SuggestionsSeparator: AiToolbarSuggestionsSeparator
658
+ }
659
+ );
660
+
661
+ exports.AI_TOOLBAR_COLLISION_PADDING = AI_TOOLBAR_COLLISION_PADDING;
662
+ exports.AiToolbar = AiToolbar;
663
+ //# sourceMappingURL=AiToolbar.js.map