@platejs/ai 49.2.15 → 50.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseAIPlugin-DyivsvyQ.d.mts → getEditorPrompt-FgyoD13L.d.mts} +23 -1
- package/dist/{BaseAIPlugin-DyivsvyQ.d.ts → getEditorPrompt-FgyoD13L.d.ts} +23 -1
- package/dist/index.d.mts +6 -32
- package/dist/index.d.ts +6 -32
- package/dist/index.js +48 -898
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -902
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +72 -66
- package/dist/react/index.d.ts +72 -66
- package/dist/react/index.js +3392 -227
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +3322 -157
- package/dist/react/index.mjs.map +1 -1
- package/package.json +6 -4
package/dist/index.mjs
CHANGED
|
@@ -79,314 +79,91 @@ var BaseAIPlugin = createTSlatePlugin({
|
|
|
79
79
|
undo: bindFirst(undoAI, editor)
|
|
80
80
|
}));
|
|
81
81
|
|
|
82
|
-
// src/lib/
|
|
83
|
-
import {
|
|
84
|
-
|
|
85
|
-
return editor.getApi(MarkdownPlugin).markdown.deserializeInline(text, options);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// src/lib/streaming/streamDeserializeMd.ts
|
|
89
|
-
import { MarkdownPlugin as MarkdownPlugin2 } from "@platejs/markdown";
|
|
90
|
-
import { getPluginType as getPluginType4, KEYS as KEYS13, TextApi as TextApi3 } from "platejs";
|
|
91
|
-
|
|
92
|
-
// src/react/ai/AIPlugin.ts
|
|
93
|
-
import { toPlatePlugin } from "platejs/react";
|
|
94
|
-
var AIPlugin = toPlatePlugin(BaseAIPlugin);
|
|
95
|
-
|
|
96
|
-
// src/react/ai-chat/AIChatPlugin.ts
|
|
97
|
-
import { BlockSelectionPlugin as BlockSelectionPlugin5 } from "@platejs/selection/react";
|
|
98
|
-
import {
|
|
99
|
-
bindFirst as bindFirst2,
|
|
100
|
-
ElementApi as ElementApi3,
|
|
101
|
-
getPluginType as getPluginType3,
|
|
102
|
-
KEYS as KEYS11
|
|
103
|
-
} from "platejs";
|
|
104
|
-
import { createTPlatePlugin } from "platejs/react";
|
|
82
|
+
// src/lib/utils/getEditorPrompt.ts
|
|
83
|
+
import { isSelecting } from "@platejs/selection";
|
|
84
|
+
import { KEYS as KEYS4 } from "platejs";
|
|
105
85
|
|
|
106
|
-
// src/
|
|
86
|
+
// src/lib/utils/getMarkdown.ts
|
|
87
|
+
import { serializeMd } from "@platejs/markdown";
|
|
107
88
|
import { KEYS as KEYS3 } from "platejs";
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const { tf } = getEditorPlugin(editor, AIPlugin);
|
|
111
|
-
const api = editor.getApi({ key: KEYS3.ai });
|
|
112
|
-
const lastAINodePath = api.aiChat.node({ at: [], reverse: true })[1];
|
|
113
|
-
withAIBatch(editor, () => {
|
|
114
|
-
tf.ai.removeMarks();
|
|
115
|
-
editor.getTransforms(AIChatPlugin).aiChat.removeAnchor();
|
|
116
|
-
});
|
|
117
|
-
api.aiChat.hide();
|
|
118
|
-
editor.tf.focus();
|
|
119
|
-
const focusPoint = editor.api.end(lastAINodePath);
|
|
120
|
-
editor.tf.setSelection({
|
|
121
|
-
anchor: focusPoint,
|
|
122
|
-
focus: focusPoint
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
// src/react/ai-chat/transforms/insertBelowAIChat.ts
|
|
127
|
-
import { BlockSelectionPlugin as BlockSelectionPlugin2 } from "@platejs/selection/react";
|
|
128
|
-
import cloneDeep2 from "lodash/cloneDeep.js";
|
|
129
|
-
import { KEYS as KEYS5, PathApi, RangeApi } from "platejs";
|
|
130
|
-
|
|
131
|
-
// src/react/ai-chat/transforms/replaceSelectionAIChat.ts
|
|
132
|
-
import {
|
|
133
|
-
BlockSelectionPlugin,
|
|
134
|
-
removeBlockSelectionNodes
|
|
135
|
-
} from "@platejs/selection/react";
|
|
136
|
-
import cloneDeep from "lodash/cloneDeep.js";
|
|
137
|
-
import {
|
|
138
|
-
KEYS as KEYS4,
|
|
139
|
-
NodeApi,
|
|
140
|
-
TextApi as TextApi2
|
|
141
|
-
} from "platejs";
|
|
142
|
-
var createFormattedBlocks = ({
|
|
143
|
-
blocks,
|
|
144
|
-
format,
|
|
145
|
-
sourceBlock
|
|
89
|
+
var getMarkdown = (editor, {
|
|
90
|
+
type
|
|
146
91
|
}) => {
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!firstTextEntry) return null;
|
|
151
|
-
const blockProps = NodeApi.extractProps(sourceNode);
|
|
152
|
-
const textProps = NodeApi.extractProps(firstTextEntry[0]);
|
|
153
|
-
const applyTextFormatting = (node) => {
|
|
154
|
-
if (TextApi2.isText(node)) {
|
|
155
|
-
return { ...textProps, ...node };
|
|
156
|
-
}
|
|
157
|
-
if (node.children) {
|
|
158
|
-
return {
|
|
159
|
-
...node,
|
|
160
|
-
children: node.children.map(applyTextFormatting)
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return node;
|
|
164
|
-
};
|
|
165
|
-
return blocks.map((block, index) => {
|
|
166
|
-
if (format === "single" && index > 0) {
|
|
167
|
-
return block;
|
|
168
|
-
}
|
|
169
|
-
return applyTextFormatting({
|
|
170
|
-
...block,
|
|
171
|
-
...blockProps
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
};
|
|
175
|
-
var replaceSelectionAIChat = (editor, sourceEditor, { format = "single" } = {}) => {
|
|
176
|
-
if (!sourceEditor || sourceEditor.api.isEmpty()) return;
|
|
177
|
-
const isBlockSelecting = editor.getOption(
|
|
178
|
-
BlockSelectionPlugin,
|
|
179
|
-
"isSelectingSome"
|
|
180
|
-
);
|
|
181
|
-
editor.getApi({ key: KEYS4.ai }).aiChat.hide();
|
|
182
|
-
if (!isBlockSelecting) {
|
|
183
|
-
const firstBlock = editor.api.node({
|
|
184
|
-
block: true,
|
|
185
|
-
mode: "lowest"
|
|
186
|
-
});
|
|
187
|
-
if (firstBlock && editor.api.isSelected(firstBlock[1], { contains: true }) && format !== "none") {
|
|
188
|
-
const formattedBlocks2 = createFormattedBlocks({
|
|
189
|
-
blocks: cloneDeep(sourceEditor.children),
|
|
190
|
-
format,
|
|
191
|
-
sourceBlock: firstBlock
|
|
192
|
-
});
|
|
193
|
-
if (!formattedBlocks2) return;
|
|
194
|
-
if (firstBlock[0].type === KEYS4.codeLine && sourceEditor.children[0].type === KEYS4.codeBlock && sourceEditor.children.length === 1) {
|
|
195
|
-
editor.tf.insertFragment(formattedBlocks2[0].children);
|
|
196
|
-
} else {
|
|
197
|
-
editor.tf.insertFragment(formattedBlocks2);
|
|
198
|
-
}
|
|
199
|
-
editor.tf.focus();
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
editor.tf.insertFragment(sourceEditor.children);
|
|
203
|
-
editor.tf.focus();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const blockSelectionApi = editor.getApi(BlockSelectionPlugin).blockSelection;
|
|
207
|
-
const selectedBlocks = blockSelectionApi.getNodes();
|
|
208
|
-
if (selectedBlocks.length === 0) return;
|
|
209
|
-
if (format === "none" || format === "single" && selectedBlocks.length > 1) {
|
|
210
|
-
editor.tf.withoutNormalizing(() => {
|
|
211
|
-
removeBlockSelectionNodes(editor);
|
|
212
|
-
editor.tf.withNewBatch(() => {
|
|
213
|
-
editor.getTransforms(BlockSelectionPlugin).blockSelection.insertBlocksAndSelect(
|
|
214
|
-
cloneDeep(sourceEditor.children),
|
|
215
|
-
{
|
|
216
|
-
at: selectedBlocks[0][1]
|
|
217
|
-
}
|
|
218
|
-
);
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
editor.getApi(BlockSelectionPlugin).blockSelection.focus();
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const [, firstBlockPath] = selectedBlocks[0];
|
|
225
|
-
const formattedBlocks = createFormattedBlocks({
|
|
226
|
-
blocks: cloneDeep(sourceEditor.children),
|
|
227
|
-
format,
|
|
228
|
-
sourceBlock: selectedBlocks[0]
|
|
229
|
-
});
|
|
230
|
-
if (!formattedBlocks) return;
|
|
231
|
-
editor.tf.withoutNormalizing(() => {
|
|
232
|
-
removeBlockSelectionNodes(editor);
|
|
233
|
-
editor.tf.withNewBatch(() => {
|
|
234
|
-
editor.getTransforms(BlockSelectionPlugin).blockSelection.insertBlocksAndSelect(formattedBlocks, {
|
|
235
|
-
at: firstBlockPath
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
editor.getApi(BlockSelectionPlugin).blockSelection.focus();
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
// src/react/ai-chat/transforms/insertBelowAIChat.ts
|
|
243
|
-
var insertBelowAIChat = (editor, sourceEditor, { format = "single" } = {}) => {
|
|
244
|
-
if (!sourceEditor || sourceEditor.api.isEmpty()) return;
|
|
245
|
-
const isBlockSelecting = editor.getOption(
|
|
246
|
-
BlockSelectionPlugin2,
|
|
247
|
-
"isSelectingSome"
|
|
248
|
-
);
|
|
249
|
-
editor.getApi({ key: KEYS5.ai }).aiChat.hide();
|
|
250
|
-
const insertBlocksAndSelect = editor.getTransforms(BlockSelectionPlugin2).blockSelection.insertBlocksAndSelect;
|
|
251
|
-
if (isBlockSelecting) {
|
|
252
|
-
const selectedBlocks = editor.getApi(BlockSelectionPlugin2).blockSelection.getNodes();
|
|
253
|
-
const selectedIds = editor.getOptions(BlockSelectionPlugin2).selectedIds;
|
|
254
|
-
if (!selectedIds || selectedIds.size === 0) return;
|
|
255
|
-
const lastBlock = selectedBlocks.at(-1);
|
|
256
|
-
if (!lastBlock) return;
|
|
257
|
-
const nextPath = PathApi.next(lastBlock[1]);
|
|
258
|
-
if (format === "none") {
|
|
259
|
-
insertBlocksAndSelect(cloneDeep2(sourceEditor.children), {
|
|
260
|
-
at: nextPath
|
|
261
|
-
});
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const formattedBlocks = createFormattedBlocks({
|
|
265
|
-
blocks: cloneDeep2(sourceEditor.children),
|
|
266
|
-
format,
|
|
267
|
-
sourceBlock: lastBlock
|
|
268
|
-
});
|
|
269
|
-
if (!formattedBlocks) return;
|
|
270
|
-
insertBlocksAndSelect(formattedBlocks, {
|
|
271
|
-
at: nextPath
|
|
272
|
-
});
|
|
273
|
-
} else {
|
|
274
|
-
const [, end] = RangeApi.edges(editor.selection);
|
|
275
|
-
const endPath = [end.path[0]];
|
|
276
|
-
const currentBlock = editor.api.node({
|
|
277
|
-
at: endPath,
|
|
278
|
-
block: true,
|
|
279
|
-
mode: "lowest"
|
|
280
|
-
});
|
|
281
|
-
if (!currentBlock) return;
|
|
282
|
-
if (format === "none") {
|
|
283
|
-
insertBlocksAndSelect(cloneDeep2(sourceEditor.children), {
|
|
284
|
-
at: PathApi.next(endPath)
|
|
285
|
-
});
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
const formattedBlocks = createFormattedBlocks({
|
|
289
|
-
blocks: cloneDeep2(sourceEditor.children),
|
|
290
|
-
format,
|
|
291
|
-
sourceBlock: currentBlock
|
|
292
|
-
});
|
|
293
|
-
if (!formattedBlocks) return;
|
|
294
|
-
insertBlocksAndSelect(formattedBlocks, {
|
|
295
|
-
at: PathApi.next(endPath)
|
|
92
|
+
if (type === "editor" || type === "editorWithBlockId") {
|
|
93
|
+
return serializeMd(editor, {
|
|
94
|
+
withBlockId: type === "editorWithBlockId"
|
|
296
95
|
});
|
|
297
96
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
getPluginType as getPluginType2,
|
|
304
|
-
KEYS as KEYS6
|
|
305
|
-
} from "platejs";
|
|
306
|
-
var removeAnchorAIChat = (editor, options) => {
|
|
307
|
-
editor.tf.withoutSaving(() => {
|
|
308
|
-
editor.tf.removeNodes({
|
|
309
|
-
at: [],
|
|
310
|
-
match: (n) => ElementApi.isElement(n) && n.type === getPluginType2(editor, KEYS6.aiChat),
|
|
311
|
-
...options
|
|
97
|
+
if (type === "block" || type === "blockWithBlockId") {
|
|
98
|
+
const blocks = editor.api.blocks({ mode: "highest" }).map(([node]) => node);
|
|
99
|
+
return serializeMd(editor, {
|
|
100
|
+
value: blocks,
|
|
101
|
+
withBlockId: type === "blockWithBlockId"
|
|
312
102
|
});
|
|
313
|
-
});
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
// src/react/ai-chat/utils/getEditorPrompt.ts
|
|
317
|
-
import { isSelecting } from "@platejs/selection";
|
|
318
|
-
import { BlockSelectionPlugin as BlockSelectionPlugin4 } from "@platejs/selection/react";
|
|
319
|
-
|
|
320
|
-
// src/react/ai-chat/utils/getMarkdown.ts
|
|
321
|
-
import { serializeMd } from "@platejs/markdown";
|
|
322
|
-
import { BlockSelectionPlugin as BlockSelectionPlugin3 } from "@platejs/selection/react";
|
|
323
|
-
import { KEYS as KEYS7 } from "platejs";
|
|
324
|
-
var getMarkdown = (editor, type) => {
|
|
325
|
-
if (type === "editor") {
|
|
326
|
-
return serializeMd(editor);
|
|
327
103
|
}
|
|
328
|
-
if (type === "
|
|
329
|
-
const blocks = editor.getOption(BlockSelectionPlugin3, "isSelectingSome") ? editor.getApi(BlockSelectionPlugin3).blockSelection.getNodes() : editor.api.nodes({
|
|
330
|
-
mode: "highest",
|
|
331
|
-
match: (n) => editor.api.isBlock(n)
|
|
332
|
-
});
|
|
333
|
-
const nodes = Array.from(blocks, (entry) => entry[0]);
|
|
334
|
-
return serializeMd(editor, { value: nodes });
|
|
335
|
-
}
|
|
336
|
-
if (type === "selection") {
|
|
104
|
+
if (type === "blockSelection" || type === "blockSelectionWithBlockId") {
|
|
337
105
|
const fragment = editor.api.fragment();
|
|
338
106
|
if (fragment.length === 1) {
|
|
339
107
|
const modifiedFragment = [
|
|
340
108
|
{
|
|
341
109
|
children: fragment[0].children,
|
|
342
|
-
type:
|
|
110
|
+
type: KEYS3.p
|
|
343
111
|
}
|
|
344
112
|
];
|
|
345
|
-
return serializeMd(editor, {
|
|
113
|
+
return serializeMd(editor, {
|
|
114
|
+
value: modifiedFragment,
|
|
115
|
+
withBlockId: type === "blockSelectionWithBlockId"
|
|
116
|
+
});
|
|
346
117
|
}
|
|
347
|
-
return serializeMd(editor, {
|
|
118
|
+
return serializeMd(editor, {
|
|
119
|
+
value: fragment,
|
|
120
|
+
withBlockId: type === "blockSelectionWithBlockId"
|
|
121
|
+
});
|
|
348
122
|
}
|
|
349
123
|
return "";
|
|
350
124
|
};
|
|
351
125
|
|
|
352
|
-
// src/
|
|
126
|
+
// src/lib/utils/getEditorPrompt.ts
|
|
353
127
|
var replacePlaceholders = (editor, text, {
|
|
354
128
|
prompt
|
|
355
|
-
}) => {
|
|
129
|
+
} = {}) => {
|
|
356
130
|
let result = text.replace("{prompt}", prompt || "");
|
|
357
131
|
const placeholders = {
|
|
132
|
+
"{blockSelectionWithBlockId}": "blockSelectionWithBlockId",
|
|
133
|
+
"{blockSelection}": "blockSelection",
|
|
134
|
+
"{blockWithBlockId}": "blockWithBlockId",
|
|
358
135
|
"{block}": "block",
|
|
359
|
-
"{
|
|
360
|
-
"{
|
|
136
|
+
"{editorWithBlockId}": "editorWithBlockId",
|
|
137
|
+
"{editor}": "editor"
|
|
361
138
|
};
|
|
362
139
|
Object.entries(placeholders).forEach(([placeholder, type]) => {
|
|
363
140
|
if (result.includes(placeholder)) {
|
|
364
|
-
result = result.replace(placeholder, getMarkdown(editor, type));
|
|
141
|
+
result = result.replace(placeholder, getMarkdown(editor, { type }));
|
|
365
142
|
}
|
|
366
143
|
});
|
|
367
144
|
return result;
|
|
368
145
|
};
|
|
369
146
|
var createPromptFromConfig = (config, params) => {
|
|
370
|
-
const { isBlockSelecting, isSelecting:
|
|
147
|
+
const { isBlockSelecting, isSelecting: isSelecting2 } = params;
|
|
371
148
|
if (isBlockSelecting && config.blockSelecting) {
|
|
372
149
|
return config.blockSelecting ?? config.default;
|
|
373
|
-
} else if (
|
|
150
|
+
} else if (isSelecting2 && config.selecting) {
|
|
374
151
|
return config.selecting ?? config.default;
|
|
375
152
|
} else {
|
|
376
153
|
return config.default;
|
|
377
154
|
}
|
|
378
155
|
};
|
|
379
156
|
var getEditorPrompt = (editor, {
|
|
380
|
-
prompt = ""
|
|
381
|
-
|
|
382
|
-
} = {}) => {
|
|
157
|
+
prompt = ""
|
|
158
|
+
}) => {
|
|
383
159
|
const params = {
|
|
384
160
|
editor,
|
|
385
|
-
isBlockSelecting: editor.getOption(
|
|
161
|
+
isBlockSelecting: editor.getOption(
|
|
162
|
+
{ key: KEYS4.blockSelection },
|
|
163
|
+
"isSelectingSome"
|
|
164
|
+
),
|
|
386
165
|
isSelecting: isSelecting(editor)
|
|
387
166
|
};
|
|
388
|
-
const template = promptTemplate(params);
|
|
389
|
-
if (!template) return;
|
|
390
167
|
let promptText = "";
|
|
391
168
|
if (typeof prompt === "function") {
|
|
392
169
|
promptText = prompt(params);
|
|
@@ -395,650 +172,16 @@ var getEditorPrompt = (editor, {
|
|
|
395
172
|
} else {
|
|
396
173
|
promptText = prompt;
|
|
397
174
|
}
|
|
398
|
-
return
|
|
399
|
-
prompt: promptText
|
|
400
|
-
});
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
// src/react/ai-chat/utils/resetAIChat.ts
|
|
404
|
-
import { KEYS as KEYS8 } from "platejs";
|
|
405
|
-
import { getEditorPlugin as getEditorPlugin2 } from "platejs/react";
|
|
406
|
-
var resetAIChat = (editor) => {
|
|
407
|
-
const { api, getOptions } = getEditorPlugin2(editor, {
|
|
408
|
-
key: KEYS8.aiChat
|
|
409
|
-
});
|
|
410
|
-
api.aiChat.stop();
|
|
411
|
-
const chat = getOptions().chat;
|
|
412
|
-
if (chat.messages && chat.messages.length > 0) {
|
|
413
|
-
chat.setMessages?.([]);
|
|
414
|
-
}
|
|
415
|
-
editor.getTransforms(AIPlugin).ai.undo();
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
// src/react/ai-chat/utils/submitAIChat.ts
|
|
419
|
-
import { isSelecting as isSelecting2 } from "@platejs/selection";
|
|
420
|
-
import { KEYS as KEYS9 } from "platejs";
|
|
421
|
-
import { getEditorPlugin as getEditorPlugin3 } from "platejs/react";
|
|
422
|
-
var submitAIChat = (editor, {
|
|
423
|
-
mode,
|
|
424
|
-
options,
|
|
425
|
-
prompt,
|
|
426
|
-
system
|
|
427
|
-
} = {}) => {
|
|
428
|
-
const { getOptions, setOption } = getEditorPlugin3(
|
|
429
|
-
editor,
|
|
430
|
-
{
|
|
431
|
-
key: KEYS9.aiChat
|
|
432
|
-
}
|
|
433
|
-
);
|
|
434
|
-
const { chat, promptTemplate, systemTemplate } = getOptions();
|
|
435
|
-
if (!prompt && chat.input?.length === 0) {
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
if (!prompt) {
|
|
439
|
-
prompt = chat.input;
|
|
440
|
-
}
|
|
441
|
-
if (!mode) {
|
|
442
|
-
mode = isSelecting2(editor) ? "chat" : "insert";
|
|
443
|
-
}
|
|
444
|
-
if (mode === "insert") {
|
|
445
|
-
editor.getTransforms(AIPlugin).ai.undo();
|
|
446
|
-
}
|
|
447
|
-
setOption("mode", mode);
|
|
448
|
-
chat.setInput?.("");
|
|
449
|
-
void chat.append?.(
|
|
450
|
-
{
|
|
451
|
-
content: getEditorPrompt(editor, {
|
|
452
|
-
prompt,
|
|
453
|
-
promptTemplate
|
|
454
|
-
}) ?? "",
|
|
455
|
-
role: "user"
|
|
456
|
-
},
|
|
457
|
-
{
|
|
458
|
-
body: {
|
|
459
|
-
system: getEditorPrompt(editor, {
|
|
460
|
-
prompt: system,
|
|
461
|
-
promptTemplate: systemTemplate
|
|
462
|
-
})
|
|
463
|
-
},
|
|
464
|
-
...options
|
|
465
|
-
}
|
|
466
|
-
);
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
// src/react/ai-chat/withAIChat.ts
|
|
470
|
-
import { ElementApi as ElementApi2, KEYS as KEYS10 } from "platejs";
|
|
471
|
-
var withAIChat = ({
|
|
472
|
-
api,
|
|
473
|
-
editor,
|
|
474
|
-
getOptions,
|
|
475
|
-
tf: { insertText, normalizeNode, setSelection },
|
|
476
|
-
type
|
|
477
|
-
}) => {
|
|
478
|
-
const tf = editor.getTransforms(AIPlugin);
|
|
479
|
-
const matchesTrigger = (text) => {
|
|
480
|
-
const { trigger } = getOptions();
|
|
481
|
-
if (trigger instanceof RegExp) {
|
|
482
|
-
return trigger.test(text);
|
|
483
|
-
}
|
|
484
|
-
if (Array.isArray(trigger)) {
|
|
485
|
-
return trigger.includes(text);
|
|
486
|
-
}
|
|
487
|
-
return text === trigger;
|
|
488
|
-
};
|
|
489
|
-
return {
|
|
490
|
-
transforms: {
|
|
491
|
-
insertText(text, options) {
|
|
492
|
-
const { triggerPreviousCharPattern, triggerQuery } = getOptions();
|
|
493
|
-
const fn = () => {
|
|
494
|
-
if (!editor.selection || !matchesTrigger(text) || triggerQuery && !triggerQuery(editor)) {
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
const previousChar = editor.api.string(
|
|
498
|
-
editor.api.range("before", editor.selection)
|
|
499
|
-
);
|
|
500
|
-
const matchesPreviousCharPattern = triggerPreviousCharPattern?.test(previousChar);
|
|
501
|
-
if (!matchesPreviousCharPattern) return;
|
|
502
|
-
const nodeEntry = editor.api.block({ highest: true });
|
|
503
|
-
if (!nodeEntry || !editor.api.isEmpty(nodeEntry[0])) return;
|
|
504
|
-
api.aiChat.show();
|
|
505
|
-
return true;
|
|
506
|
-
};
|
|
507
|
-
if (fn()) return;
|
|
508
|
-
return insertText(text, options);
|
|
509
|
-
},
|
|
510
|
-
normalizeNode(entry) {
|
|
511
|
-
const [node, path] = entry;
|
|
512
|
-
if (node[KEYS10.ai] && !getOptions().open) {
|
|
513
|
-
tf.ai.removeMarks({ at: path });
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
if (ElementApi2.isElement(node) && node.type === type && !getOptions().open) {
|
|
517
|
-
editor.getTransforms(AIChatPlugin).aiChat.removeAnchor({ at: path });
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
return normalizeNode(entry);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
// src/react/ai-chat/AIChatPlugin.ts
|
|
527
|
-
var AIChatPlugin = createTPlatePlugin({
|
|
528
|
-
key: KEYS11.aiChat,
|
|
529
|
-
dependencies: ["ai"],
|
|
530
|
-
node: {
|
|
531
|
-
isElement: true
|
|
532
|
-
},
|
|
533
|
-
options: {
|
|
534
|
-
_blockChunks: "",
|
|
535
|
-
_blockPath: null,
|
|
536
|
-
_mdxName: null,
|
|
537
|
-
aiEditor: null,
|
|
538
|
-
chat: { messages: [] },
|
|
539
|
-
experimental_lastTextId: null,
|
|
540
|
-
mode: "chat",
|
|
541
|
-
open: false,
|
|
542
|
-
streaming: false,
|
|
543
|
-
trigger: " ",
|
|
544
|
-
triggerPreviousCharPattern: /^\s?$/,
|
|
545
|
-
promptTemplate: () => "{prompt}",
|
|
546
|
-
systemTemplate: () => {
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}).overrideEditor(withAIChat).extendApi(({ editor, getOption, getOptions, setOption, type }) => {
|
|
550
|
-
return {
|
|
551
|
-
reset: bindFirst2(resetAIChat, editor),
|
|
552
|
-
submit: bindFirst2(submitAIChat, editor),
|
|
553
|
-
node: (options = {}) => {
|
|
554
|
-
const { anchor = false, streaming = false, ...rest } = options;
|
|
555
|
-
if (anchor) {
|
|
556
|
-
return editor.api.node({
|
|
557
|
-
at: [],
|
|
558
|
-
match: (n) => ElementApi3.isElement(n) && n.type === type,
|
|
559
|
-
...rest
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
if (streaming) {
|
|
563
|
-
if (!getOption("streaming")) return;
|
|
564
|
-
const path = getOption("_blockPath");
|
|
565
|
-
if (!path) return;
|
|
566
|
-
return editor.api.node({
|
|
567
|
-
at: path,
|
|
568
|
-
mode: "lowest",
|
|
569
|
-
reverse: true,
|
|
570
|
-
match: (t) => !!t[getPluginType3(editor, KEYS11.ai)],
|
|
571
|
-
...rest
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
return editor.api.node({
|
|
575
|
-
match: (n) => n[getPluginType3(editor, KEYS11.ai)],
|
|
576
|
-
...rest
|
|
577
|
-
});
|
|
578
|
-
},
|
|
579
|
-
reload: () => {
|
|
580
|
-
const { chat, mode } = getOptions();
|
|
581
|
-
if (mode === "insert") {
|
|
582
|
-
editor.getTransforms(AIPlugin).ai.undo();
|
|
583
|
-
}
|
|
584
|
-
void chat.reload?.({
|
|
585
|
-
body: {
|
|
586
|
-
system: getEditorPrompt(editor, {
|
|
587
|
-
promptTemplate: getOptions().systemTemplate
|
|
588
|
-
})
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
},
|
|
592
|
-
stop: () => {
|
|
593
|
-
setOption("streaming", false);
|
|
594
|
-
getOptions().chat.stop?.();
|
|
595
|
-
}
|
|
596
|
-
};
|
|
597
|
-
}).extendApi(({ api, editor, getOptions, setOption, tf, type }) => ({
|
|
598
|
-
hide: () => {
|
|
599
|
-
api.aiChat.reset();
|
|
600
|
-
setOption("open", false);
|
|
601
|
-
if (editor.getOption(BlockSelectionPlugin5, "isSelectingSome")) {
|
|
602
|
-
} else {
|
|
603
|
-
editor.tf.focus();
|
|
604
|
-
}
|
|
605
|
-
const lastBatch = editor.history.undos.at(-1);
|
|
606
|
-
if (lastBatch?.ai) {
|
|
607
|
-
delete lastBatch.ai;
|
|
608
|
-
}
|
|
609
|
-
tf.aiChat.removeAnchor();
|
|
610
|
-
},
|
|
611
|
-
show: () => {
|
|
612
|
-
api.aiChat.reset();
|
|
613
|
-
getOptions().chat.setMessages?.([]);
|
|
614
|
-
setOption("open", true);
|
|
615
|
-
}
|
|
616
|
-
})).extendTransforms(({ editor }) => ({
|
|
617
|
-
accept: bindFirst2(acceptAIChat, editor),
|
|
618
|
-
insertBelow: bindFirst2(insertBelowAIChat, editor),
|
|
619
|
-
removeAnchor: bindFirst2(removeAnchorAIChat, editor),
|
|
620
|
-
replaceSelection: bindFirst2(replaceSelectionAIChat, editor)
|
|
621
|
-
}));
|
|
622
|
-
|
|
623
|
-
// src/lib/streaming/utils/escapeInput.ts
|
|
624
|
-
var escapeInput = (data) => {
|
|
625
|
-
let res = data;
|
|
626
|
-
if (data.startsWith("$$") && !data.startsWith("$$\n") && !isCompleteMath(data)) {
|
|
627
|
-
res = data.replace("$$", String.raw`\$\$`);
|
|
628
|
-
}
|
|
629
|
-
return res;
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
// src/lib/streaming/utils/getListNode.ts
|
|
633
|
-
var getListNode = (editor, node) => {
|
|
634
|
-
if (node.listStyleType && node.listStart) {
|
|
635
|
-
const previousNode = editor.api.previous({
|
|
636
|
-
at: editor.selection?.focus
|
|
637
|
-
})?.[0];
|
|
638
|
-
if (previousNode?.listStyleType && previousNode?.listStart) {
|
|
639
|
-
return node;
|
|
640
|
-
} else {
|
|
641
|
-
if (node.listStart === 1) return node;
|
|
642
|
-
return {
|
|
643
|
-
...node,
|
|
644
|
-
listRestartPolite: node.listStart
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
return node;
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
// src/lib/streaming/utils/isSameNode.ts
|
|
652
|
-
import { isDefined, KEYS as KEYS12 } from "platejs";
|
|
653
|
-
var LIST_STYLE_TYPE = "listStyleType";
|
|
654
|
-
var isSameNode = (editor, node1, node2) => {
|
|
655
|
-
if (node1.type !== editor.getType(KEYS12.p) || node2.type !== editor.getType(KEYS12.p))
|
|
656
|
-
return node1.type === node2.type;
|
|
657
|
-
if (isDefined(node1[LIST_STYLE_TYPE]) || isDefined(node2[LIST_STYLE_TYPE])) {
|
|
658
|
-
return node1[LIST_STYLE_TYPE] === node2[LIST_STYLE_TYPE];
|
|
659
|
-
}
|
|
660
|
-
return node1.type === node2.type;
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
// src/lib/streaming/utils/nodesWithProps.ts
|
|
664
|
-
import { ElementApi as ElementApi4 } from "platejs";
|
|
665
|
-
var nodesWithProps = (editor, nodes, options) => {
|
|
666
|
-
return nodes.map((node) => {
|
|
667
|
-
if (ElementApi4.isElement(node)) {
|
|
668
|
-
return {
|
|
669
|
-
...getListNode(editor, node),
|
|
670
|
-
...options.elementProps,
|
|
671
|
-
children: nodesWithProps(editor, node.children, options)
|
|
672
|
-
};
|
|
673
|
-
} else {
|
|
674
|
-
return {
|
|
675
|
-
...options.textProps,
|
|
676
|
-
...node,
|
|
677
|
-
text: node.text
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
});
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
// src/lib/streaming/utils/utils.ts
|
|
684
|
-
var getChunkTrimmed = (chunk, {
|
|
685
|
-
direction = "right"
|
|
686
|
-
} = {}) => {
|
|
687
|
-
const str = direction === "right" ? chunk.trimEnd() : chunk.trimStart();
|
|
688
|
-
if (direction === "right") {
|
|
689
|
-
return chunk.slice(str.length);
|
|
690
|
-
} else {
|
|
691
|
-
return chunk.slice(0, chunk.length - str.length);
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
function isCompleteCodeBlock(str) {
|
|
695
|
-
const trimmed = str.trim();
|
|
696
|
-
const startsWithCodeBlock = trimmed.startsWith("```");
|
|
697
|
-
const endsWithCodeBlock = trimmed.endsWith("```");
|
|
698
|
-
return startsWithCodeBlock && endsWithCodeBlock;
|
|
699
|
-
}
|
|
700
|
-
function isCompleteMath(str) {
|
|
701
|
-
const trimmed = str.trim();
|
|
702
|
-
const startsWithMath = trimmed.startsWith("$$");
|
|
703
|
-
const endsWithMath = trimmed.endsWith("$$");
|
|
704
|
-
return startsWithMath && endsWithMath;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// src/lib/streaming/streamDeserializeMd.ts
|
|
708
|
-
var statMdxTagRegex = /<([A-Za-z][A-Za-z0-9._:-]*)(?:\s[^>]*)?>/;
|
|
709
|
-
var streamDeserializeMd = (editor, data, options) => {
|
|
710
|
-
const input = escapeInput(data);
|
|
711
|
-
const value = withoutDeserializeInMdx(editor, input);
|
|
712
|
-
if (Array.isArray(value)) return value;
|
|
713
|
-
let blocks = [];
|
|
714
|
-
blocks = editor.getApi(MarkdownPlugin2).markdown.deserialize(input, {
|
|
715
|
-
...options,
|
|
716
|
-
preserveEmptyParagraphs: false
|
|
717
|
-
});
|
|
718
|
-
const trimmedData = getChunkTrimmed(data);
|
|
719
|
-
const lastBlock = blocks.at(-1);
|
|
720
|
-
const addNewLine = trimmedData === "\n\n";
|
|
721
|
-
const unshiftNewLine = getChunkTrimmed(data, { direction: "left" }) === "\n\n";
|
|
722
|
-
const isCodeBlockOrTable = lastBlock?.type === "code_block" || lastBlock?.type === "table";
|
|
723
|
-
let result = blocks;
|
|
724
|
-
if (lastBlock && !isCodeBlockOrTable && trimmedData.length > 0 && !addNewLine) {
|
|
725
|
-
const textNode = [
|
|
726
|
-
{
|
|
727
|
-
text: trimmedData
|
|
728
|
-
}
|
|
729
|
-
];
|
|
730
|
-
const lastChild = lastBlock.children.at(-1);
|
|
731
|
-
if (lastChild && TextApi3.isText(lastChild) && Object.keys(lastChild).length === 1) {
|
|
732
|
-
lastBlock.children.pop();
|
|
733
|
-
const textNode2 = [
|
|
734
|
-
{
|
|
735
|
-
text: lastChild.text + trimmedData
|
|
736
|
-
}
|
|
737
|
-
];
|
|
738
|
-
lastBlock.children.push(...textNode2);
|
|
739
|
-
} else {
|
|
740
|
-
lastBlock.children.push(...textNode);
|
|
741
|
-
}
|
|
742
|
-
result = [...blocks.slice(0, -1), lastBlock];
|
|
743
|
-
}
|
|
744
|
-
if (addNewLine && !isCodeBlockOrTable) {
|
|
745
|
-
result.push({
|
|
746
|
-
children: [{ text: "" }],
|
|
747
|
-
type: KEYS13.p
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
if (unshiftNewLine && !isCodeBlockOrTable) {
|
|
751
|
-
result.unshift({
|
|
752
|
-
children: [{ text: "" }],
|
|
753
|
-
type: KEYS13.p
|
|
754
|
-
});
|
|
755
|
-
}
|
|
756
|
-
return result;
|
|
757
|
-
};
|
|
758
|
-
var withoutDeserializeInMdx = (editor, input) => {
|
|
759
|
-
const mdxName = editor.getOption(AIChatPlugin, "_mdxName");
|
|
760
|
-
if (mdxName) {
|
|
761
|
-
const isMdxEnd = input.includes(`</${mdxName}>`);
|
|
762
|
-
if (isMdxEnd) {
|
|
763
|
-
editor.setOption(AIChatPlugin, "_mdxName", null);
|
|
764
|
-
return false;
|
|
765
|
-
} else {
|
|
766
|
-
return [
|
|
767
|
-
{
|
|
768
|
-
children: [
|
|
769
|
-
{
|
|
770
|
-
text: input
|
|
771
|
-
}
|
|
772
|
-
],
|
|
773
|
-
type: getPluginType4(editor, KEYS13.p)
|
|
774
|
-
}
|
|
775
|
-
];
|
|
776
|
-
}
|
|
777
|
-
} else {
|
|
778
|
-
const newMdxName = statMdxTagRegex.exec(input)?.[1];
|
|
779
|
-
if (input.startsWith(`<${newMdxName}`)) {
|
|
780
|
-
editor.setOption(AIChatPlugin, "_mdxName", newMdxName ?? null);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
// src/lib/streaming/streamInsertChunk.ts
|
|
786
|
-
import {
|
|
787
|
-
getPluginType as getPluginType5,
|
|
788
|
-
KEYS as KEYS15,
|
|
789
|
-
NodeApi as NodeApi2,
|
|
790
|
-
PathApi as PathApi2
|
|
791
|
-
} from "platejs";
|
|
792
|
-
|
|
793
|
-
// src/lib/streaming/streamSerializeMd.ts
|
|
794
|
-
import { MarkdownPlugin as MarkdownPlugin3 } from "@platejs/markdown";
|
|
795
|
-
import {
|
|
796
|
-
ElementApi as ElementApi5,
|
|
797
|
-
getPluginKey,
|
|
798
|
-
KEYS as KEYS14,
|
|
799
|
-
TextApi as TextApi4
|
|
800
|
-
} from "platejs";
|
|
801
|
-
var trimEndHeading = (editor, value) => {
|
|
802
|
-
const headingKeys = /* @__PURE__ */ new Set([
|
|
803
|
-
KEYS14.h1,
|
|
804
|
-
KEYS14.h2,
|
|
805
|
-
KEYS14.h3,
|
|
806
|
-
KEYS14.h4,
|
|
807
|
-
KEYS14.h5,
|
|
808
|
-
KEYS14.h6
|
|
809
|
-
]);
|
|
810
|
-
const lastBlock = value.at(-1);
|
|
811
|
-
if (lastBlock && headingKeys.has(
|
|
812
|
-
getPluginKey(editor, lastBlock.type) ?? lastBlock.type
|
|
813
|
-
) && ElementApi5.isElement(lastBlock)) {
|
|
814
|
-
const lastTextNode = lastBlock.children.at(-1);
|
|
815
|
-
if (TextApi4.isText(lastTextNode)) {
|
|
816
|
-
const trimmedText = getChunkTrimmed(lastTextNode?.text);
|
|
817
|
-
const newChildren = [
|
|
818
|
-
...lastBlock.children.slice(0, -1),
|
|
819
|
-
{ text: lastTextNode.text.trimEnd() }
|
|
820
|
-
];
|
|
821
|
-
const newLastBlock = {
|
|
822
|
-
...lastBlock,
|
|
823
|
-
children: newChildren
|
|
824
|
-
};
|
|
825
|
-
return {
|
|
826
|
-
trimmedText,
|
|
827
|
-
value: [...value.slice(0, -1), newLastBlock]
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return { trimmedText: "", value };
|
|
832
|
-
};
|
|
833
|
-
var streamSerializeMd = (editor, options, chunk) => {
|
|
834
|
-
const { value: optionsValue, ...restOptions } = options;
|
|
835
|
-
const { value } = trimEndHeading(editor, optionsValue ?? editor.children);
|
|
836
|
-
let result = "";
|
|
837
|
-
result = editor.getApi(MarkdownPlugin3).markdown.serialize({
|
|
838
|
-
preserveEmptyParagraphs: false,
|
|
839
|
-
value,
|
|
840
|
-
...restOptions
|
|
841
|
-
});
|
|
842
|
-
const trimmedChunk = getChunkTrimmed(chunk);
|
|
843
|
-
if (isCompleteCodeBlock(result) && !chunk.endsWith("```")) {
|
|
844
|
-
result = result.trimEnd().slice(0, -3) + trimmedChunk;
|
|
845
|
-
}
|
|
846
|
-
if (isCompleteMath(result) && !chunk.endsWith("$$")) {
|
|
847
|
-
result = result.trimEnd().slice(0, -3) + trimmedChunk;
|
|
848
|
-
}
|
|
849
|
-
result = result.replace(/ /g, " ");
|
|
850
|
-
result = result.replace(/​/g, " ");
|
|
851
|
-
result = result.replace(/\u200B/g, "");
|
|
852
|
-
if (trimmedChunk !== "\n\n") {
|
|
853
|
-
result = result.trimEnd() + trimmedChunk;
|
|
854
|
-
}
|
|
855
|
-
if (chunk.endsWith("\n\n")) {
|
|
856
|
-
if (result === "\n") {
|
|
857
|
-
result = "";
|
|
858
|
-
} else if (result.endsWith("\n\n")) {
|
|
859
|
-
result = result.slice(0, -1);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
result = result.replace(/\\([\\`*_{}\\[\]()#+\-\\.!~<>|$])/g, "$1");
|
|
863
|
-
return result;
|
|
864
|
-
};
|
|
865
|
-
|
|
866
|
-
// src/lib/streaming/streamInsertChunk.ts
|
|
867
|
-
var getNextPath = (path, length) => {
|
|
868
|
-
let result = path;
|
|
869
|
-
for (let i = 0; i < length; i++) {
|
|
870
|
-
result = PathApi2.next(result);
|
|
871
|
-
}
|
|
872
|
-
return result;
|
|
873
|
-
};
|
|
874
|
-
function streamInsertChunk(editor, chunk, options = {}) {
|
|
875
|
-
const { _blockChunks, _blockPath } = editor.getOptions(AIChatPlugin);
|
|
876
|
-
if (_blockPath === null) {
|
|
877
|
-
const blocks = streamDeserializeMd(editor, chunk);
|
|
878
|
-
const path = getCurrentBlockPath(editor);
|
|
879
|
-
const startBlock = editor.api.node(path)[0];
|
|
880
|
-
const startInEmptyParagraph = NodeApi2.string(startBlock).length === 0 && startBlock.type === getPluginType5(editor, KEYS15.p);
|
|
881
|
-
if (startInEmptyParagraph) {
|
|
882
|
-
editor.tf.removeNodes({ at: path });
|
|
883
|
-
}
|
|
884
|
-
if (blocks.length > 0) {
|
|
885
|
-
editor.tf.insertNodes(nodesWithProps(editor, [blocks[0]], options), {
|
|
886
|
-
at: path,
|
|
887
|
-
nextBlock: !startInEmptyParagraph,
|
|
888
|
-
select: true
|
|
889
|
-
});
|
|
890
|
-
editor.setOption(AIChatPlugin, "_blockPath", getCurrentBlockPath(editor));
|
|
891
|
-
editor.setOption(AIChatPlugin, "_blockChunks", chunk);
|
|
892
|
-
if (blocks.length > 1) {
|
|
893
|
-
const nextBlocks = blocks.slice(1);
|
|
894
|
-
const nextPath = getCurrentBlockPath(editor);
|
|
895
|
-
editor.tf.insertNodes(nodesWithProps(editor, nextBlocks, options), {
|
|
896
|
-
at: nextPath,
|
|
897
|
-
nextBlock: true,
|
|
898
|
-
select: true
|
|
899
|
-
});
|
|
900
|
-
const lastBlock = editor.api.node(
|
|
901
|
-
getNextPath(nextPath, nextBlocks.length)
|
|
902
|
-
);
|
|
903
|
-
editor.setOption(AIChatPlugin, "_blockPath", lastBlock[1]);
|
|
904
|
-
const lastBlockChunks = streamSerializeMd(
|
|
905
|
-
editor,
|
|
906
|
-
{
|
|
907
|
-
value: [lastBlock[0]]
|
|
908
|
-
},
|
|
909
|
-
chunk
|
|
910
|
-
);
|
|
911
|
-
editor.setOption(AIChatPlugin, "_blockChunks", lastBlockChunks);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
} else {
|
|
915
|
-
const tempBlockChunks = _blockChunks + chunk;
|
|
916
|
-
const tempBlocks = streamDeserializeMd(editor, tempBlockChunks);
|
|
917
|
-
if (tempBlocks.length === 0) {
|
|
918
|
-
return console.warn(
|
|
919
|
-
`unsupport md nodes: ${JSON.stringify(tempBlockChunks)}`
|
|
920
|
-
);
|
|
921
|
-
}
|
|
922
|
-
if (tempBlocks.length === 1) {
|
|
923
|
-
const currentBlock = editor.api.node(_blockPath)[0];
|
|
924
|
-
if (isSameNode(editor, currentBlock, tempBlocks[0])) {
|
|
925
|
-
const chunkNodes = streamDeserializeInlineMd(editor, chunk);
|
|
926
|
-
editor.tf.insertNodes(nodesWithProps(editor, chunkNodes, options), {
|
|
927
|
-
at: editor.api.end(_blockPath),
|
|
928
|
-
select: true
|
|
929
|
-
});
|
|
930
|
-
const updatedBlock = editor.api.node(_blockPath);
|
|
931
|
-
const serializedBlock = streamSerializeMd(
|
|
932
|
-
editor,
|
|
933
|
-
{
|
|
934
|
-
value: [updatedBlock[0]]
|
|
935
|
-
},
|
|
936
|
-
tempBlockChunks
|
|
937
|
-
);
|
|
938
|
-
const blockText = NodeApi2.string(tempBlocks[0]);
|
|
939
|
-
if (serializedBlock === tempBlockChunks && blockText === serializedBlock) {
|
|
940
|
-
editor.setOption(AIChatPlugin, "_blockChunks", tempBlockChunks);
|
|
941
|
-
} else {
|
|
942
|
-
editor.tf.replaceNodes(
|
|
943
|
-
nodesWithProps(editor, [tempBlocks[0]], options),
|
|
944
|
-
{
|
|
945
|
-
at: _blockPath,
|
|
946
|
-
select: true
|
|
947
|
-
}
|
|
948
|
-
);
|
|
949
|
-
const serializedBlock2 = streamSerializeMd(
|
|
950
|
-
editor,
|
|
951
|
-
{
|
|
952
|
-
value: [tempBlocks[0]]
|
|
953
|
-
},
|
|
954
|
-
tempBlockChunks
|
|
955
|
-
);
|
|
956
|
-
editor.setOption(
|
|
957
|
-
AIChatPlugin,
|
|
958
|
-
"_blockChunks",
|
|
959
|
-
// one block includes multiple children
|
|
960
|
-
tempBlocks[0].type === getPluginType5(editor, KEYS15.codeBlock) || tempBlocks[0].type === getPluginType5(editor, KEYS15.table) || tempBlocks[0].type === getPluginType5(editor, KEYS15.equation) ? tempBlockChunks : serializedBlock2
|
|
961
|
-
);
|
|
962
|
-
}
|
|
963
|
-
} else {
|
|
964
|
-
const serializedBlock = streamSerializeMd(
|
|
965
|
-
editor,
|
|
966
|
-
{
|
|
967
|
-
value: [tempBlocks[0]]
|
|
968
|
-
},
|
|
969
|
-
tempBlockChunks
|
|
970
|
-
);
|
|
971
|
-
editor.tf.replaceNodes(
|
|
972
|
-
nodesWithProps(editor, [tempBlocks[0]], options),
|
|
973
|
-
{
|
|
974
|
-
at: _blockPath,
|
|
975
|
-
select: true
|
|
976
|
-
}
|
|
977
|
-
);
|
|
978
|
-
editor.setOption(AIChatPlugin, "_blockChunks", serializedBlock);
|
|
979
|
-
}
|
|
980
|
-
} else {
|
|
981
|
-
editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], options), {
|
|
982
|
-
at: _blockPath,
|
|
983
|
-
select: true
|
|
984
|
-
});
|
|
985
|
-
if (tempBlocks.length > 1) {
|
|
986
|
-
const newEndBlockPath = getNextPath(_blockPath, tempBlocks.length - 1);
|
|
987
|
-
editor.tf.insertNodes(
|
|
988
|
-
nodesWithProps(editor, tempBlocks.slice(1), options),
|
|
989
|
-
{
|
|
990
|
-
at: PathApi2.next(_blockPath),
|
|
991
|
-
select: true
|
|
992
|
-
}
|
|
993
|
-
);
|
|
994
|
-
editor.setOption(AIChatPlugin, "_blockPath", newEndBlockPath);
|
|
995
|
-
const endBlock = editor.api.node(newEndBlockPath)[0];
|
|
996
|
-
const serializedBlock = streamSerializeMd(
|
|
997
|
-
editor,
|
|
998
|
-
{
|
|
999
|
-
value: [endBlock]
|
|
1000
|
-
},
|
|
1001
|
-
tempBlockChunks
|
|
1002
|
-
);
|
|
1003
|
-
editor.setOption(AIChatPlugin, "_blockChunks", serializedBlock);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
var getCurrentBlockPath = (editor) => {
|
|
1009
|
-
const getAnchorPreviousPath = (editor2) => {
|
|
1010
|
-
const anchorNode = editor2.getApi(AIChatPlugin).aiChat.node({ anchor: true });
|
|
1011
|
-
if (anchorNode) {
|
|
1012
|
-
return PathApi2.previous(anchorNode[1]);
|
|
1013
|
-
}
|
|
1014
|
-
};
|
|
1015
|
-
const getFocusPath = (editor2) => {
|
|
1016
|
-
return editor2.selection?.focus.path.slice(0, 1);
|
|
1017
|
-
};
|
|
1018
|
-
const path = getAnchorPreviousPath(editor) ?? getFocusPath(editor) ?? [0];
|
|
1019
|
-
const entry = editor.api.node(path);
|
|
1020
|
-
if (entry && (entry[0].type === getPluginType5(editor, KEYS15.columnGroup) || entry[0].type === getPluginType5(editor, KEYS15.table))) {
|
|
1021
|
-
return editor.api.above()?.[1] ?? path;
|
|
1022
|
-
}
|
|
1023
|
-
return path;
|
|
175
|
+
return promptText;
|
|
1024
176
|
};
|
|
1025
177
|
export {
|
|
1026
178
|
BaseAIPlugin,
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
getCurrentBlockPath,
|
|
1030
|
-
getListNode,
|
|
179
|
+
getEditorPrompt,
|
|
180
|
+
getMarkdown,
|
|
1031
181
|
insertAINodes,
|
|
1032
|
-
isCompleteCodeBlock,
|
|
1033
|
-
isCompleteMath,
|
|
1034
|
-
isSameNode,
|
|
1035
|
-
nodesWithProps,
|
|
1036
182
|
removeAIMarks,
|
|
1037
183
|
removeAINodes,
|
|
1038
|
-
|
|
1039
|
-
streamDeserializeMd,
|
|
1040
|
-
streamInsertChunk,
|
|
1041
|
-
streamSerializeMd,
|
|
184
|
+
replacePlaceholders,
|
|
1042
185
|
undoAI,
|
|
1043
186
|
withAIBatch
|
|
1044
187
|
};
|