@liveblocks/react-tiptap 2.16.1-ai2 → 2.16.1

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 (42) hide show
  1. package/dist/LiveblocksExtension.js +21 -120
  2. package/dist/LiveblocksExtension.js.map +1 -1
  3. package/dist/LiveblocksExtension.mjs +19 -118
  4. package/dist/LiveblocksExtension.mjs.map +1 -1
  5. package/dist/comments/CommentsExtension.js.map +1 -1
  6. package/dist/comments/CommentsExtension.mjs.map +1 -1
  7. package/dist/index.d.mts +14 -56
  8. package/dist/index.d.ts +14 -56
  9. package/dist/index.js +0 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +0 -1
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/toolbar/FloatingToolbar.js +0 -7
  14. package/dist/toolbar/FloatingToolbar.js.map +1 -1
  15. package/dist/toolbar/FloatingToolbar.mjs +0 -7
  16. package/dist/toolbar/FloatingToolbar.mjs.map +1 -1
  17. package/dist/toolbar/Toolbar.js +1 -35
  18. package/dist/toolbar/Toolbar.js.map +1 -1
  19. package/dist/toolbar/Toolbar.mjs +2 -36
  20. package/dist/toolbar/Toolbar.mjs.map +1 -1
  21. package/dist/types.js.map +1 -1
  22. package/dist/types.mjs.map +1 -1
  23. package/dist/utils.js +1 -21
  24. package/dist/utils.js.map +1 -1
  25. package/dist/utils.mjs +2 -20
  26. package/dist/utils.mjs.map +1 -1
  27. package/dist/version.js +1 -1
  28. package/dist/version.js.map +1 -1
  29. package/dist/version.mjs +1 -1
  30. package/dist/version.mjs.map +1 -1
  31. package/package.json +6 -7
  32. package/src/styles/index.css +3 -322
  33. package/styles.css +1 -1
  34. package/styles.css.map +1 -1
  35. package/dist/ai/AiExtension.js +0 -299
  36. package/dist/ai/AiExtension.js.map +0 -1
  37. package/dist/ai/AiExtension.mjs +0 -296
  38. package/dist/ai/AiExtension.mjs.map +0 -1
  39. package/dist/ai/AiToolbar.js +0 -570
  40. package/dist/ai/AiToolbar.js.map +0 -1
  41. package/dist/ai/AiToolbar.mjs +0 -567
  42. package/dist/ai/AiToolbar.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"AiExtension.mjs","sources":["../../src/ai/AiExtension.ts"],"sourcesContent":["import { autoRetry, HttpError } from \"@liveblocks/core\";\nimport type { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { CommandProps, Editor } from \"@tiptap/core\";\nimport { Extension } from \"@tiptap/core\";\nimport { Fragment, Slice } from \"@tiptap/pm/model\";\nimport { Plugin } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport {\n ySyncPluginKey,\n yXmlFragmentToProseMirrorFragment,\n} from \"y-prosemirror\";\nimport type { Snapshot } from \"yjs\";\nimport {\n createDocFromSnapshot,\n emptySnapshot,\n equalSnapshots,\n snapshot,\n} from \"yjs\";\n\nimport {\n AI_TOOLBAR_SELECTION_PLUGIN,\n type AiCommands,\n type AiExtensionOptions,\n type AiExtensionStorage,\n type AiToolbarOutput,\n type AiToolbarState,\n type LiveblocksExtensionStorage,\n type YSyncPluginState,\n} from \"../types\";\n\nconst DEFAULT_AI_NAME = \"AI\";\nexport const DEFAULT_STATE: AiToolbarState = { phase: \"closed\" };\n\nconst RESOLVE_AI_PROMPT_RETRY_ATTEMPTS = 3;\nconst RESOLVE_AI_PROMPT_RETRY_DELAYS = [1000, 2000, 4000];\n\nfunction getYjsBinding(editor: Editor) {\n return (ySyncPluginKey.getState(editor.view.state) as YSyncPluginState)\n .binding;\n}\n\nfunction getLiveblocksYjsProvider(editor: Editor) {\n return (\n editor.extensionStorage.liveblocksExtension as\n | LiveblocksExtensionStorage\n | undefined\n )?.provider as LiveblocksYjsProvider | undefined;\n}\n\nexport const AiExtension = Extension.create<\n AiExtensionOptions,\n AiExtensionStorage\n>({\n name: \"liveblocksAi\",\n addOptions() {\n return {\n doc: undefined,\n pud: undefined,\n\n // The actual default resolver is set in LiveblocksExtension via AiExtension.configure()\n resolveAiPrompt: () => Promise.reject(),\n name: DEFAULT_AI_NAME,\n };\n },\n addStorage() {\n return {\n state: DEFAULT_STATE,\n name: this.options.name,\n };\n },\n addCommands() {\n return {\n askAi: (prompt) => () => {\n if (typeof prompt === \"string\") {\n (\n this.editor.commands as unknown as AiCommands\n ).$startAiToolbarThinking(prompt);\n } else {\n (\n this.editor.commands as unknown as AiCommands\n ).$openAiToolbarAsking();\n }\n\n return true;\n },\n\n $acceptAiToolbarOutput:\n () =>\n ({ tr }: CommandProps) => {\n const currentState = this.storage.state;\n if (currentState.phase !== \"reviewing\") {\n return false;\n }\n const binding = getYjsBinding(this.editor);\n if (!binding) {\n return false;\n }\n\n const fragmentContent = yXmlFragmentToProseMirrorFragment(\n binding.type,\n this.editor.state.schema\n );\n tr.setMeta(\"addToHistory\", false);\n tr.replace(\n 0,\n this.editor.state.doc.content.size,\n new Slice(Fragment.from(fragmentContent), 0, 0)\n );\n tr.setMeta(ySyncPluginKey, {\n snapshot: null,\n prevSnapshot: null,\n });\n\n // TODO: move this cleanup to somewhere that closeAIToolbar can share\n getLiveblocksYjsProvider(this.editor)?.unpause();\n this.editor.setEditable(true);\n this.storage.snapshot = undefined;\n this.storage.state = { phase: \"closed\" };\n return true;\n },\n\n $closeAiToolbar:\n () =>\n ({ tr }: CommandProps) => {\n const currentState = this.storage.state;\n\n // 1. If in \"thinking\" phase, cancel the current AI request\n if (currentState.phase === \"thinking\") {\n currentState.abortController.abort();\n }\n\n // 2. If in \"thinking\" or \"reviewing\" phase, revert the editor if possible and unblock it\n if (\n currentState.phase === \"thinking\" ||\n currentState.phase === \"reviewing\"\n ) {\n if (this.storage.snapshot) {\n const binding = getYjsBinding(this.editor);\n\n if (binding) {\n binding.mapping.clear();\n\n const docFromSnapshot = createDocFromSnapshot(\n binding.doc,\n this.storage.snapshot\n );\n const type = docFromSnapshot.getXmlFragment(\"default\"); // TODO: field\n const fragmentContent = yXmlFragmentToProseMirrorFragment(\n type,\n this.editor.state.schema\n );\n\n tr.setMeta(\"addToHistory\", false);\n tr.replace(\n 0,\n this.editor.state.doc.content.size,\n new Slice(Fragment.from(fragmentContent), 0, 0)\n );\n tr.setMeta(ySyncPluginKey, {\n snapshot: null,\n prevSnapshot: null,\n });\n\n getLiveblocksYjsProvider(this.editor)?.unpause();\n\n if (this.options.doc) {\n this.options.doc.gc = true;\n }\n\n this.storage.snapshot = undefined;\n }\n }\n\n this.editor.setEditable(true);\n }\n\n // 4. Set to \"closed\" phase\n this.storage.state = { phase: \"closed\" };\n\n return true;\n },\n\n $openAiToolbarAsking: () => () => {\n const currentState = this.storage.state;\n\n // 1. If NOT in \"closed\" phase, do nothing\n if (currentState.phase !== \"closed\") {\n return false;\n }\n\n // 2. Blur the editor if needed\n if (this.editor.isFocused) {\n this.editor.commands.blur();\n }\n\n // 3. Set to \"asking\" phase\n this.storage.state = {\n phase: \"asking\",\n\n // Initialize the custom prompt as empty\n customPrompt: \"\",\n };\n\n return true;\n },\n\n $startAiToolbarThinking: (prompt: string) => () => {\n const currentState = this.storage.state;\n\n // 1. If in \"thinking\" phase already, do nothing\n if (currentState.phase === \"thinking\") {\n return false;\n }\n\n if (currentState.phase === \"reviewing\") {\n // TODO: this is a retry, we should actually retry and start thinking again\n // Maybe not, it could be a refinement prompt\n return (\n this.editor.commands as unknown as AiCommands\n ).$closeAiToolbar();\n }\n\n // 2. Blur the editor if needed\n if (this.editor.isFocused) {\n this.editor.commands.blur();\n }\n\n const abortController = new AbortController();\n const provider = getLiveblocksYjsProvider(this.editor);\n\n // 3. Set to \"thinking\" phase\n this.storage.state = {\n phase: \"thinking\",\n customPrompt: currentState.customPrompt ?? \"\",\n prompt,\n abortController,\n };\n\n // 4. Block the editor\n this.editor.setEditable(false);\n\n // 5. Start the AI request\n autoRetry(\n async () => {\n await provider?.pause();\n const { from, to } = this.editor.state.selection.empty\n ? {\n // TODO: this is a hack to get the context around the selection, we need to improve this\n from: Math.max(this.editor.state.selection.to - 30, 0),\n to: this.editor.state.selection.to,\n }\n : this.editor.state.selection;\n\n return this.options.resolveAiPrompt({\n prompt,\n selectionText: this.editor.state.doc.textBetween(from, to, \" \"),\n /*\n TODO: This needs a maximum to avoid overloading context, for now I've arbitrailiry chosen 3000\n characters but this will need to be improved, probably using word boundary of some sort (languages can make that tricky)\n as well as choosing text around the selection, so before/after.\n */\n context: this.editor.getText().slice(0, 3_000),\n signal: abortController.signal,\n });\n },\n RESOLVE_AI_PROMPT_RETRY_ATTEMPTS,\n RESOLVE_AI_PROMPT_RETRY_DELAYS,\n\n (error) => {\n // Don't retry on 4xx errors or if the request was aborted\n return (\n abortController.signal.aborted ||\n (error instanceof HttpError &&\n error.status >= 400 &&\n error.status < 500)\n );\n }\n )\n .then((output) => {\n if (abortController.signal.aborted) {\n return;\n }\n\n // 5.a. If the AI request succeeds, set to \"reviewing\" phase with the output\n (\n this.editor.commands as unknown as AiCommands\n )._handleAiToolbarThinkingSuccess({\n type: output.type,\n text: output.content,\n });\n })\n .catch((error) => {\n if (abortController.signal.aborted) {\n return;\n }\n\n // 5.b. If the AI request fails, set to \"asking\" phase with error\n (\n this.editor.commands as unknown as AiCommands\n )._handleAiToolbarThinkingError(error as Error);\n });\n\n return true;\n },\n\n $cancelAiToolbarThinking: () => () => {\n const currentState = this.storage.state;\n\n // 1. If NOT in \"thinking\" phase, do nothing\n if (currentState.phase !== \"thinking\") {\n return false;\n }\n\n // 2. Cancel the current AI request\n currentState.abortController.abort();\n\n // 3. Unblock the editor\n this.editor.setEditable(true);\n\n // 4. Set to \"asking\" phase\n this.storage.state = {\n phase: \"asking\",\n // If the custom prompt is different than the prompt, reset it\n customPrompt:\n currentState.prompt === currentState.customPrompt\n ? currentState.customPrompt\n : \"\",\n };\n\n return true;\n },\n\n _handleAiToolbarThinkingSuccess: (output: AiToolbarOutput) => () => {\n const currentState = this.storage.state;\n\n // 1. If NOT in \"thinking\" phase, do nothing\n if (currentState.phase !== \"thinking\" || !this.options.doc) {\n return false;\n }\n\n let selection: { from: number; to: number } =\n this.editor.state.selection;\n\n // 2. If the output is a diff, apply it to the editor\n if ([\"modification\", \"insert\"].includes(output.type)) {\n this.options.doc.gc = false;\n this.storage.snapshot = snapshot(this.options.doc);\n\n const { empty, from, to } = this.editor.state.selection;\n\n // if the selection is empty, insert at the end of the selection\n const contentTarget =\n empty || output.type === \"insert\"\n ? to\n : {\n from,\n to,\n };\n\n // when inserting, use the \"to\" position, otherwise when modifing, use the \"from\" position\n const targetTo = output.type === \"insert\" ? to : from;\n\n // 2.a. Insert the output.\n this.editor.commands.insertContentAt(contentTarget, output.text);\n\n // 2.b. Diff the output in the editor\n // TODO: setTimeout will make this execute after the current transaction is committed, which is returned by the insert content command.\n setTimeout(() => {\n if (this.storage.snapshot) {\n (\n this.editor.commands as unknown as AiCommands\n )._renderAiToolbarDiffInEditor(this.storage.snapshot);\n }\n }, 100);\n\n selection = {\n from,\n to: targetTo + output.text.length, // take into account the new length with output\n };\n }\n\n // 3. Set to \"reviewing\" phase with the output\n this.storage.state = {\n phase: \"reviewing\",\n customPrompt: \"\",\n prompt: currentState.prompt,\n output,\n selection,\n };\n\n return true;\n },\n\n _handleAiToolbarThinkingError: (error: Error) => () => {\n const currentState = this.storage.state;\n\n // 1. If NOT in \"thinking\" phase, do nothing\n if (currentState.phase !== \"thinking\") {\n return false;\n }\n\n // 2. Unblock the editor\n this.editor.setEditable(true);\n\n // 3. Set to \"asking\" phase with error\n this.storage.state = {\n phase: \"asking\",\n // If the custom prompt is different than the prompt, reset it\n customPrompt:\n currentState.prompt === currentState.customPrompt\n ? currentState.customPrompt\n : \"\",\n // TODO: Improve error handling\n error,\n };\n\n return true;\n },\n\n _updateAiToolbarCustomPrompt:\n (customPrompt: string | ((currentCustomPrompt: string) => string)) =>\n () => {\n const currentState = this.storage.state;\n\n // 1. If NOT in a phase with a custom prompt, do nothing\n if (typeof currentState.customPrompt !== \"string\") {\n return false;\n }\n\n // 2. Update the custom prompt\n this.storage.state.customPrompt =\n typeof customPrompt === \"function\"\n ? customPrompt(currentState.customPrompt)\n : customPrompt;\n\n return true;\n },\n\n _renderAiToolbarDiffInEditor: (previous?: Snapshot) => () => {\n if (!this.options.doc) {\n return false;\n }\n\n const previousSnapshot: Snapshot = previous ?? emptySnapshot;\n const currentSnapshot = snapshot(this.options.doc);\n\n if (equalSnapshots(previousSnapshot, currentSnapshot)) {\n return true;\n }\n\n const binding = getYjsBinding(this.editor);\n\n if (binding) {\n binding.renderSnapshot(currentSnapshot, previousSnapshot);\n return true;\n }\n\n return false;\n },\n };\n },\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: AI_TOOLBAR_SELECTION_PLUGIN,\n props: {\n decorations: ({ doc, selection }) => {\n if (\n this.storage.state.phase === \"closed\" ||\n // TODO: Don't show the selection when reviewing diff outputs\n (this.storage.state.phase === \"reviewing\" &&\n this.storage.state.output.type !== \"other\")\n ) {\n return DecorationSet.create(doc, []);\n }\n const { from, to } = this.storage.state.selection ?? selection;\n const decorations: Decoration[] = [\n Decoration.inline(from, to, {\n class: \"lb-root lb-selection lb-tiptap-active-selection\",\n }),\n ];\n return DecorationSet.create(doc, decorations);\n },\n },\n }),\n ];\n },\n});\n"],"names":[],"mappings":"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"}
