@liveblocks/react-tiptap 2.16.0 → 2.16.1-ai
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 +141 -21
- package/dist/LiveblocksExtension.js.map +1 -1
- package/dist/LiveblocksExtension.mjs +141 -21
- package/dist/LiveblocksExtension.mjs.map +1 -1
- package/dist/ai/AiExtension.js +288 -0
- package/dist/ai/AiExtension.js.map +1 -0
- package/dist/ai/AiExtension.mjs +285 -0
- package/dist/ai/AiExtension.mjs.map +1 -0
- package/dist/ai/AiToolbar.js +540 -0
- package/dist/ai/AiToolbar.js.map +1 -0
- package/dist/ai/AiToolbar.mjs +537 -0
- package/dist/ai/AiToolbar.mjs.map +1 -0
- package/dist/comments/CommentsExtension.js.map +1 -1
- package/dist/comments/CommentsExtension.mjs.map +1 -1
- package/dist/index.d.mts +54 -14
- package/dist/index.d.ts +54 -14
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/toolbar/FloatingToolbar.js +7 -0
- package/dist/toolbar/FloatingToolbar.js.map +1 -1
- package/dist/toolbar/FloatingToolbar.mjs +7 -0
- package/dist/toolbar/FloatingToolbar.mjs.map +1 -1
- package/dist/toolbar/Toolbar.js +36 -2
- package/dist/toolbar/Toolbar.js.map +1 -1
- package/dist/toolbar/Toolbar.mjs +37 -3
- package/dist/toolbar/Toolbar.mjs.map +1 -1
- package/dist/toolbar/shared.js +4 -1
- package/dist/toolbar/shared.js.map +1 -1
- package/dist/toolbar/shared.mjs +5 -2
- package/dist/toolbar/shared.mjs.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils.js +29 -1
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +27 -2
- 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 +7 -6
- package/src/styles/index.css +319 -3
- package/styles.css +1 -1
- package/styles.css.map +1 -1
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var core = require('@liveblocks/core');
|
|
3
|
+
var core$1 = require('@liveblocks/core');
|
|
4
4
|
var react$1 = require('@liveblocks/react');
|
|
5
5
|
var _private = require('@liveblocks/react/_private');
|
|
6
6
|
var yjs$1 = require('@liveblocks/yjs');
|
|
7
|
-
var core
|
|
7
|
+
var core = require('@tiptap/core');
|
|
8
8
|
var Collaboration = require('@tiptap/extension-collaboration');
|
|
9
9
|
var CollaborationCursor = require('@tiptap/extension-collaboration-cursor');
|
|
10
10
|
var react = require('react');
|
|
11
11
|
var yjs = require('yjs');
|
|
12
|
+
var AiExtension = require('./ai/AiExtension.js');
|
|
12
13
|
var CommentsExtension = require('./comments/CommentsExtension.js');
|
|
13
14
|
var MentionExtension = require('./mentions/MentionExtension.js');
|
|
14
15
|
var types = require('./types.js');
|
|
15
16
|
|
|
16
17
|
const providersMap = /* @__PURE__ */ new Map();
|
|
17
18
|
const docMap = /* @__PURE__ */ new Map();
|
|
19
|
+
const pudMap = /* @__PURE__ */ new Map();
|
|
18
20
|
const DEFAULT_OPTIONS = {
|
|
19
21
|
field: "default",
|
|
20
22
|
comments: true,
|
|
@@ -67,6 +69,52 @@ function useIsEditorReady() {
|
|
|
67
69
|
);
|
|
68
70
|
return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
69
71
|
}
|
|
72
|
+
const YChangeMark = core.Mark.create({
|
|
73
|
+
name: "ychange",
|
|
74
|
+
inclusive: false,
|
|
75
|
+
parseHTML() {
|
|
76
|
+
return [{ tag: "ychange" }];
|
|
77
|
+
},
|
|
78
|
+
addAttributes() {
|
|
79
|
+
return {
|
|
80
|
+
user: {
|
|
81
|
+
default: null,
|
|
82
|
+
parseHTML: (element) => element.getAttribute("ychange_user") ?? null,
|
|
83
|
+
renderHTML: (attributes) => {
|
|
84
|
+
if (!attributes.user) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
return { "data-ychange-user": attributes.user };
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
type: {
|
|
91
|
+
default: null,
|
|
92
|
+
parseHTML: (element) => element.getAttribute("ychange_type") ?? null,
|
|
93
|
+
renderHTML: (attributes) => {
|
|
94
|
+
if (!attributes.type) {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
"data-ychange-type": attributes.type,
|
|
99
|
+
class: `lb-changed-${attributes.type}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
color: {
|
|
104
|
+
default: null,
|
|
105
|
+
parseHTML: (element) => {
|
|
106
|
+
return element.getAttribute("ychange_color") ?? null;
|
|
107
|
+
},
|
|
108
|
+
renderHTML: () => {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
renderHTML({ HTMLAttributes }) {
|
|
115
|
+
return ["ychange", HTMLAttributes, 0];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
70
118
|
const useLiveblocksExtension = (opts) => {
|
|
71
119
|
const options = {
|
|
72
120
|
...DEFAULT_OPTIONS,
|
|
@@ -74,6 +122,10 @@ const useLiveblocksExtension = (opts) => {
|
|
|
74
122
|
};
|
|
75
123
|
const [editor, setEditor] = react.useState(null);
|
|
76
124
|
const room = react$1.useRoom();
|
|
125
|
+
react$1.useCommentsErrorListener((error) => {
|
|
126
|
+
if (error instanceof _private.CreateThreadError) {
|
|
127
|
+
}
|
|
128
|
+
});
|
|
77
129
|
const isEditorReady = useIsEditorReady();
|
|
78
130
|
const client = react$1.useClient();
|
|
79
131
|
const store = _private.getUmbrellaStoreForClient(client);
|
|
@@ -90,12 +142,12 @@ const useLiveblocksExtension = (opts) => {
|
|
|
90
142
|
}
|
|
91
143
|
}, [isEditorReady, yjsProvider, options.initialContent, editor]);
|
|
92
144
|
_private.useReportTextEditor(
|
|
93
|
-
core.TextEditorType.TipTap,
|
|
145
|
+
core$1.TextEditorType.TipTap,
|
|
94
146
|
options.field ?? DEFAULT_OPTIONS.field
|
|
95
147
|
);
|
|
96
148
|
const createTextMention = _private.useCreateTextMention();
|
|
97
149
|
const deleteTextMention = _private.useDeleteTextMention();
|
|
98
|
-
return core
|
|
150
|
+
return core.Extension.create({
|
|
99
151
|
name: "liveblocksExtension",
|
|
100
152
|
onCreate() {
|
|
101
153
|
setEditor(this.editor);
|
|
@@ -112,26 +164,32 @@ const useLiveblocksExtension = (opts) => {
|
|
|
112
164
|
);
|
|
113
165
|
}
|
|
114
166
|
const self = room.getSelf();
|
|
167
|
+
const updateUser = ({
|
|
168
|
+
info,
|
|
169
|
+
id: userId
|
|
170
|
+
}) => {
|
|
171
|
+
if (!info) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const { user: storedUser } = this.storage.provider.awareness.getLocalState();
|
|
175
|
+
this.storage.permanentUserData?.setUserMapping(
|
|
176
|
+
this.storage.doc,
|
|
177
|
+
this.storage.doc.clientID,
|
|
178
|
+
userId ?? "Unknown"
|
|
179
|
+
);
|
|
180
|
+
if (info.name !== storedUser?.name || info.color !== storedUser?.color) {
|
|
181
|
+
this.editor.commands.updateUser({
|
|
182
|
+
name: info.name,
|
|
183
|
+
color: info.color
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
|
115
187
|
if (self?.info) {
|
|
116
|
-
|
|
117
|
-
name: self.info.name,
|
|
118
|
-
color: self.info.color
|
|
119
|
-
});
|
|
188
|
+
updateUser(self);
|
|
120
189
|
}
|
|
121
|
-
this.storage.unsubs.push(
|
|
122
|
-
room.events.self.subscribe(({ info }) => {
|
|
123
|
-
if (!info) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const { name, color } = info;
|
|
127
|
-
const { user } = this.storage.provider.awareness.getLocalState();
|
|
128
|
-
if (name !== user?.name || color !== user?.color) {
|
|
129
|
-
this.editor.commands.updateUser({ name, color });
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
);
|
|
190
|
+
this.storage.unsubs.push(room.events.self.subscribe(updateUser));
|
|
133
191
|
if (options.comments) {
|
|
134
|
-
const commentMarkType = core
|
|
192
|
+
const commentMarkType = core.getMarkType(
|
|
135
193
|
types.LIVEBLOCKS_COMMENT_MARK_TYPE,
|
|
136
194
|
this.editor.schema
|
|
137
195
|
);
|
|
@@ -176,10 +234,21 @@ const useLiveblocksExtension = (opts) => {
|
|
|
176
234
|
onDestroy() {
|
|
177
235
|
this.storage.unsubs.forEach((unsub) => unsub());
|
|
178
236
|
},
|
|
237
|
+
addGlobalAttributes() {
|
|
238
|
+
return [
|
|
239
|
+
{
|
|
240
|
+
types: ["paragraph", "heading"],
|
|
241
|
+
attributes: {
|
|
242
|
+
ychange: { default: null }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
];
|
|
246
|
+
},
|
|
179
247
|
addStorage() {
|
|
180
248
|
if (!providersMap.has(room.id)) {
|
|
181
249
|
const doc = new yjs.Doc();
|
|
182
250
|
docMap.set(room.id, doc);
|
|
251
|
+
pudMap.set(room.id, new yjs.PermanentUserData(doc));
|
|
183
252
|
providersMap.set(
|
|
184
253
|
room.id,
|
|
185
254
|
new yjs$1.LiveblocksYjsProvider(room, doc, {
|
|
@@ -190,12 +259,17 @@ const useLiveblocksExtension = (opts) => {
|
|
|
190
259
|
return {
|
|
191
260
|
doc: docMap.get(room.id),
|
|
192
261
|
provider: providersMap.get(room.id),
|
|
262
|
+
permanentUserData: pudMap.get(room.id),
|
|
193
263
|
unsubs: []
|
|
194
264
|
};
|
|
195
265
|
},
|
|
196
266
|
addExtensions() {
|
|
197
267
|
const extensions = [
|
|
268
|
+
YChangeMark,
|
|
198
269
|
LiveblocksCollab.configure({
|
|
270
|
+
ySyncOptions: {
|
|
271
|
+
permanentUserData: this.storage.permanentUserData
|
|
272
|
+
},
|
|
199
273
|
document: this.storage.doc,
|
|
200
274
|
field: options.field
|
|
201
275
|
}),
|
|
@@ -214,6 +288,52 @@ const useLiveblocksExtension = (opts) => {
|
|
|
214
288
|
})
|
|
215
289
|
);
|
|
216
290
|
}
|
|
291
|
+
if (options.ai) {
|
|
292
|
+
const resolveAiPrompt = async ({
|
|
293
|
+
prompt,
|
|
294
|
+
selectionText,
|
|
295
|
+
context,
|
|
296
|
+
signal,
|
|
297
|
+
retryCount = 0
|
|
298
|
+
}) => {
|
|
299
|
+
const result = await room[core$1.kInternal].executeContextualPrompt({
|
|
300
|
+
prompt,
|
|
301
|
+
selectionText,
|
|
302
|
+
context,
|
|
303
|
+
signal
|
|
304
|
+
});
|
|
305
|
+
if (retryCount > 3) {
|
|
306
|
+
throw new Error("Failed to resolve AI prompt");
|
|
307
|
+
}
|
|
308
|
+
const retry = () => {
|
|
309
|
+
return resolveAiPrompt({
|
|
310
|
+
prompt,
|
|
311
|
+
selectionText,
|
|
312
|
+
context,
|
|
313
|
+
signal,
|
|
314
|
+
retryCount: retryCount + 1
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
const parsedResponse = JSON.parse(result);
|
|
319
|
+
const type = typeof parsedResponse.type === "string" ? parsedResponse.type : "";
|
|
320
|
+
if (typeof parsedResponse.content === "string" && ["insert", "modification", "other"].includes(type)) {
|
|
321
|
+
return parsedResponse;
|
|
322
|
+
}
|
|
323
|
+
return retry();
|
|
324
|
+
} catch (e) {
|
|
325
|
+
return retry();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
extensions.push(
|
|
329
|
+
AiExtension.AiExtension.configure({
|
|
330
|
+
resolveAiPrompt,
|
|
331
|
+
...typeof options.ai === "boolean" ? {} : options.ai,
|
|
332
|
+
doc: this.storage.doc,
|
|
333
|
+
pud: this.storage.permanentUserData
|
|
334
|
+
})
|
|
335
|
+
);
|
|
336
|
+
}
|
|
217
337
|
return extensions;
|
|
218
338
|
}
|
|
219
339
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, TextEditorType } from \"@liveblocks/core\";\nimport { useClient, useRoom } from \"@liveblocks/react\";\nimport {\n getUmbrellaStoreForClient,\n useCreateTextMention,\n useDeleteTextMention,\n useReportTextEditor,\n useYjsProvider,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension, Content, Editor } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback, useEffect, useState, useSyncExternalStore } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments?: boolean; // | CommentsConfiguration\n mentions?: boolean; // | MentionsConfiguration\n offlineSupport_experimental?: boolean;\n initialContent?: Content;\n};\n\nconst DEFAULT_OPTIONS: WithRequired<LiveblocksExtensionOptions, \"field\"> = {\n field: \"default\",\n comments: true,\n mentions: true,\n offlineSupport_experimental: false,\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\n/**\n * Returns whether the editor has loaded the initial text contents from the\n * server and is ready to be used.\n *\n */\nexport function useIsEditorReady(): boolean {\n const yjsProvider = useYjsProvider();\n\n const getSnapshot = useCallback(() => {\n const status = yjsProvider?.getStatus();\n return status === \"synchronizing\" || status === \"synchronized\";\n }, [yjsProvider]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n if (yjsProvider === undefined) return () => {};\n yjsProvider.on(\"status\", callback);\n return () => {\n yjsProvider.off(\"status\", callback);\n };\n },\n [yjsProvider]\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport const useLiveblocksExtension = (\n opts?: LiveblocksExtensionOptions\n): Extension => {\n const options = {\n ...DEFAULT_OPTIONS,\n ...opts,\n };\n const [editor, setEditor] = useState<Editor | null>(null);\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n // useErrorListener((error) => {\n // // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n // if (\n // error.context.type === \"CREATE_THREAD_ERROR\" &&\n // error.context.roomId === room.id\n // ) {\n // handleThreadDelete(error.context.threadId);\n // }\n // });\n\n const isEditorReady = useIsEditorReady();\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n const yjsProvider = useYjsProvider();\n\n // If the user provided initialContent, wait for ready and then set it\n useEffect(() => {\n if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)\n return;\n\n // As noted in the tiptap documentation, you may not set initial content with collaboration.\n // The docs provide the following workaround:\n const ydoc = (yjsProvider as LiveblocksYjsProvider).getYDoc();\n const hasContentSet = ydoc.getMap(\"liveblocks_config\").get(\"hasContentSet\");\n if (!hasContentSet) {\n ydoc.getMap(\"liveblocks_config\").set(\"hasContentSet\", true);\n editor.commands.setContent(options.initialContent);\n }\n }, [isEditorReady, yjsProvider, options.initialContent, editor]);\n\n useReportTextEditor(\n TextEditorType.TipTap,\n options.field ?? DEFAULT_OPTIONS.field\n );\n\n const createTextMention = useCreateTextMention();\n const deleteTextMention = useDeleteTextMention();\n\n return Extension.create<\n never,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n setEditor(this.editor);\n if (this.editor.options.content) {\n console.warn(\n \"[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options.\"\n );\n }\n if (\n options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.outputs.threads.subscribe(() => {\n const threadMap = new Map(\n store.outputs.threads\n .get()\n .findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(\n room.id,\n new LiveblocksYjsProvider(room, doc, {\n offlineSupport_experimental: options.offlineSupport_experimental,\n })\n );\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (options.comments) {\n extensions.push(CommentsExtension);\n }\n if (options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention: createTextMention,\n onDeleteMention: deleteTextMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useYjsProvider","useCallback","useSyncExternalStore","useState","useRoom","useClient","getUmbrellaStoreForClient","useEffect","useReportTextEditor","TextEditorType","useCreateTextMention","useDeleteTextMention","Extension","getMarkType","LIVEBLOCKS_COMMENT_MARK_TYPE","Doc","LiveblocksYjsProvider","CommentsExtension","MentionExtension"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import type {\n BaseUserMeta,\n IUserInfo,\n JsonObject,\n User,\n} from \"@liveblocks/core\";\nimport { kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n useCreateTextMention,\n useDeleteTextMention,\n useReportTextEditor,\n useYjsProvider,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension, Editor } from \"@tiptap/core\";\nimport { Extension, getMarkType, Mark } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback, useEffect, useState, useSyncExternalStore } from \"react\";\nimport { Doc, PermanentUserData } from \"yjs\";\n\nimport { AiExtension } from \"./ai/AiExtension\";\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport type {\n AiResponse,\n LiveblocksExtensionOptions,\n LiveblocksExtensionStorage,\n ResolveAiPromptArgs,\n} from \"./types\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };\n\nconst pudMap = new Map<string, PermanentUserData>();\n\nconst DEFAULT_OPTIONS: WithRequired<LiveblocksExtensionOptions, \"field\"> = {\n field: \"default\",\n comments: true,\n mentions: true,\n offlineSupport_experimental: false,\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\n/**\n * Returns whether the editor has loaded the initial text contents from the\n * server and is ready to be used.\n *\n */\nexport function useIsEditorReady(): boolean {\n const yjsProvider = useYjsProvider();\n\n const getSnapshot = useCallback(() => {\n const status = yjsProvider?.getStatus();\n return status === \"synchronizing\" || status === \"synchronized\";\n }, [yjsProvider]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n if (yjsProvider === undefined) return () => {};\n yjsProvider.on(\"status\", callback);\n return () => {\n yjsProvider.off(\"status\", callback);\n };\n },\n [yjsProvider]\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nconst YChangeMark = Mark.create({\n name: \"ychange\",\n inclusive: false,\n parseHTML() {\n return [{ tag: \"ychange\" }];\n },\n addAttributes() {\n return {\n user: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"ychange_user\") ?? null,\n renderHTML: (attributes: { user: string | null }) => {\n if (!attributes.user) {\n return {};\n }\n return { \"data-ychange-user\": attributes.user };\n },\n },\n type: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"ychange_type\") ?? null,\n renderHTML: (attributes: { type: string | null }) => {\n if (!attributes.type) {\n return {};\n }\n return {\n \"data-ychange-type\": attributes.type,\n class: `lb-changed-${attributes.type}`,\n };\n },\n },\n color: {\n default: null,\n parseHTML: (element) => {\n return element.getAttribute(\"ychange_color\") ?? null;\n },\n renderHTML: () => {\n // attributes: { color: { light: string; dark: string } | null }\n return {}; // we don't need this color attribute for now\n },\n },\n };\n },\n renderHTML({ HTMLAttributes }) {\n return [\"ychange\", HTMLAttributes, 0];\n },\n});\n\nexport const useLiveblocksExtension = (\n opts?: LiveblocksExtensionOptions\n): Extension => {\n const options = {\n ...DEFAULT_OPTIONS,\n ...opts,\n };\n const [editor, setEditor] = useState<Editor | null>(null);\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const isEditorReady = useIsEditorReady();\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n const yjsProvider = useYjsProvider();\n\n // If the user provided initialContent, wait for ready and then set it\n useEffect(() => {\n if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)\n return;\n\n // As noted in the tiptap documentation, you may not set initial content with collaboration.\n // The docs provide the following workaround:\n const ydoc = (yjsProvider as LiveblocksYjsProvider).getYDoc();\n const hasContentSet = ydoc.getMap(\"liveblocks_config\").get(\"hasContentSet\");\n if (!hasContentSet) {\n ydoc.getMap(\"liveblocks_config\").set(\"hasContentSet\", true);\n editor.commands.setContent(options.initialContent);\n }\n }, [isEditorReady, yjsProvider, options.initialContent, editor]);\n\n useReportTextEditor(\n TextEditorType.TipTap,\n options.field ?? DEFAULT_OPTIONS.field\n );\n\n const createTextMention = useCreateTextMention();\n const deleteTextMention = useDeleteTextMention();\n\n return Extension.create<never, LiveblocksExtensionStorage>({\n name: \"liveblocksExtension\",\n\n onCreate() {\n setEditor(this.editor);\n if (this.editor.options.content) {\n console.warn(\n \"[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options.\"\n );\n }\n if (\n options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n const updateUser = ({\n info,\n id: userId,\n }: User<JsonObject, BaseUserMeta>) => {\n if (!info) {\n return;\n }\n const { user: storedUser } =\n this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n this.storage.permanentUserData?.setUserMapping(\n this.storage.doc,\n this.storage.doc.clientID,\n userId ?? \"Unknown\" // TODO: change this to the user's ID so we can map it to the user's name\n );\n if (\n info.name !== storedUser?.name ||\n info.color !== storedUser?.color\n ) {\n this.editor.commands.updateUser({\n name: info.name,\n color: info.color,\n });\n }\n };\n // if we already have user info, we update the user\n if (self?.info) {\n updateUser(self);\n }\n // we also listen in case the user info changes\n this.storage.unsubs.push(room.events.self.subscribe(updateUser));\n if (options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.outputs.threads.subscribe(() => {\n const threadMap = new Map(\n store.outputs.threads\n .get()\n .findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addGlobalAttributes() {\n return [\n {\n types: [\"paragraph\", \"heading\"],\n attributes: {\n ychange: { default: null },\n },\n },\n ];\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n pudMap.set(room.id, new PermanentUserData(doc));\n providersMap.set(\n room.id,\n new LiveblocksYjsProvider(room, doc, {\n offlineSupport_experimental: options.offlineSupport_experimental,\n })\n );\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n permanentUserData: pudMap.get(room.id)!,\n unsubs: [],\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n YChangeMark,\n LiveblocksCollab.configure({\n ySyncOptions: {\n permanentUserData: this.storage.permanentUserData,\n },\n document: this.storage.doc,\n field: options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider,\n }),\n ];\n\n if (options.comments) {\n extensions.push(CommentsExtension);\n }\n if (options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention: createTextMention,\n onDeleteMention: deleteTextMention,\n })\n );\n }\n if (options.ai) {\n const resolveAiPrompt = async ({\n prompt,\n selectionText,\n context,\n signal,\n retryCount = 0,\n }: ResolveAiPromptArgs): Promise<AiResponse> => {\n const result = await room[kInternal].executeContextualPrompt({\n prompt,\n selectionText,\n context,\n signal,\n });\n\n if (retryCount > 3) {\n throw new Error(\"Failed to resolve AI prompt\");\n }\n\n const retry = () => {\n return resolveAiPrompt({\n prompt,\n selectionText,\n context,\n signal,\n retryCount: retryCount + 1,\n });\n };\n // TODO: proper backoff\n try {\n const parsedResponse = JSON.parse(result) as JsonObject;\n const type =\n typeof parsedResponse.type === \"string\"\n ? parsedResponse.type\n : \"\";\n if (\n typeof parsedResponse.content === \"string\" &&\n [\"insert\", \"modification\", \"other\"].includes(type)\n ) {\n return parsedResponse as AiResponse;\n }\n return retry();\n } catch (e) {\n return retry();\n }\n };\n extensions.push(\n AiExtension.configure({\n resolveAiPrompt,\n ...(typeof options.ai === \"boolean\" ? {} : options.ai),\n doc: this.storage.doc,\n pud: this.storage.permanentUserData,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useYjsProvider","useCallback","useSyncExternalStore","Mark","useState","useRoom","useCommentsErrorListener","CreateThreadError","useClient","getUmbrellaStoreForClient","useEffect","useReportTextEditor","TextEditorType","useCreateTextMention","useDeleteTextMention","Extension","getMarkType","LIVEBLOCKS_COMMENT_MARK_TYPE","Doc","PermanentUserData","LiveblocksYjsProvider","CommentsExtension","MentionExtension","kInternal","AiExtension"],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { TextEditorType } from '@liveblocks/core';
|
|
2
|
-
import { useRoom, useClient } from '@liveblocks/react';
|
|
3
|
-
import { useYjsProvider, getUmbrellaStoreForClient, useReportTextEditor, useCreateTextMention, useDeleteTextMention } from '@liveblocks/react/_private';
|
|
1
|
+
import { TextEditorType, kInternal } from '@liveblocks/core';
|
|
2
|
+
import { useRoom, useCommentsErrorListener, useClient } from '@liveblocks/react';
|
|
3
|
+
import { useYjsProvider, CreateThreadError, getUmbrellaStoreForClient, useReportTextEditor, useCreateTextMention, useDeleteTextMention } from '@liveblocks/react/_private';
|
|
4
4
|
import { LiveblocksYjsProvider } from '@liveblocks/yjs';
|
|
5
|
-
import { Extension, getMarkType } from '@tiptap/core';
|
|
5
|
+
import { Mark, Extension, getMarkType } from '@tiptap/core';
|
|
6
6
|
import Collaboration from '@tiptap/extension-collaboration';
|
|
7
7
|
import CollaborationCursor from '@tiptap/extension-collaboration-cursor';
|
|
8
8
|
import { useCallback, useSyncExternalStore, useState, useEffect } from 'react';
|
|
9
|
-
import { Doc } from 'yjs';
|
|
9
|
+
import { Doc, PermanentUserData } from 'yjs';
|
|
10
|
+
import { AiExtension } from './ai/AiExtension.mjs';
|
|
10
11
|
import { CommentsExtension } from './comments/CommentsExtension.mjs';
|
|
11
12
|
import { MentionExtension } from './mentions/MentionExtension.mjs';
|
|
12
13
|
import { LIVEBLOCKS_COMMENT_MARK_TYPE } from './types.mjs';
|
|
13
14
|
|
|
14
15
|
const providersMap = /* @__PURE__ */ new Map();
|
|
15
16
|
const docMap = /* @__PURE__ */ new Map();
|
|
17
|
+
const pudMap = /* @__PURE__ */ new Map();
|
|
16
18
|
const DEFAULT_OPTIONS = {
|
|
17
19
|
field: "default",
|
|
18
20
|
comments: true,
|
|
@@ -65,6 +67,52 @@ function useIsEditorReady() {
|
|
|
65
67
|
);
|
|
66
68
|
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
67
69
|
}
|
|
70
|
+
const YChangeMark = Mark.create({
|
|
71
|
+
name: "ychange",
|
|
72
|
+
inclusive: false,
|
|
73
|
+
parseHTML() {
|
|
74
|
+
return [{ tag: "ychange" }];
|
|
75
|
+
},
|
|
76
|
+
addAttributes() {
|
|
77
|
+
return {
|
|
78
|
+
user: {
|
|
79
|
+
default: null,
|
|
80
|
+
parseHTML: (element) => element.getAttribute("ychange_user") ?? null,
|
|
81
|
+
renderHTML: (attributes) => {
|
|
82
|
+
if (!attributes.user) {
|
|
83
|
+
return {};
|
|
84
|
+
}
|
|
85
|
+
return { "data-ychange-user": attributes.user };
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
type: {
|
|
89
|
+
default: null,
|
|
90
|
+
parseHTML: (element) => element.getAttribute("ychange_type") ?? null,
|
|
91
|
+
renderHTML: (attributes) => {
|
|
92
|
+
if (!attributes.type) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
"data-ychange-type": attributes.type,
|
|
97
|
+
class: `lb-changed-${attributes.type}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
color: {
|
|
102
|
+
default: null,
|
|
103
|
+
parseHTML: (element) => {
|
|
104
|
+
return element.getAttribute("ychange_color") ?? null;
|
|
105
|
+
},
|
|
106
|
+
renderHTML: () => {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
renderHTML({ HTMLAttributes }) {
|
|
113
|
+
return ["ychange", HTMLAttributes, 0];
|
|
114
|
+
}
|
|
115
|
+
});
|
|
68
116
|
const useLiveblocksExtension = (opts) => {
|
|
69
117
|
const options = {
|
|
70
118
|
...DEFAULT_OPTIONS,
|
|
@@ -72,6 +120,10 @@ const useLiveblocksExtension = (opts) => {
|
|
|
72
120
|
};
|
|
73
121
|
const [editor, setEditor] = useState(null);
|
|
74
122
|
const room = useRoom();
|
|
123
|
+
useCommentsErrorListener((error) => {
|
|
124
|
+
if (error instanceof CreateThreadError) {
|
|
125
|
+
}
|
|
126
|
+
});
|
|
75
127
|
const isEditorReady = useIsEditorReady();
|
|
76
128
|
const client = useClient();
|
|
77
129
|
const store = getUmbrellaStoreForClient(client);
|
|
@@ -110,24 +162,30 @@ const useLiveblocksExtension = (opts) => {
|
|
|
110
162
|
);
|
|
111
163
|
}
|
|
112
164
|
const self = room.getSelf();
|
|
165
|
+
const updateUser = ({
|
|
166
|
+
info,
|
|
167
|
+
id: userId
|
|
168
|
+
}) => {
|
|
169
|
+
if (!info) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const { user: storedUser } = this.storage.provider.awareness.getLocalState();
|
|
173
|
+
this.storage.permanentUserData?.setUserMapping(
|
|
174
|
+
this.storage.doc,
|
|
175
|
+
this.storage.doc.clientID,
|
|
176
|
+
userId ?? "Unknown"
|
|
177
|
+
);
|
|
178
|
+
if (info.name !== storedUser?.name || info.color !== storedUser?.color) {
|
|
179
|
+
this.editor.commands.updateUser({
|
|
180
|
+
name: info.name,
|
|
181
|
+
color: info.color
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
113
185
|
if (self?.info) {
|
|
114
|
-
|
|
115
|
-
name: self.info.name,
|
|
116
|
-
color: self.info.color
|
|
117
|
-
});
|
|
186
|
+
updateUser(self);
|
|
118
187
|
}
|
|
119
|
-
this.storage.unsubs.push(
|
|
120
|
-
room.events.self.subscribe(({ info }) => {
|
|
121
|
-
if (!info) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const { name, color } = info;
|
|
125
|
-
const { user } = this.storage.provider.awareness.getLocalState();
|
|
126
|
-
if (name !== user?.name || color !== user?.color) {
|
|
127
|
-
this.editor.commands.updateUser({ name, color });
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
);
|
|
188
|
+
this.storage.unsubs.push(room.events.self.subscribe(updateUser));
|
|
131
189
|
if (options.comments) {
|
|
132
190
|
const commentMarkType = getMarkType(
|
|
133
191
|
LIVEBLOCKS_COMMENT_MARK_TYPE,
|
|
@@ -174,10 +232,21 @@ const useLiveblocksExtension = (opts) => {
|
|
|
174
232
|
onDestroy() {
|
|
175
233
|
this.storage.unsubs.forEach((unsub) => unsub());
|
|
176
234
|
},
|
|
235
|
+
addGlobalAttributes() {
|
|
236
|
+
return [
|
|
237
|
+
{
|
|
238
|
+
types: ["paragraph", "heading"],
|
|
239
|
+
attributes: {
|
|
240
|
+
ychange: { default: null }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
];
|
|
244
|
+
},
|
|
177
245
|
addStorage() {
|
|
178
246
|
if (!providersMap.has(room.id)) {
|
|
179
247
|
const doc = new Doc();
|
|
180
248
|
docMap.set(room.id, doc);
|
|
249
|
+
pudMap.set(room.id, new PermanentUserData(doc));
|
|
181
250
|
providersMap.set(
|
|
182
251
|
room.id,
|
|
183
252
|
new LiveblocksYjsProvider(room, doc, {
|
|
@@ -188,12 +257,17 @@ const useLiveblocksExtension = (opts) => {
|
|
|
188
257
|
return {
|
|
189
258
|
doc: docMap.get(room.id),
|
|
190
259
|
provider: providersMap.get(room.id),
|
|
260
|
+
permanentUserData: pudMap.get(room.id),
|
|
191
261
|
unsubs: []
|
|
192
262
|
};
|
|
193
263
|
},
|
|
194
264
|
addExtensions() {
|
|
195
265
|
const extensions = [
|
|
266
|
+
YChangeMark,
|
|
196
267
|
LiveblocksCollab.configure({
|
|
268
|
+
ySyncOptions: {
|
|
269
|
+
permanentUserData: this.storage.permanentUserData
|
|
270
|
+
},
|
|
197
271
|
document: this.storage.doc,
|
|
198
272
|
field: options.field
|
|
199
273
|
}),
|
|
@@ -212,6 +286,52 @@ const useLiveblocksExtension = (opts) => {
|
|
|
212
286
|
})
|
|
213
287
|
);
|
|
214
288
|
}
|
|
289
|
+
if (options.ai) {
|
|
290
|
+
const resolveAiPrompt = async ({
|
|
291
|
+
prompt,
|
|
292
|
+
selectionText,
|
|
293
|
+
context,
|
|
294
|
+
signal,
|
|
295
|
+
retryCount = 0
|
|
296
|
+
}) => {
|
|
297
|
+
const result = await room[kInternal].executeContextualPrompt({
|
|
298
|
+
prompt,
|
|
299
|
+
selectionText,
|
|
300
|
+
context,
|
|
301
|
+
signal
|
|
302
|
+
});
|
|
303
|
+
if (retryCount > 3) {
|
|
304
|
+
throw new Error("Failed to resolve AI prompt");
|
|
305
|
+
}
|
|
306
|
+
const retry = () => {
|
|
307
|
+
return resolveAiPrompt({
|
|
308
|
+
prompt,
|
|
309
|
+
selectionText,
|
|
310
|
+
context,
|
|
311
|
+
signal,
|
|
312
|
+
retryCount: retryCount + 1
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
try {
|
|
316
|
+
const parsedResponse = JSON.parse(result);
|
|
317
|
+
const type = typeof parsedResponse.type === "string" ? parsedResponse.type : "";
|
|
318
|
+
if (typeof parsedResponse.content === "string" && ["insert", "modification", "other"].includes(type)) {
|
|
319
|
+
return parsedResponse;
|
|
320
|
+
}
|
|
321
|
+
return retry();
|
|
322
|
+
} catch (e) {
|
|
323
|
+
return retry();
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
extensions.push(
|
|
327
|
+
AiExtension.configure({
|
|
328
|
+
resolveAiPrompt,
|
|
329
|
+
...typeof options.ai === "boolean" ? {} : options.ai,
|
|
330
|
+
doc: this.storage.doc,
|
|
331
|
+
pud: this.storage.permanentUserData
|
|
332
|
+
})
|
|
333
|
+
);
|
|
334
|
+
}
|
|
215
335
|
return extensions;
|
|
216
336
|
}
|
|
217
337
|
});
|