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