@@ -1,570 +0,0 @@
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
- const AiToolbarDropdownGroup = react.forwardRef(({ children, label, ...props }, forwardedRef) => {
49
- return /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.Group, {
50
- heading: /* @__PURE__ */ jsxRuntime.jsx("span", {
51
- className: "lb-dropdown-label",
52
- children: label
53
- }),
54
- ...props,
55
- ref: forwardedRef,
56
- children
57
- });
58
- });
59
- const AiToolbarSuggestionsGroup = react.forwardRef((props, forwardedRef) => {
60
- return /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownGroup, {
61
- ref: forwardedRef,
62
- ...props
63
- });
64
- });
65
- const AiToolbarDropdownItem = react.forwardRef(({ children, onSelect, icon, ...props }, forwardedRef) => {
66
- return /* @__PURE__ */ jsxRuntime.jsxs(cmdk.Command.Item, {
67
- className: "lb-dropdown-item",
68
- onSelect,
69
- ...props,
70
- ref: forwardedRef,
71
- children: [
72
- icon ? /* @__PURE__ */ jsxRuntime.jsx("span", {
73
- className: "lb-icon-container",
74
- children: icon
75
- }) : null,
76
- children ? /* @__PURE__ */ jsxRuntime.jsx("span", {
77
- className: "lb-dropdown-item-label",
78
- children
79
- }) : null
80
- ]
81
- });
82
- });
83
- const AiToolbarSuggestion = react.forwardRef(({ prompt: manualPrompt, ...props }, forwardedRef) => {
84
- const editor = context.useCurrentEditor("Suggestion", "AiToolbar");
85
- const handleSelect = react.useCallback(
86
- (prompt) => {
87
- editor.commands.$startAiToolbarThinking(
88
- manualPrompt ?? prompt
89
- );
90
- },
91
- [editor, manualPrompt]
92
- );
93
- return /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
94
- ...props,
95
- onSelect: handleSelect,
96
- ref: forwardedRef
97
- });
98
- });
99
- function AiToolbarReviewingSuggestions() {
100
- const editor = context.useCurrentEditor("ReviewingSuggestions", "AiToolbar");
101
- const { state } = useAiToolbarContext();
102
- const { output } = state;
103
- const handleDiscard = react.useCallback(() => {
104
- editor.commands.$closeAiToolbar();
105
- }, [editor]);
106
- const handleAccept = react.useCallback(() => {
107
- editor.commands.$acceptAiToolbarOutput();
108
- }, [editor]);
109
- if (output.type === "insert" || output.type === "modification") {
110
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
111
- children: [
112
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
113
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
114
- onSelect: handleAccept,
115
- children: "Accept"
116
- }),
117
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
118
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
119
- disabled: true,
120
- children: "Try again"
121
- }),
122
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
123
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CrossIcon, {}),
124
- onSelect: handleDiscard,
125
- children: "Discard"
126
- })
127
- ]
128
- });
129
- } else if (output.type === "other") {
130
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
131
- children: [
132
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
133
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
134
- disabled: true,
135
- children: "Replace selection"
136
- }),
137
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
138
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
139
- disabled: true,
140
- children: "Insert below"
141
- }),
142
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
143
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
144
- disabled: true,
145
- children: "Try again"
146
- }),
147
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarDropdownItem, {
148
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CrossIcon, {}),
149
- onSelect: handleDiscard,
150
- children: "Discard"
151
- })
152
- ]
153
- });
154
- }
155
- return null;
156
- }
157
- function AiToolbarCustomPromptContent({ disabled }) {
158
- const editor = context.useCurrentEditor("CustomPromptContent", "AiToolbar");
159
- const aiName = editor.storage.liveblocksAi.name;
160
- const textAreaRef = react.useRef(null);
161
- const { state, dropdownRef, isDropdownHidden } = useAiToolbarContext();
162
- const { customPrompt } = state;
163
- const isCustomPromptEmpty = react.useMemo(
164
- () => customPrompt.trim() === "",
165
- [customPrompt]
166
- );
167
- _private$1.useLayoutEffect(
168
- () => {
169
- setTimeout(() => {
170
- const textArea = textAreaRef.current;
171
- if (!textArea) {
172
- return;
173
- }
174
- textArea.focus();
175
- textArea.setSelectionRange(
176
- textArea.value.length,
177
- textArea.value.length
178
- );
179
- }, 0);
180
- },
181
- []
182
- );
183
- const handlePromptKeyDown = (event) => {
184
- if (event.key === "Enter") {
185
- event.preventDefault();
186
- event.stopPropagation();
187
- if (event.shiftKey) {
188
- editor.commands._updateAiToolbarCustomPrompt(
189
- (customPrompt2) => customPrompt2 + "\n"
190
- );
191
- } else {
192
- const selectedDropdownItem = dropdownRef.current?.querySelector(
193
- "[role='option'][data-selected='true']"
194
- );
195
- if (!isDropdownHidden && selectedDropdownItem) {
196
- selectedDropdownItem.click();
197
- } else if (!isCustomPromptEmpty) {
198
- editor.commands.$startAiToolbarThinking(
199
- customPrompt
200
- );
201
- }
202
- }
203
- }
204
- };
205
- const handleCustomPromptChange = react.useCallback(
206
- (customPrompt2) => {
207
- editor.commands._updateAiToolbarCustomPrompt(
208
- customPrompt2
209
- );
210
- },
211
- [editor]
212
- );
213
- const handleSendClick = react.useCallback(() => {
214
- if (isCustomPromptEmpty) {
215
- return;
216
- }
217
- editor.commands.$startAiToolbarThinking(
218
- customPrompt
219
- );
220
- }, [editor, customPrompt, isCustomPromptEmpty]);
221
- return /* @__PURE__ */ jsxRuntime.jsxs("div", {
222
- className: "lb-tiptap-ai-toolbar-content",
223
- children: [
224
- /* @__PURE__ */ jsxRuntime.jsx("span", {
225
- className: "lb-icon-container lb-tiptap-ai-toolbar-icon-container",
226
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.SparklesIcon, {})
227
- }),
228
- /* @__PURE__ */ jsxRuntime.jsx("div", {
229
- className: "lb-tiptap-ai-toolbar-custom-prompt-container",
230
- "data-value": customPrompt,
231
- children: /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.Input, {
232
- value: customPrompt,
233
- onValueChange: handleCustomPromptChange,
234
- asChild: true,
235
- children: /* @__PURE__ */ jsxRuntime.jsx("textarea", {
236
- ref: textAreaRef,
237
- className: "lb-tiptap-ai-toolbar-custom-prompt",
238
- placeholder: `Ask ${aiName} anything\u2026`,
239
- onKeyDown: handlePromptKeyDown,
240
- rows: 1,
241
- autoFocus: true,
242
- disabled
243
- })
244
- })
245
- }),
246
- /* @__PURE__ */ jsxRuntime.jsx("div", {
247
- className: "lb-tiptap-ai-toolbar-actions",
248
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortcutTooltip, {
249
- content: `Ask ${aiName}`,
250
- shortcut: "Enter",
251
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.Button, {
252
- className: "lb-tiptap-ai-toolbar-action",
253
- variant: "primary",
254
- "aria-label": `Ask ${aiName}`,
255
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.SendIcon, {}),
256
- disabled: isCustomPromptEmpty || disabled,
257
- onClick: handleSendClick
258
- })
259
- })
260
- })
261
- ]
262
- });
263
- }
264
- function AiToolbarAsking() {
265
- return /* @__PURE__ */ jsxRuntime.jsx(AiToolbarCustomPromptContent, {});
266
- }
267
- function AiToolbarThinking() {
268
- const editor = context.useCurrentEditor("AiToolbarThinking", "AiToolbar");
269
- const contentRef = react.useRef(null);
270
- const aiName = editor.storage.liveblocksAi.name;
271
- const handleCancel = react.useCallback(() => {
272
- editor.commands.$cancelAiToolbarThinking();
273
- }, [editor]);
274
- _private$1.useLayoutEffect(() => {
275
- contentRef.current?.focus();
276
- window.getSelection()?.removeAllRanges();
277
- }, []);
278
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {
279
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", {
280
- className: "lb-tiptap-ai-toolbar-content",
281
- tabIndex: 0,
282
- ref: contentRef,
283
- children: [
284
- /* @__PURE__ */ jsxRuntime.jsx("span", {
285
- className: "lb-icon-container lb-tiptap-ai-toolbar-icon-container",
286
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.SparklesIcon, {})
287
- }),
288
- /* @__PURE__ */ jsxRuntime.jsxs("div", {
289
- className: "lb-tiptap-ai-toolbar-thinking",
290
- children: [
291
- aiName,
292
- " is thinking\u2026"
293
- ]
294
- }),
295
- /* @__PURE__ */ jsxRuntime.jsx("div", {
296
- className: "lb-tiptap-ai-toolbar-actions",
297
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortcutTooltip, {
298
- content: "Cancel",
299
- shortcut: "Escape",
300
- children: /* @__PURE__ */ jsxRuntime.jsx(_private.Button, {
301
- className: "lb-tiptap-ai-toolbar-action",
302
- variant: "secondary",
303
- "aria-label": "Cancel",
304
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.UndoIcon, {}),
305
- onClick: handleCancel
306
- })
307
- })
308
- })
309
- ]
310
- })
311
- });
312
- }
313
- function AiToolbarReviewing() {
314
- const { state } = useAiToolbarContext();
315
- const { output } = state;
316
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
317
- children: [
318
- output.type === "other" ? /* @__PURE__ */ jsxRuntime.jsx("div", {
319
- className: "lb-tiptap-ai-toolbar-output-container",
320
- children: /* @__PURE__ */ jsxRuntime.jsx("div", {
321
- className: "lb-tiptap-ai-toolbar-output",
322
- children: output.text
323
- })
324
- }) : null,
325
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarCustomPromptContent, {
326
- disabled: true
327
- })
328
- ]
329
- });
330
- }
331
- function AiToolbarContainer({
332
- state,
333
- toolbarRef,
334
- dropdownRef,
335
- children
336
- }) {
337
- const editor = context.useCurrentEditor("AiToolbarContainer", "AiToolbar");
338
- const customPrompt = state.customPrompt;
339
- const isCustomPromptMultiline = react.useMemo(
340
- () => customPrompt?.includes("\n"),
341
- [customPrompt]
342
- );
343
- const hasDropdownItems = cmdk.useCommandState(
344
- (state2) => state2.filtered.count > 0
345
- );
346
- const isDropdownHidden = isCustomPromptMultiline || !hasDropdownItems;
347
- react.useEffect(() => {
348
- if (!editor) {
349
- return;
350
- }
351
- const handleKeyDown = (event) => {
352
- if (!event.defaultPrevented && event.key === "Escape") {
353
- event.preventDefault();
354
- event.stopPropagation();
355
- if (state.phase === "thinking") {
356
- editor.commands.$cancelAiToolbarThinking();
357
- } else {
358
- editor.chain().$closeAiToolbar().focus().run();
359
- }
360
- }
361
- };
362
- document.addEventListener("keydown", handleKeyDown);
363
- return () => {
364
- document.removeEventListener("keydown", handleKeyDown);
365
- };
366
- }, [editor, state.phase]);
367
- return /* @__PURE__ */ jsxRuntime.jsxs(AiToolbarContext.Provider, {
368
- value: { state, toolbarRef, dropdownRef, isDropdownHidden },
369
- children: [
370
- /* @__PURE__ */ jsxRuntime.jsxs("div", {
371
- className: "lb-tiptap-ai-toolbar-container",
372
- children: [
373
- /* @__PURE__ */ jsxRuntime.jsx("div", {
374
- className: "lb-elevation lb-tiptap-ai-toolbar",
375
- children: state.phase === "asking" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarAsking, {}) : state.phase === "thinking" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarThinking, {}) : state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarReviewing, {}) : null
376
- }),
377
- /* @__PURE__ */ jsxRuntime.jsxs("div", {
378
- className: "lb-tiptap-ai-toolbar-halo",
379
- "data-active": state.phase === "thinking" ? "" : void 0,
380
- "aria-hidden": true,
381
- children: [
382
- /* @__PURE__ */ jsxRuntime.jsx("div", {
383
- className: "lb-tiptap-ai-toolbar-halo-horizontal"
384
- }),
385
- /* @__PURE__ */ jsxRuntime.jsx("div", {
386
- className: "lb-tiptap-ai-toolbar-halo-vertical"
387
- })
388
- ]
389
- })
390
- ]
391
- }),
392
- state.phase === "asking" || state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command.List, {
393
- className: "lb-elevation lb-dropdown lb-tiptap-ai-toolbar-dropdown",
394
- "data-hidden": isDropdownHidden ? "" : void 0,
395
- ref: dropdownRef,
396
- children: state.phase === "reviewing" ? /* @__PURE__ */ jsxRuntime.jsx(AiToolbarReviewingSuggestions, {}) : children
397
- }) : null
398
- ]
399
- });
400
- }
401
- const defaultSuggestions = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
402
- children: [
403
- /* @__PURE__ */ jsxRuntime.jsxs(AiToolbarSuggestionsGroup, {
404
- label: "Modify",
405
- children: [
406
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
407
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.EditIcon, {}),
408
- children: "Improve writing"
409
- }),
410
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
411
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.CheckIcon, {}),
412
- children: "Fix mistakes"
413
- }),
414
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
415
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.ShortenIcon, {}),
416
- children: "Simplify"
417
- }),
418
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
419
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.LengthenIcon, {}),
420
- children: "Add more detail"
421
- })
422
- ]
423
- }),
424
- /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestionsGroup, {
425
- label: "Generate",
426
- children: /* @__PURE__ */ jsxRuntime.jsx(AiToolbarSuggestion, {
427
- icon: /* @__PURE__ */ jsxRuntime.jsx(_private.QuestionMarkIcon, {}),
428
- children: "Explain"
429
- })
430
- })
431
- ]
432
- });
433
- const AiToolbar = Object.assign(
434
- react.forwardRef(
435
- ({
436
- position = "bottom",
437
- offset: sideOffset = 6,
438
- editor,
439
- className,
440
- suggestions: Suggestions = defaultSuggestions,
441
- ...props
442
- }, forwardedRef) => {
443
- const state = react$1.useEditorState({
444
- editor,
445
- selector: (ctx) => {
446
- return ctx.editor?.storage.liveblocksAi?.state;
447
- }
448
- }) ?? AiExtension.DEFAULT_STATE;
449
- const selection = state.selection ?? editor?.state.selection;
450
- const floatingOptions = react.useMemo(() => {
451
- const detectOverflowOptions = {
452
- padding: AI_TOOLBAR_COLLISION_PADDING
453
- };
454
- return {
455
- strategy: "fixed",
456
- placement: position,
457
- middleware: [
458
- tiptapFloating(editor),
459
- reactDom.hide(detectOverflowOptions),
460
- reactDom.offset(sideOffset)
461
- ],
462
- whileElementsMounted: (...args) => {
463
- return reactDom.autoUpdate(...args, {
464
- animationFrame: true
465
- });
466
- }
467
- };
468
- }, [editor, position, sideOffset]);
469
- const isOpen = selection !== void 0 && state.phase !== "closed";
470
- const {
471
- refs: { setReference, setFloating },
472
- strategy,
473
- x,
474
- y,
475
- isPositioned
476
- } = reactDom.useFloating({
477
- ...floatingOptions,
478
- open: isOpen
479
- });
480
- const toolbarRef = react.useRef(null);
481
- const mergedRefs = _private.useRefs(forwardedRef, toolbarRef, setFloating);
482
- const dropdownRef = react.useRef(null);
483
- react.useEffect(() => {
484
- if (!editor) {
485
- return;
486
- }
487
- if (!selection && state.phase !== "closed") {
488
- editor.commands.$closeAiToolbar();
489
- }
490
- }, [state.phase, editor, selection]);
491
- _private$1.useLayoutEffect(() => {
492
- if (!editor || !isOpen) {
493
- return;
494
- }
495
- setReference(null);
496
- setTimeout(() => {
497
- if (!selection) {
498
- setReference(null);
499
- } else {
500
- const domRange = utils.getDomRangeFromSelection(selection, editor);
501
- setReference(domRange);
502
- }
503
- }, 0);
504
- }, [selection, editor, isOpen, setReference]);
505
- react.useEffect(() => {
506
- if (!editor || !isOpen) {
507
- return;
508
- }
509
- const handleOutsideEvent = (event) => {
510
- if (!toolbarRef.current) {
511
- return;
512
- }
513
- if (event.target && !toolbarRef.current.contains(event.target) && (dropdownRef.current ? !dropdownRef.current.contains(event.target) : true)) {
514
- editor.commands.$closeAiToolbar();
515
- }
516
- };
517
- setTimeout(() => {
518
- document.addEventListener("pointerdown", handleOutsideEvent);
519
- }, 0);
520
- return () => {
521
- document.removeEventListener("pointerdown", handleOutsideEvent);
522
- };
523
- }, [editor, isOpen]);
524
- if (!editor || !isOpen) {
525
- return null;
526
- }
527
- return reactDom$1.createPortal(
528
- /* @__PURE__ */ jsxRuntime.jsx(_private.TooltipProvider, {
529
- children: /* @__PURE__ */ jsxRuntime.jsx(context.EditorProvider, {
530
- editor,
531
- children: /* @__PURE__ */ jsxRuntime.jsx(cmdk.Command, {
532
- role: "toolbar",
533
- label: "AI toolbar",
534
- "aria-orientation": "horizontal",
535
- className: classnames.classNames(
536
- "lb-root lb-portal lb-tiptap-ai-toolbar-portal",
537
- className
538
- ),
539
- ref: mergedRefs,
540
- style: {
541
- position: strategy,
542
- top: 0,
543
- left: 0,
544
- transform: isPositioned ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)` : "translate3d(0, -200%, 0)"
545
- },
546
- ...props,
547
- children: /* @__PURE__ */ jsxRuntime.jsx(AiToolbarContainer, {
548
- state,
549
- dropdownRef,
550
- toolbarRef,
551
- children: typeof Suggestions === "function" ? /* @__PURE__ */ jsxRuntime.jsx(Suggestions, {
552
- children: defaultSuggestions
553
- }) : Suggestions
554
- })
555
- })
556
- })
557
- }),
558
- document.body
559
- );
560
- }
561
- ),
562
- {
563
- SuggestionsGroup: AiToolbarSuggestionsGroup,
564
- Suggestion: AiToolbarSuggestion
565
- }
566
- );
567
-
568
- exports.AI_TOOLBAR_COLLISION_PADDING = AI_TOOLBAR_COLLISION_PADDING;
569
- exports.AiToolbar = AiToolbar;
570
- //# sourceMappingURL=AiToolbar.js.map