@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.
- package/dist/LiveblocksExtension.js +21 -120
- package/dist/LiveblocksExtension.js.map +1 -1
- package/dist/LiveblocksExtension.mjs +19 -118
- package/dist/LiveblocksExtension.mjs.map +1 -1
- package/dist/comments/CommentsExtension.js.map +1 -1
- package/dist/comments/CommentsExtension.mjs.map +1 -1
- package/dist/index.d.mts +14 -56
- package/dist/index.d.ts +14 -56
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/dist/toolbar/FloatingToolbar.js +0 -7
- package/dist/toolbar/FloatingToolbar.js.map +1 -1
- package/dist/toolbar/FloatingToolbar.mjs +0 -7
- package/dist/toolbar/FloatingToolbar.mjs.map +1 -1
- package/dist/toolbar/Toolbar.js +1 -35
- package/dist/toolbar/Toolbar.js.map +1 -1
- package/dist/toolbar/Toolbar.mjs +2 -36
- 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 +1 -21
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +2 -20
- package/dist/utils.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/package.json +6 -7
- package/src/styles/index.css +3 -322
- package/styles.css +1 -1
- package/styles.css.map +1 -1
- package/dist/ai/AiExtension.js +0 -299
- package/dist/ai/AiExtension.js.map +0 -1
- package/dist/ai/AiExtension.mjs +0 -296
- package/dist/ai/AiExtension.mjs.map +0 -1
- package/dist/ai/AiToolbar.js +0 -570
- package/dist/ai/AiToolbar.js.map +0 -1
- package/dist/ai/AiToolbar.mjs +0 -567
- 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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"}
|
package/dist/ai/AiToolbar.js
DELETED
|
@@ -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
|