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