@liveblocks/react-tiptap 2.16.1-ai2 → 2.16.1-ai4

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.
@@ -4,8 +4,9 @@ import { Slice, Fragment } from '@tiptap/pm/model';
4
4
  import { Plugin } from '@tiptap/pm/state';
5
5
  import { DecorationSet, Decoration } from '@tiptap/pm/view';
6
6
  import { ySyncPluginKey, yXmlFragmentToProseMirrorFragment } from 'y-prosemirror';
7
- import { createDocFromSnapshot, snapshot, emptySnapshot, equalSnapshots } from 'yjs';
7
+ import { createDocFromSnapshot, emptySnapshot, snapshot, equalSnapshots } from 'yjs';
8
8
  import { AI_TOOLBAR_SELECTION_PLUGIN } from '../types.mjs';
9
+ import { getContextualPromptContext } from '../utils.mjs';
9
10
 
10
11
  const DEFAULT_AI_NAME = "AI";
11
12
  const DEFAULT_STATE = { phase: "closed" };
@@ -17,13 +18,52 @@ function getYjsBinding(editor) {
17
18
  function getLiveblocksYjsProvider(editor) {
18
19
  return editor.extensionStorage.liveblocksExtension?.provider;
19
20
  }
21
+ function isContextualPromptDiffResponse(response) {
22
+ return response.type === "replace" || response.type === "insert";
23
+ }
24
+ function isResolveContextualPromptResponse(response) {
25
+ return typeof response === "object" && response !== null && typeof response.text === "string" && typeof response.type === "string" && ["insert", "replace", "other"].includes(response.type);
26
+ }
27
+ function getRevertTransaction(tr, editor, storage, doc) {
28
+ if (storage.snapshot) {
29
+ const binding = getYjsBinding(editor);
30
+ if (binding) {
31
+ binding.mapping.clear();
32
+ const docFromSnapshot = createDocFromSnapshot(
33
+ binding.doc,
34
+ storage.snapshot
35
+ );
36
+ const type = docFromSnapshot.getXmlFragment("default");
37
+ const fragmentContent = yXmlFragmentToProseMirrorFragment(
38
+ type,
39
+ editor.state.schema
40
+ );
41
+ tr.setMeta("addToHistory", false);
42
+ tr.replace(
43
+ 0,
44
+ editor.state.doc.content.size,
45
+ new Slice(Fragment.from(fragmentContent), 0, 0)
46
+ );
47
+ tr.setMeta(ySyncPluginKey, {
48
+ snapshot: null,
49
+ prevSnapshot: null
50
+ });
51
+ if (doc) {
52
+ doc.gc = true;
53
+ }
54
+ storage.snapshot = void 0;
55
+ return tr;
56
+ }
57
+ }
58
+ return null;
59
+ }
20
60
  const AiExtension = Extension.create({
21
61
  name: "liveblocksAi",
22
62
  addOptions() {
23
63
  return {
24
64
  doc: void 0,
25
65
  pud: void 0,
26
- resolveAiPrompt: () => Promise.reject(),
66
+ resolveContextualPrompt: () => Promise.reject(),
27
67
  name: DEFAULT_AI_NAME
28
68
  };
29
69
  },
@@ -43,73 +83,74 @@ const AiExtension = Extension.create({
43
83
  }
44
84
  return true;
45
85
  },
46
- $acceptAiToolbarOutput: () => ({ tr }) => {
86
+ $acceptAiToolbarResponse: () => ({ tr, view }) => {
47
87
  const currentState = this.storage.state;
48
88
  if (currentState.phase !== "reviewing") {
49
89
  return false;
50
90
  }
51
- const binding = getYjsBinding(this.editor);
52
- if (!binding) {
53
- return false;
91
+ if (isContextualPromptDiffResponse(currentState.response)) {
92
+ const binding = getYjsBinding(this.editor);
93
+ if (!binding) {
94
+ return false;
95
+ }
96
+ const fragmentContent = yXmlFragmentToProseMirrorFragment(
97
+ binding.type,
98
+ this.editor.state.schema
99
+ );
100
+ tr.setMeta("addToHistory", false);
101
+ tr.replace(
102
+ 0,
103
+ this.editor.state.doc.content.size,
104
+ new Slice(Fragment.from(fragmentContent), 0, 0)
105
+ );
106
+ tr.setMeta(ySyncPluginKey, {
107
+ snapshot: null,
108
+ prevSnapshot: null
109
+ });
110
+ this.storage.snapshot = void 0;
111
+ } else {
112
+ const block = this.editor.schema.nodes.paragraph || Object.values(this.editor.schema.nodes).find(
113
+ (node) => node.isBlock
114
+ );
115
+ if (!block) {
116
+ throw new Error("Could not insert a new paragraph.");
117
+ }
118
+ const paragraph = block.create(
119
+ {},
120
+ this.editor.schema.text(currentState.response.text)
121
+ );
122
+ tr.insert(this.editor.state.selection.$to.end() + 1, paragraph);
123
+ view.dispatch(tr);
124
+ tr.setMeta("preventDispatch", true);
54
125
  }
55
- const fragmentContent = yXmlFragmentToProseMirrorFragment(
56
- binding.type,
57
- this.editor.state.schema
58
- );
59
- tr.setMeta("addToHistory", false);
60
- tr.replace(
61
- 0,
62
- this.editor.state.doc.content.size,
63
- new Slice(Fragment.from(fragmentContent), 0, 0)
64
- );
65
- tr.setMeta(ySyncPluginKey, {
66
- snapshot: null,
67
- prevSnapshot: null
68
- });
69
126
  getLiveblocksYjsProvider(this.editor)?.unpause();
70
127
  this.editor.setEditable(true);
71
- this.storage.snapshot = void 0;
72
128
  this.storage.state = { phase: "closed" };
73
129
  return true;
74
130
  },
75
- $closeAiToolbar: () => ({ tr }) => {
131
+ $closeAiToolbar: () => ({ tr, view }) => {
76
132
  const currentState = this.storage.state;
133
+ if (currentState.phase === "closed") {
134
+ return false;
135
+ }
77
136
  if (currentState.phase === "thinking") {
78
137
  currentState.abortController.abort();
79
138
  }
80
139
  if (currentState.phase === "thinking" || currentState.phase === "reviewing") {
81
- if (this.storage.snapshot) {
82
- const binding = getYjsBinding(this.editor);
83
- if (binding) {
84
- binding.mapping.clear();
85
- const docFromSnapshot = createDocFromSnapshot(
86
- binding.doc,
87
- this.storage.snapshot
88
- );
89
- const type = docFromSnapshot.getXmlFragment("default");
90
- const fragmentContent = yXmlFragmentToProseMirrorFragment(
91
- type,
92
- this.editor.state.schema
93
- );
94
- tr.setMeta("addToHistory", false);
95
- tr.replace(
96
- 0,
97
- this.editor.state.doc.content.size,
98
- new Slice(Fragment.from(fragmentContent), 0, 0)
99
- );
100
- tr.setMeta(ySyncPluginKey, {
101
- snapshot: null,
102
- prevSnapshot: null
103
- });
104
- getLiveblocksYjsProvider(this.editor)?.unpause();
105
- if (this.options.doc) {
106
- this.options.doc.gc = true;
107
- }
108
- this.storage.snapshot = void 0;
109
- }
140
+ const revertTr = getRevertTransaction(
141
+ tr,
142
+ this.editor,
143
+ this.storage,
144
+ this.options.doc
145
+ );
146
+ if (revertTr) {
147
+ view.dispatch(revertTr);
148
+ tr.setMeta("preventDispatch", true);
110
149
  }
111
- this.editor.setEditable(true);
112
150
  }
151
+ this.editor.commands.setTextSelection(currentState.initialSelection);
152
+ getLiveblocksYjsProvider(this.editor)?.unpause();
153
+ this.editor.setEditable(true);
113
154
  this.storage.state = { phase: "closed" };
114
155
  return true;
115
156
  },
@@ -123,56 +164,84 @@ const AiExtension = Extension.create({
123
164
  }
124
165
  this.storage.state = {
125
166
  phase: "asking",
167
+ initialSelection: {
168
+ from: this.editor.state.selection.from,
169
+ to: this.editor.state.selection.to
170
+ },
126
171
  customPrompt: ""
127
172
  };
128
173
  return true;
129
174
  },
130
- $startAiToolbarThinking: (prompt) => () => {
175
+ $startAiToolbarThinking: (prompt, withPreviousResponse) => ({ tr, view }) => {
131
176
  const currentState = this.storage.state;
132
177
  if (currentState.phase === "thinking") {
133
178
  return false;
134
179
  }
135
- if (currentState.phase === "reviewing") {
136
- return this.editor.commands.$closeAiToolbar();
137
- }
138
180
  if (this.editor.isFocused) {
139
181
  this.editor.commands.blur();
140
182
  }
141
183
  const abortController = new AbortController();
142
184
  const provider = getLiveblocksYjsProvider(this.editor);
185
+ if (currentState.phase === "reviewing") {
186
+ const revertTr = getRevertTransaction(
187
+ tr,
188
+ this.editor,
189
+ this.storage,
190
+ this.options.doc
191
+ );
192
+ if (revertTr) {
193
+ view.dispatch(revertTr);
194
+ tr.setMeta("preventDispatch", true);
195
+ }
196
+ this.editor.commands.setTextSelection(
197
+ currentState.initialSelection
198
+ );
199
+ }
143
200
  this.storage.state = {
144
201
  phase: "thinking",
202
+ initialSelection: currentState.initialSelection ?? {
203
+ from: this.editor.state.selection.from,
204
+ to: this.editor.state.selection.to
205
+ },
145
206
  customPrompt: currentState.customPrompt ?? "",
146
207
  prompt,
147
- abortController
208
+ abortController,
209
+ previousResponse: currentState.response
148
210
  };
149
211
  this.editor.setEditable(false);
150
212
  autoRetry(
151
213
  async () => {
152
214
  await provider?.pause();
153
- const { from, to } = this.editor.state.selection.empty ? {
154
- from: Math.max(this.editor.state.selection.to - 30, 0),
155
- to: this.editor.state.selection.to
156
- } : this.editor.state.selection;
157
- return this.options.resolveAiPrompt({
215
+ const response = await this.options.resolveContextualPrompt({
158
216
  prompt,
159
- selectionText: this.editor.state.doc.textBetween(from, to, " "),
160
- context: this.editor.getText().slice(0, 3e3),
161
- signal: abortController.signal
217
+ context: getContextualPromptContext(this.editor, 3e3),
218
+ signal: abortController.signal,
219
+ previous: withPreviousResponse && currentState.phase === "reviewing" ? {
220
+ prompt: currentState.prompt,
221
+ response: {
222
+ type: currentState.response?.type,
223
+ text: currentState.response?.text
224
+ }
225
+ } : void 0
162
226
  });
227
+ if (isResolveContextualPromptResponse(response)) {
228
+ return response;
229
+ } else {
230
+ throw new Error("Failed to resolve AI prompt.");
231
+ }
163
232
  },
164
233
  RESOLVE_AI_PROMPT_RETRY_ATTEMPTS,
165
234
  RESOLVE_AI_PROMPT_RETRY_DELAYS,
166
235
  (error) => {
167
236
  return abortController.signal.aborted || error instanceof HttpError && error.status >= 400 && error.status < 500;
168
237
  }
169
- ).then((output) => {
238
+ ).then((response) => {
170
239
  if (abortController.signal.aborted) {
171
240
  return;
172
241
  }
173
242
  this.editor.commands._handleAiToolbarThinkingSuccess({
174
- type: output.type,
175
- text: output.content
243
+ type: response.type,
244
+ text: response.text
176
245
  });
177
246
  }).catch((error) => {
178
247
  if (abortController.signal.aborted) {
@@ -191,43 +260,64 @@ const AiExtension = Extension.create({
191
260
  this.editor.setEditable(true);
192
261
  this.storage.state = {
193
262
  phase: "asking",
263
+ initialSelection: currentState.initialSelection,
194
264
  customPrompt: currentState.prompt === currentState.customPrompt ? currentState.customPrompt : ""
195
265
  };
196
266
  return true;
197
267
  },
198
- _handleAiToolbarThinkingSuccess: (output) => () => {
268
+ _showAiToolbarReviewingDiff: () => () => {
269
+ if (this.storage.state.phase !== "reviewing") {
270
+ return false;
271
+ }
272
+ if (!this.options.doc || !this.storage.snapshot) {
273
+ return false;
274
+ }
275
+ const previousSnapshot = this.storage.snapshot ?? emptySnapshot;
276
+ const currentSnapshot = snapshot(this.options.doc);
277
+ if (equalSnapshots(previousSnapshot, currentSnapshot)) {
278
+ return true;
279
+ }
280
+ getYjsBinding(this.editor)?.renderSnapshot(
281
+ currentSnapshot,
282
+ previousSnapshot
283
+ );
284
+ return true;
285
+ },
286
+ _handleAiToolbarThinkingSuccess: (response) => ({ view, tr }) => {
199
287
  const currentState = this.storage.state;
200
- if (currentState.phase !== "thinking" || !this.options.doc) {
288
+ if (currentState.phase !== "thinking") {
201
289
  return false;
202
290
  }
203
- let selection = this.editor.state.selection;
204
- if (["modification", "insert"].includes(output.type)) {
205
- this.options.doc.gc = false;
206
- this.storage.snapshot = snapshot(this.options.doc);
207
- const { empty, from, to } = this.editor.state.selection;
208
- const contentTarget = empty || output.type === "insert" ? to : {
209
- from,
210
- to
211
- };
212
- const targetTo = output.type === "insert" ? to : from;
213
- this.editor.commands.insertContentAt(contentTarget, output.text);
214
- setTimeout(() => {
215
- if (this.storage.snapshot) {
216
- this.editor.commands._renderAiToolbarDiffInEditor(this.storage.snapshot);
217
- }
218
- }, 100);
219
- selection = {
220
- from,
221
- to: targetTo + output.text.length
291
+ if (!isContextualPromptDiffResponse(response)) {
292
+ this.storage.state = {
293
+ phase: "reviewing",
294
+ initialSelection: currentState.initialSelection,
295
+ customPrompt: "",
296
+ prompt: currentState.prompt,
297
+ response
222
298
  };
299
+ return true;
223
300
  }
301
+ if (!this.options.doc) {
302
+ return false;
303
+ }
304
+ this.options.doc.gc = false;
305
+ this.storage.snapshot = snapshot(this.options.doc);
224
306
  this.storage.state = {
225
307
  phase: "reviewing",
308
+ initialSelection: currentState.initialSelection,
226
309
  customPrompt: "",
227
310
  prompt: currentState.prompt,
228
- output,
229
- selection
311
+ response
230
312
  };
313
+ tr.insertText(
314
+ response.text,
315
+ response.type === "insert" ? this.editor.state.selection.to : this.editor.state.selection.from,
316
+ this.editor.state.selection.to
317
+ );
318
+ view.dispatch(tr);
319
+ tr.setMeta("preventDispatch", true);
320
+ this.editor.commands._showAiToolbarReviewingDiff();
231
321
  return true;
232
322
  },
233
323
  _handleAiToolbarThinkingError: (error) => () => {
@@ -236,10 +326,12 @@ const AiExtension = Extension.create({
236
326
  return false;
237
327
  }
238
328
  this.editor.setEditable(true);
329
+ console.error(error);
239
330
  this.storage.state = {
240
331
  phase: "asking",
332
+ initialSelection: currentState.initialSelection,
241
333
  customPrompt: currentState.prompt === currentState.customPrompt ? currentState.customPrompt : "",
242
- error
334
+ error: error instanceof Error ? error : new Error(String(error), { cause: error })
243
335
  };
244
336
  return true;
245
337
  },
@@ -250,22 +342,6 @@ const AiExtension = Extension.create({
250
342
  }
251
343
  this.storage.state.customPrompt = typeof customPrompt === "function" ? customPrompt(currentState.customPrompt) : customPrompt;
252
344
  return true;
253
- },
254
- _renderAiToolbarDiffInEditor: (previous) => () => {
255
- if (!this.options.doc) {
256
- return false;
257
- }
258
- const previousSnapshot = previous ?? emptySnapshot;
259
- const currentSnapshot = snapshot(this.options.doc);
260
- if (equalSnapshots(previousSnapshot, currentSnapshot)) {
261
- return true;
262
- }
263
- const binding = getYjsBinding(this.editor);
264
- if (binding) {
265
- binding.renderSnapshot(currentSnapshot, previousSnapshot);
266
- return true;
267
- }
268
- return false;
269
345
  }
270
346
  };
271
347
  },
@@ -275,10 +351,10 @@ const AiExtension = Extension.create({
275
351
  key: AI_TOOLBAR_SELECTION_PLUGIN,
276
352
  props: {
277
353
  decorations: ({ doc, selection }) => {
278
- if (this.storage.state.phase === "closed" || this.storage.state.phase === "reviewing" && this.storage.state.output.type !== "other") {
354
+ if (this.storage.state.phase === "closed" || this.storage.state.phase === "reviewing" && isContextualPromptDiffResponse(this.storage.state.response)) {
279
355
  return DecorationSet.create(doc, []);
280
356
  }
281
- const { from, to } = this.storage.state.selection ?? selection;
357
+ const { from, to } = selection;
282
358
  const decorations = [
283
359
  Decoration.inline(from, to, {
284
360
  class: "lb-root lb-selection lb-tiptap-active-selection"
@@ -292,5 +368,5 @@ const AiExtension = Extension.create({
292
368
  }
293
369
  });
294
370
 
295
- export { AiExtension, DEFAULT_STATE };
371
+ export { AiExtension, DEFAULT_STATE, isContextualPromptDiffResponse };
296
372
  //# sourceMappingURL=AiExtension.mjs.map
@@ -1 +1 @@
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
+ {"version":3,"file":"AiExtension.mjs","sources":["../../src/ai/AiExtension.ts"],"sourcesContent":["import {\n autoRetry,\n type ContextualPromptResponse,\n HttpError,\n} 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 type { Transaction } from \"@tiptap/pm/state\";\nimport { Plugin } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport {\n ySyncPluginKey,\n yXmlFragmentToProseMirrorFragment,\n} from \"y-prosemirror\";\nimport type { Doc, Snapshot } from \"yjs\";\nimport {\n createDocFromSnapshot,\n emptySnapshot,\n equalSnapshots,\n snapshot as takeSnapshot,\n} from \"yjs\";\n\nimport {\n AI_TOOLBAR_SELECTION_PLUGIN,\n type AiCommands,\n type AiExtensionOptions,\n type AiExtensionStorage,\n type AiToolbarState,\n type LiveblocksExtensionStorage,\n type ResolveContextualPromptResponse,\n type YSyncPluginState,\n} from \"../types\";\nimport { getContextualPromptContext } from \"../utils\";\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 function isContextualPromptDiffResponse(\n response: ContextualPromptResponse\n): response is Extract<\n ContextualPromptResponse,\n { type: \"replace\" | \"insert\" }\n> {\n return response.type === \"replace\" || response.type === \"insert\";\n}\n\nfunction isResolveContextualPromptResponse(\n response: unknown\n): response is ResolveContextualPromptResponse {\n return (\n typeof response === \"object\" &&\n response !== null &&\n typeof (response as { text: unknown }).text === \"string\" &&\n typeof (response as { type: unknown }).type === \"string\" &&\n [\"insert\", \"replace\", \"other\"].includes((response as { type: string }).type)\n );\n}\n\nfunction getRevertTransaction(\n tr: Transaction,\n editor: Editor,\n storage: AiExtensionStorage,\n doc?: Doc\n): Transaction | null {\n if (storage.snapshot) {\n const binding = getYjsBinding(editor);\n\n if (binding) {\n binding.mapping.clear();\n\n const docFromSnapshot = createDocFromSnapshot(\n binding.doc,\n storage.snapshot\n );\n const type = docFromSnapshot.getXmlFragment(\"default\"); // TODO: field\n const fragmentContent = yXmlFragmentToProseMirrorFragment(\n type,\n editor.state.schema\n );\n\n tr.setMeta(\"addToHistory\", false);\n tr.replace(\n 0,\n 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 if (doc) {\n doc.gc = true;\n }\n\n storage.snapshot = undefined;\n\n return tr;\n }\n }\n return null;\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 resolveContextualPrompt: () => 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 $acceptAiToolbarResponse:\n () =>\n ({ tr, view }: CommandProps) => {\n const currentState = this.storage.state;\n\n // 1. If NOT in \"reviewing\" phase, do nothing\n if (currentState.phase !== \"reviewing\") {\n return false;\n }\n\n // 2. Accept the response\n if (isContextualPromptDiffResponse(currentState.response)) {\n // 2.a. If the response is a diff, apply it definitely\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 this.storage.snapshot = undefined;\n } else {\n // 2.b. If the response is not a diff, insert it below the selection\n const block =\n this.editor.schema.nodes.paragraph ||\n Object.values(this.editor.schema.nodes).find(\n (node) => node.isBlock\n );\n\n if (!block) {\n throw new Error(\"Could not insert a new paragraph.\");\n }\n\n const paragraph = block.create(\n {},\n this.editor.schema.text(currentState.response.text)\n );\n\n tr.insert(this.editor.state.selection.$to.end() + 1, paragraph);\n view.dispatch(tr);\n // Prevent TipTap from dispatching this transaction, because we already did (this is a hack)\n tr.setMeta(\"preventDispatch\", true);\n }\n\n // 3. Unblock the editor\n getLiveblocksYjsProvider(this.editor)?.unpause();\n this.editor.setEditable(true);\n\n // 4. Set to \"closed\" phase\n this.storage.state = { phase: \"closed\" };\n\n return true;\n },\n\n $closeAiToolbar:\n () =>\n ({ tr, view }: CommandProps) => {\n const currentState = this.storage.state;\n\n // 1. If already in \"closed\" phase, do nothing\n if (currentState.phase === \"closed\") {\n return false;\n }\n\n // 2. If in \"thinking\" phase, cancel the current AI request\n if (currentState.phase === \"thinking\") {\n currentState.abortController.abort();\n }\n\n // 3. 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 // Get the revert transaction and dispatch it\n const revertTr = getRevertTransaction(\n tr,\n this.editor,\n this.storage,\n this.options.doc\n );\n if (revertTr) {\n view.dispatch(revertTr);\n // Prevent TipTap from dispatching this transaction, because we already did (this is a hack)\n tr.setMeta(\"preventDispatch\", true);\n }\n }\n\n // 4. Restore the initial selection\n this.editor.commands.setTextSelection(currentState.initialSelection);\n\n // 5. Unblock the editor\n getLiveblocksYjsProvider(this.editor)?.unpause();\n this.editor.setEditable(true);\n\n // 6. 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 // Store the initial selection\n initialSelection: {\n from: this.editor.state.selection.from,\n to: this.editor.state.selection.to,\n },\n // Initialize the custom prompt as empty\n customPrompt: \"\",\n };\n\n return true;\n },\n\n $startAiToolbarThinking:\n (prompt: string, withPreviousResponse?: boolean) =>\n ({ tr, view }: CommandProps) => {\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 // 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. If this is a retry or a refinement, revert the editor and restore the initial selection\n if (currentState.phase === \"reviewing\") {\n // 3.a. Revert the editor\n const revertTr = getRevertTransaction(\n tr,\n this.editor,\n this.storage,\n this.options.doc\n );\n if (revertTr) {\n // Important: in this scenario we do not unpause the provider\n view.dispatch(revertTr);\n // Prevent Tiptap from dispatching this transaction, because we already did (this is a hack)\n tr.setMeta(\"preventDispatch\", true);\n }\n\n // 3.b. Restore the initial selection\n this.editor.commands.setTextSelection(\n currentState.initialSelection\n );\n }\n\n // 4. Set to \"thinking\" phase\n this.storage.state = {\n phase: \"thinking\",\n // Store the initial selection if the toolbar is opened directly in the \"thinking\" phase\n initialSelection: currentState.initialSelection ?? {\n from: this.editor.state.selection.from,\n to: this.editor.state.selection.to,\n },\n // Initialize the custom prompt as empty if the toolbar is opened directly in the \"thinking\" phase\n customPrompt: currentState.customPrompt ?? \"\",\n prompt,\n abortController,\n previousResponse: currentState.response,\n };\n\n // 5. Block the editor\n this.editor.setEditable(false);\n\n // 6. Start the AI request\n autoRetry(\n async () => {\n await provider?.pause();\n\n // 6.a. Resolve the AI prompt\n const response = (await this.options.resolveContextualPrompt({\n prompt,\n context: getContextualPromptContext(this.editor, 3_000),\n signal: abortController.signal,\n previous:\n withPreviousResponse && currentState.phase === \"reviewing\"\n ? {\n prompt: currentState.prompt,\n response: {\n type: currentState.response?.type,\n text: currentState.response?.text,\n },\n }\n : undefined,\n })) as unknown;\n\n // 6.b. Validate the response\n if (isResolveContextualPromptResponse(response)) {\n return response;\n } else {\n throw new Error(\"Failed to resolve AI prompt.\");\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((response) => {\n if (abortController.signal.aborted) {\n return;\n }\n\n // 6.a. If the AI request succeeds, set to \"reviewing\" phase with the response\n (\n this.editor.commands as unknown as AiCommands\n )._handleAiToolbarThinkingSuccess({\n type: response.type,\n text: response.text,\n });\n })\n .catch((error) => {\n if (abortController.signal.aborted) {\n return;\n }\n\n // 6.b. If the AI request fails, set to \"asking\" phase with error\n (\n this.editor.commands as unknown as AiCommands\n )._handleAiToolbarThinkingError(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 initialSelection: currentState.initialSelection,\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 _showAiToolbarReviewingDiff: () => () => {\n // The diff is applied right before moving to the \"reviewing\" phase\n if (this.storage.state.phase !== \"reviewing\") {\n return false;\n }\n\n if (!this.options.doc || !this.storage.snapshot) {\n return false;\n }\n\n const previousSnapshot: Snapshot =\n this.storage.snapshot ?? emptySnapshot;\n const currentSnapshot = takeSnapshot(this.options.doc);\n\n if (equalSnapshots(previousSnapshot, currentSnapshot)) {\n return true;\n }\n\n getYjsBinding(this.editor)?.renderSnapshot(\n currentSnapshot,\n previousSnapshot\n );\n\n return true;\n },\n\n _handleAiToolbarThinkingSuccess:\n (response: ContextualPromptResponse) =>\n ({ view, tr }: CommandProps) => {\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. If this is not a diff, the response will just be in the toolbar, set to \"reviewing\" phase directly with the response\n if (!isContextualPromptDiffResponse(response)) {\n this.storage.state = {\n phase: \"reviewing\",\n initialSelection: currentState.initialSelection,\n customPrompt: \"\",\n prompt: currentState.prompt,\n response,\n };\n\n return true;\n }\n\n if (!this.options.doc) {\n return false;\n }\n\n // 3. Otherwise, the response is a diff, apply it to the editor\n this.options.doc.gc = false;\n this.storage.snapshot = takeSnapshot(this.options.doc);\n\n // 4. Set to \"reviewing\" phase with the new range\n this.storage.state = {\n phase: \"reviewing\",\n initialSelection: currentState.initialSelection,\n customPrompt: \"\",\n prompt: currentState.prompt,\n response,\n };\n\n // 5. Insert the response's text\n tr.insertText(\n response.text,\n response.type === \"insert\"\n ? this.editor.state.selection.to\n : this.editor.state.selection.from,\n this.editor.state.selection.to\n );\n view.dispatch(tr);\n // Prevent Tiptap from dispatching this transaction, because we already did (this is a hack)\n tr.setMeta(\"preventDispatch\", true);\n\n // 6. Show the diff\n (\n this.editor.commands as unknown as AiCommands\n )._showAiToolbarReviewingDiff();\n\n // We moved to the \"reviewing\" phase, so even if `_showAiToolbarReviewingDiff`\n // returns `false` somehow, we still want to return `true`\n return true;\n },\n\n _handleAiToolbarThinkingError: (error: unknown) => () => {\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. Log the error\n console.error(error);\n\n // 4. Set to \"asking\" phase with error\n this.storage.state = {\n phase: \"asking\",\n initialSelection: currentState.initialSelection,\n // If the custom prompt is different than the prompt, reset it\n customPrompt:\n currentState.prompt === currentState.customPrompt\n ? currentState.customPrompt\n : \"\",\n // Pass the error so it can be displayed\n error:\n error instanceof Error\n ? error\n : new Error(String(error), { cause: 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 },\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: AI_TOOLBAR_SELECTION_PLUGIN,\n props: {\n decorations: ({ doc, selection }) => {\n // Don't show the AI toolbar selection if the toolbar is closed or when reviewing diff responses\n if (\n this.storage.state.phase === \"closed\" ||\n (this.storage.state.phase === \"reviewing\" &&\n isContextualPromptDiffResponse(this.storage.state.response))\n ) {\n return DecorationSet.create(doc, []);\n }\n\n const { from, to } = selection;\n const decorations: Decoration[] = [\n Decoration.inline(from, to, {\n class: \"lb-root lb-selection lb-tiptap-active-selection\",\n }),\n ];\n\n return DecorationSet.create(doc, decorations);\n },\n },\n }),\n ];\n },\n});\n"],"names":["takeSnapshot"],"mappings":"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"}