@platejs/ai 52.3.8 → 52.3.12

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.
@@ -0,0 +1,235 @@
1
+ import { ElementApi, KEYS, TextApi, bindFirst, createTSlatePlugin, getPluginType } from "platejs";
2
+ import cloneDeep from "lodash/cloneDeep.js";
3
+ import { getTransientSuggestionKey } from "@platejs/suggestion";
4
+ import { isSelecting } from "@platejs/selection";
5
+
6
+ //#region src/lib/transforms/aiStreamSnapshot.ts
7
+ const AI_PREVIEW_KEY = "aiPreview";
8
+ const AI_STREAM_SNAPSHOT = /* @__PURE__ */ new WeakMap();
9
+ const clearAIPreview = (editor) => {
10
+ AI_STREAM_SNAPSHOT.delete(editor);
11
+ };
12
+ const getAIPreview = (editor) => AI_STREAM_SNAPSHOT.get(editor);
13
+ const getAIPreviewRange = (editor) => {
14
+ let closed = false;
15
+ let end = -1;
16
+ let invalid = false;
17
+ let start = -1;
18
+ editor.children.forEach((node, index) => {
19
+ if (!node?.[AI_PREVIEW_KEY]) {
20
+ if (start !== -1) closed = true;
21
+ return;
22
+ }
23
+ if (closed) {
24
+ invalid = true;
25
+ return;
26
+ }
27
+ if (start === -1) start = index;
28
+ end = index;
29
+ });
30
+ if (invalid) return { kind: "invalid" };
31
+ if (start === -1 && end === -1) return { kind: "none" };
32
+ return {
33
+ end,
34
+ kind: "range",
35
+ start
36
+ };
37
+ };
38
+ const removeAIPreviewAnchor = (editor) => {
39
+ const aiChatType = getPluginType(editor, KEYS.aiChat);
40
+ editor.tf.removeNodes({
41
+ at: [],
42
+ match: (node) => ElementApi.isElement(node) && node.type === aiChatType
43
+ });
44
+ };
45
+ const restoreAIPreviewSelection = (editor, selection) => {
46
+ if (selection) {
47
+ editor.tf.select(cloneDeep(selection));
48
+ return;
49
+ }
50
+ editor.tf.deselect();
51
+ };
52
+ const removePreviewRange = (editor, range) => {
53
+ for (let index = range.end; index >= range.start; index--) editor.tf.removeNodes({ at: [index] });
54
+ };
55
+ const replacePreviewRange = (editor, range, blocks) => {
56
+ removePreviewRange(editor, range);
57
+ if (blocks.length === 0) return;
58
+ editor.tf.insertNodes(cloneDeep(blocks), { at: [range.start] });
59
+ };
60
+ const cloneAcceptedPreviewBlocks = (editor, range) => {
61
+ const aiType = getPluginType(editor, KEYS.ai);
62
+ const blocks = cloneDeep(editor.children.slice(range.start, range.end + 1));
63
+ const stripNode = (node) => {
64
+ if (ElementApi.isElement(node)) {
65
+ const { [AI_PREVIEW_KEY]: _preview, children, ...rest$1 } = node;
66
+ return {
67
+ ...rest$1,
68
+ children: children.map(stripNode)
69
+ };
70
+ }
71
+ const { [aiType]: _ai, ...rest } = node;
72
+ return rest;
73
+ };
74
+ return blocks.map(stripNode);
75
+ };
76
+ const beginAIPreview = (editor, { originalBlocks = [] } = {}) => {
77
+ if (getAIPreview(editor)) return false;
78
+ AI_STREAM_SNAPSHOT.set(editor, {
79
+ originalBlocks: cloneDeep(originalBlocks),
80
+ selectionBefore: cloneDeep(editor.selection)
81
+ });
82
+ return true;
83
+ };
84
+ const hasAIPreview = (editor) => !!getAIPreview(editor);
85
+ const cancelAIPreview = (editor) => {
86
+ const preview = getAIPreview(editor);
87
+ if (!preview) return false;
88
+ const range = getAIPreviewRange(editor);
89
+ if (range.kind === "invalid") return false;
90
+ editor.tf.withoutSaving(() => {
91
+ if (range.kind === "range") replacePreviewRange(editor, range, preview.originalBlocks);
92
+ removeAIPreviewAnchor(editor);
93
+ restoreAIPreviewSelection(editor, preview.selectionBefore);
94
+ });
95
+ clearAIPreview(editor);
96
+ return true;
97
+ };
98
+ const discardAIPreview = (editor) => {
99
+ if (!getAIPreview(editor)) return false;
100
+ clearAIPreview(editor);
101
+ return true;
102
+ };
103
+ const acceptAIPreview = (editor, _value) => {
104
+ const preview = getAIPreview(editor);
105
+ if (!preview) return false;
106
+ const range = getAIPreviewRange(editor);
107
+ if (range.kind === "invalid") return false;
108
+ if (range.kind === "range") {
109
+ const acceptedBlocks = cloneAcceptedPreviewBlocks(editor, range);
110
+ editor.tf.withoutSaving(() => {
111
+ replacePreviewRange(editor, range, preview.originalBlocks);
112
+ removeAIPreviewAnchor(editor);
113
+ restoreAIPreviewSelection(editor, preview.selectionBefore);
114
+ });
115
+ editor.tf.withNewBatch(() => {
116
+ if (preview.originalBlocks.length > 0) for (let index = preview.originalBlocks.length - 1; index >= 0; index--) editor.tf.removeNodes({ at: [range.start + index] });
117
+ if (acceptedBlocks.length > 0) editor.tf.insertNodes(acceptedBlocks, { at: [range.start] });
118
+ });
119
+ const lastBatch = editor.history?.undos.at(-1);
120
+ if (lastBatch) lastBatch.selectionBefore = cloneDeep(preview.selectionBefore);
121
+ } else editor.tf.withoutSaving(() => {
122
+ removeAIPreviewAnchor(editor);
123
+ });
124
+ clearAIPreview(editor);
125
+ return true;
126
+ };
127
+
128
+ //#endregion
129
+ //#region src/lib/transforms/insertAINodes.ts
130
+ const insertAINodes = (editor, nodes, { target } = {}) => {
131
+ if (!target && !editor.selection?.focus.path) return;
132
+ const aiNodes = nodes.map((node) => ({
133
+ ...node,
134
+ ai: true
135
+ }));
136
+ editor.tf.withoutNormalizing(() => {
137
+ editor.tf.insertNodes(aiNodes, {
138
+ at: editor.api.end(target || editor.selection.focus.path),
139
+ select: true
140
+ });
141
+ editor.tf.collapse({ edge: "end" });
142
+ });
143
+ };
144
+
145
+ //#endregion
146
+ //#region src/lib/transforms/removeAIMarks.ts
147
+ const removeAIMarks = (editor, { at = [] } = {}) => {
148
+ const nodeType = getPluginType(editor, KEYS.ai);
149
+ editor.tf.unsetNodes(nodeType, {
150
+ at,
151
+ match: (n) => n[nodeType]
152
+ });
153
+ };
154
+
155
+ //#endregion
156
+ //#region src/lib/transforms/removeAINodes.ts
157
+ const removeAINodes = (editor, { at = [] } = {}) => {
158
+ editor.tf.removeNodes({
159
+ at,
160
+ match: (n) => TextApi.isText(n) && !!n.ai
161
+ });
162
+ };
163
+
164
+ //#endregion
165
+ //#region src/lib/transforms/undoAI.ts
166
+ const undoAI = (editor) => {
167
+ if (hasAIPreview(editor) && cancelAIPreview(editor)) return;
168
+ const hasAINodeOrAISuggestion = editor.api.some({
169
+ at: [],
170
+ match: (n) => !!n.ai
171
+ }) || editor.api.some({
172
+ at: [],
173
+ match: (n) => !!n[getTransientSuggestionKey()]
174
+ });
175
+ if (editor.history.undos.at(-1)?.ai && hasAINodeOrAISuggestion) {
176
+ editor.undo();
177
+ editor.history.redos.pop();
178
+ return;
179
+ }
180
+ if (hasAINodeOrAISuggestion) cancelAIPreview(editor);
181
+ };
182
+
183
+ //#endregion
184
+ //#region src/lib/transforms/withAIBatch.ts
185
+ const withAIBatch = (editor, fn, { split } = {}) => {
186
+ if (split) editor.tf.withNewBatch(fn);
187
+ else editor.tf.withMerging(fn);
188
+ const lastBatch = editor.history.undos?.at(-1);
189
+ if (lastBatch) lastBatch.ai = true;
190
+ };
191
+
192
+ //#endregion
193
+ //#region src/lib/BaseAIPlugin.ts
194
+ const getAITransforms = (editor) => ({
195
+ acceptPreview: bindFirst(acceptAIPreview, editor),
196
+ beginPreview: bindFirst(beginAIPreview, editor),
197
+ cancelPreview: bindFirst(cancelAIPreview, editor),
198
+ discardPreview: bindFirst(discardAIPreview, editor),
199
+ hasPreview: bindFirst(hasAIPreview, editor),
200
+ insertNodes: bindFirst(insertAINodes, editor),
201
+ removeMarks: bindFirst(removeAIMarks, editor),
202
+ removeNodes: bindFirst(removeAINodes, editor),
203
+ undo: bindFirst(undoAI, editor)
204
+ });
205
+ const BaseAIPlugin = createTSlatePlugin({
206
+ key: KEYS.ai,
207
+ node: {
208
+ isDecoration: false,
209
+ isLeaf: true
210
+ }
211
+ }).extendTransforms(({ editor }) => getAITransforms(editor)).extendEditorTransforms(({ editor }) => ({ ai: getAITransforms(editor) }));
212
+
213
+ //#endregion
214
+ //#region src/lib/utils/getEditorPrompt.ts
215
+ const createPromptFromConfig = (config, params) => {
216
+ const { isBlockSelecting, isSelecting: isSelecting$1 } = params;
217
+ if (isBlockSelecting && config.blockSelecting) return config.blockSelecting ?? config.default;
218
+ if (isSelecting$1 && config.selecting) return config.selecting ?? config.default;
219
+ return config.default;
220
+ };
221
+ const getEditorPrompt = (editor, { prompt = "" }) => {
222
+ const params = {
223
+ editor,
224
+ isBlockSelecting: editor.getOption({ key: KEYS.blockSelection }, "isSelectingSome"),
225
+ isSelecting: isSelecting(editor)
226
+ };
227
+ let promptText = "";
228
+ if (typeof prompt === "function") promptText = prompt(params);
229
+ else if (typeof prompt === "object") promptText = createPromptFromConfig(prompt, params);
230
+ else promptText = prompt;
231
+ return promptText;
232
+ };
233
+
234
+ //#endregion
235
+ export { removeAINodes as a, AI_PREVIEW_KEY as c, cancelAIPreview as d, discardAIPreview as f, undoAI as i, acceptAIPreview as l, BaseAIPlugin as n, removeAIMarks as o, hasAIPreview as p, withAIBatch as r, insertAINodes as s, getEditorPrompt as t, beginAIPreview as u };
@@ -0,0 +1,137 @@
1
+ import * as platejs0 from "platejs";
2
+ import { Descendant, History, OmitFirst, Path, PluginConfig, SlateEditor, TLocation, Value } from "platejs";
3
+
4
+ //#region src/lib/transforms/aiStreamSnapshot.d.ts
5
+ type BeginAIPreviewOptions = {
6
+ originalBlocks?: Value;
7
+ };
8
+ declare const AI_PREVIEW_KEY = "aiPreview";
9
+ declare const beginAIPreview: (editor: SlateEditor, {
10
+ originalBlocks
11
+ }?: BeginAIPreviewOptions) => boolean;
12
+ declare const hasAIPreview: (editor: SlateEditor) => boolean;
13
+ declare const cancelAIPreview: (editor: SlateEditor) => boolean;
14
+ declare const discardAIPreview: (editor: SlateEditor) => boolean;
15
+ declare const acceptAIPreview: (editor: SlateEditor, _value?: Value) => boolean;
16
+ //#endregion
17
+ //#region src/lib/transforms/insertAINodes.d.ts
18
+ declare const insertAINodes: (editor: SlateEditor, nodes: Descendant[], {
19
+ target
20
+ }?: {
21
+ target?: Path;
22
+ }) => void;
23
+ //#endregion
24
+ //#region src/lib/transforms/removeAIMarks.d.ts
25
+ declare const removeAIMarks: (editor: SlateEditor, {
26
+ at
27
+ }?: {
28
+ at?: TLocation;
29
+ }) => void;
30
+ //#endregion
31
+ //#region src/lib/transforms/removeAINodes.d.ts
32
+ declare const removeAINodes: (editor: SlateEditor, {
33
+ at
34
+ }?: {
35
+ at?: Path;
36
+ }) => void;
37
+ //#endregion
38
+ //#region src/lib/transforms/undoAI.d.ts
39
+ declare const undoAI: (editor: SlateEditor) => void;
40
+ //#endregion
41
+ //#region src/lib/transforms/withAIBatch.d.ts
42
+ type AIBatch = History['undos'][number] & {
43
+ ai?: boolean;
44
+ };
45
+ declare const withAIBatch: (editor: SlateEditor, fn: () => void, {
46
+ split
47
+ }?: {
48
+ split?: boolean;
49
+ }) => void;
50
+ //#endregion
51
+ //#region src/lib/BaseAIPlugin.d.ts
52
+ type BaseAIPluginConfig = PluginConfig<'ai', {}, {}, {
53
+ ai: {
54
+ /** Commit the active preview as one fresh undoable batch. */
55
+ acceptPreview: OmitFirst<typeof acceptAIPreview>;
56
+ /** Capture the rollback slice and selection for AI preview. */
57
+ beginPreview: OmitFirst<typeof beginAIPreview>;
58
+ /** Restore the rollback point and clear active preview state. */
59
+ cancelPreview: OmitFirst<typeof cancelAIPreview>;
60
+ /** Clear active preview bookkeeping without restoring content. */
61
+ discardPreview: OmitFirst<typeof discardAIPreview>;
62
+ /** Report whether an AI preview rollback point is active. */
63
+ hasPreview: OmitFirst<typeof hasAIPreview>;
64
+ insertNodes: OmitFirst<typeof insertAINodes>;
65
+ removeMarks: OmitFirst<typeof removeAIMarks>;
66
+ removeNodes: OmitFirst<typeof removeAINodes>;
67
+ undo: OmitFirst<typeof undoAI>;
68
+ };
69
+ }>;
70
+ declare const BaseAIPlugin: platejs0.SlatePlugin<PluginConfig<"ai", {}, {}, {
71
+ ai: {
72
+ acceptPreview: ((_value?: platejs0.Value | undefined) => boolean) & ((_value?: platejs0.Value | undefined) => boolean);
73
+ beginPreview: ((args_0?: {
74
+ originalBlocks?: platejs0.Value;
75
+ } | undefined) => boolean) & ((args_0?: {
76
+ originalBlocks?: platejs0.Value;
77
+ } | undefined) => boolean);
78
+ cancelPreview: (() => boolean) & (() => boolean);
79
+ discardPreview: (() => boolean) & (() => boolean);
80
+ hasPreview: (() => boolean) & (() => boolean);
81
+ insertNodes: ((nodes: platejs0.Descendant[], args_1?: {
82
+ target?: platejs0.Path;
83
+ } | undefined) => void) & ((nodes: platejs0.Descendant[], args_1?: {
84
+ target?: platejs0.Path;
85
+ } | undefined) => void);
86
+ removeMarks: ((args_0?: {
87
+ at?: platejs0.TLocation;
88
+ } | undefined) => void) & ((args_0?: {
89
+ at?: platejs0.TLocation;
90
+ } | undefined) => void);
91
+ removeNodes: ((args_0?: {
92
+ at?: platejs0.Path;
93
+ } | undefined) => void) & ((args_0?: {
94
+ at?: platejs0.Path;
95
+ } | undefined) => void);
96
+ undo: (() => void) & (() => void);
97
+ };
98
+ }, {}>>;
99
+ //#endregion
100
+ //#region src/lib/types.d.ts
101
+ type AIToolName = 'comment' | 'edit' | 'generate' | null;
102
+ type AIMode = 'chat' | 'insert';
103
+ //#endregion
104
+ //#region src/lib/utils/getEditorPrompt.d.ts
105
+ type EditorPrompt = ((params: EditorPromptParams) => string) | PromptConfig | string;
106
+ type EditorPromptParams = {
107
+ editor: SlateEditor;
108
+ isBlockSelecting: boolean;
109
+ isSelecting: boolean;
110
+ };
111
+ type PromptConfig = {
112
+ default: string;
113
+ blockSelecting?: string;
114
+ selecting?: string;
115
+ };
116
+ declare const getEditorPrompt: (editor: SlateEditor, {
117
+ prompt
118
+ }: {
119
+ prompt?: EditorPrompt;
120
+ }) => string;
121
+ //#endregion
122
+ //#region src/lib/utils/replacePlaceholders.d.ts
123
+ type MarkdownType = 'block' | 'blockSelection' | 'blockSelectionWithBlockId' | 'blockWithBlockId' | 'editor' | 'editorWithBlockId' | 'tableCellWithId';
124
+ declare const replacePlaceholders: (editor: SlateEditor, text: string, {
125
+ prompt
126
+ }?: {
127
+ prompt?: string;
128
+ }) => string;
129
+ //#endregion
130
+ //#region src/lib/utils/getMarkdown.d.ts
131
+ declare const getMarkdown: (editor: SlateEditor, {
132
+ type
133
+ }: {
134
+ type: MarkdownType;
135
+ }) => string;
136
+ //#endregion
137
+ export { hasAIPreview as C, discardAIPreview as S, insertAINodes as _, EditorPromptParams as a, beginAIPreview as b, AIMode as c, BaseAIPluginConfig as d, AIBatch as f, removeAIMarks as g, removeAINodes as h, EditorPrompt as i, AIToolName as l, undoAI as m, MarkdownType as n, PromptConfig as o, withAIBatch as p, replacePlaceholders as r, getEditorPrompt as s, getMarkdown as t, BaseAIPlugin as u, AI_PREVIEW_KEY as v, cancelAIPreview as x, acceptAIPreview as y };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as insertAINodes, a as EditorPromptParams, c as AIMode, d as BaseAIPluginConfig, f as AIBatch, g as removeAIMarks, h as removeAINodes, i as EditorPrompt, l as AIToolName, m as undoAI, n as MarkdownType, o as PromptConfig, p as withAIBatch, r as replacePlaceholders, s as getEditorPrompt, t as getMarkdown, u as BaseAIPlugin } from "./index-B_bqJoKL";
2
- export { AIBatch, AIMode, AIToolName, BaseAIPlugin, BaseAIPluginConfig, EditorPrompt, EditorPromptParams, MarkdownType, PromptConfig, getEditorPrompt, getMarkdown, insertAINodes, removeAIMarks, removeAINodes, replacePlaceholders, undoAI, withAIBatch };
1
+ import { C as hasAIPreview, S as discardAIPreview, _ as insertAINodes, a as EditorPromptParams, b as beginAIPreview, c as AIMode, d as BaseAIPluginConfig, f as AIBatch, g as removeAIMarks, h as removeAINodes, i as EditorPrompt, l as AIToolName, m as undoAI, n as MarkdownType, o as PromptConfig, p as withAIBatch, r as replacePlaceholders, s as getEditorPrompt, t as getMarkdown, u as BaseAIPlugin, v as AI_PREVIEW_KEY, x as cancelAIPreview, y as acceptAIPreview } from "./index-D2hJDomP";
2
+ export { AIBatch, AIMode, AIToolName, AI_PREVIEW_KEY, BaseAIPlugin, BaseAIPluginConfig, EditorPrompt, EditorPromptParams, MarkdownType, PromptConfig, acceptAIPreview, beginAIPreview, cancelAIPreview, discardAIPreview, getEditorPrompt, getMarkdown, hasAIPreview, insertAINodes, removeAIMarks, removeAINodes, replacePlaceholders, undoAI, withAIBatch };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as removeAINodes, i as undoAI, n as BaseAIPlugin, o as removeAIMarks, r as withAIBatch, s as insertAINodes, t as getEditorPrompt } from "./getEditorPrompt-BnVrgUl3.js";
1
+ import { a as removeAINodes, c as AI_PREVIEW_KEY, d as cancelAIPreview, f as discardAIPreview, i as undoAI, l as acceptAIPreview, n as BaseAIPlugin, o as removeAIMarks, p as hasAIPreview, r as withAIBatch, s as insertAINodes, t as getEditorPrompt, u as beginAIPreview } from "./getEditorPrompt-axDm9Fg4.js";
2
2
  import { KEYS } from "platejs";
3
3
  import { serializeMd } from "@platejs/markdown";
4
4
  import { getTableGridAbove } from "@platejs/table";
@@ -121,4 +121,4 @@ const replacePlaceholders = (editor, text, { prompt } = {}) => {
121
121
  };
122
122
 
123
123
  //#endregion
124
- export { BaseAIPlugin, getEditorPrompt, getMarkdown, insertAINodes, removeAIMarks, removeAINodes, replacePlaceholders, undoAI, withAIBatch };
124
+ export { AI_PREVIEW_KEY, BaseAIPlugin, acceptAIPreview, beginAIPreview, cancelAIPreview, discardAIPreview, getEditorPrompt, getMarkdown, hasAIPreview, insertAINodes, removeAIMarks, removeAINodes, replacePlaceholders, undoAI, withAIBatch };
@@ -1,6 +1,8 @@
1
- import { c as AIMode, d as BaseAIPluginConfig, i as EditorPrompt, l as AIToolName } from "../index-B_bqJoKL";
2
- import { Descendant, EditorNodesOptions, ExtendConfig, NodeEntry, OmitFirst, Path, PluginConfig, Range, RemoveNodesOptions, SlateEditor, TElement as TElement$1, TIdElement, TRange, TTableElement, TText } from "platejs";
1
+ import { c as AIMode, d as BaseAIPluginConfig, i as EditorPrompt, l as AIToolName } from "../index-D2hJDomP";
2
+ import * as platejs12 from "platejs";
3
+ import { Descendant, EditorNodesOptions, ExtendConfig, NodeEntry, OmitFirst, Path, PluginConfig, Range, RemoveNodesOptions, SlateEditor, TElement, TIdElement, TRange, TTableElement, TText } from "platejs";
3
4
  import { DeserializeMdOptions, SerializeMdOptions } from "@platejs/markdown";
5
+ import * as platejs_react0 from "platejs/react";
4
6
  import { OverrideEditor, PlateEditor, RenderNodeWrapperProps } from "platejs/react";
5
7
  import React from "react";
6
8
  import { ChatRequestOptions, UIMessage } from "ai";
@@ -9,7 +11,35 @@ import { TriggerComboboxPluginOptions } from "@platejs/combobox";
9
11
 
10
12
  //#region src/react/ai/AIPlugin.d.ts
11
13
  type AIPluginConfig = ExtendConfig<BaseAIPluginConfig>;
12
- declare const AIPlugin: any;
14
+ declare const AIPlugin: platejs_react0.PlatePlugin<platejs12.PluginConfig<"ai", {}, {}, {
15
+ ai: {
16
+ acceptPreview: ((_value?: platejs12.Value | undefined) => boolean) & ((_value?: platejs12.Value | undefined) => boolean);
17
+ beginPreview: ((args_0?: {
18
+ originalBlocks?: platejs12.Value;
19
+ } | undefined) => boolean) & ((args_0?: {
20
+ originalBlocks?: platejs12.Value;
21
+ } | undefined) => boolean);
22
+ cancelPreview: (() => boolean) & (() => boolean);
23
+ discardPreview: (() => boolean) & (() => boolean);
24
+ hasPreview: (() => boolean) & (() => boolean);
25
+ insertNodes: ((nodes: platejs12.Descendant[], args_1?: {
26
+ target?: platejs12.Path;
27
+ } | undefined) => void) & ((nodes: platejs12.Descendant[], args_1?: {
28
+ target?: platejs12.Path;
29
+ } | undefined) => void);
30
+ removeMarks: ((args_0?: {
31
+ at?: platejs12.TLocation;
32
+ } | undefined) => void) & ((args_0?: {
33
+ at?: platejs12.TLocation;
34
+ } | undefined) => void);
35
+ removeNodes: ((args_0?: {
36
+ at?: platejs12.Path;
37
+ } | undefined) => void) & ((args_0?: {
38
+ at?: platejs12.Path;
39
+ } | undefined) => void);
40
+ undo: (() => void) & (() => void);
41
+ };
42
+ }, {}>>;
13
43
  //#endregion
14
44
  //#region src/react/ai-chat/internal/types.d.ts
15
45
  type ToolName = 'comment' | 'edit' | 'generate';
@@ -58,10 +88,10 @@ declare const createFormattedBlocks: ({
58
88
  format,
59
89
  sourceBlock
60
90
  }: {
61
- blocks: TElement$1[];
91
+ blocks: TElement[];
62
92
  format: "all" | "none" | "single";
63
93
  sourceBlock: NodeEntry;
64
- }) => TElement$1[] | null;
94
+ }) => any[] | null;
65
95
  declare const replaceSelectionAIChat: (editor: PlateEditor, sourceEditor: SlateEditor, {
66
96
  format
67
97
  }?: {
@@ -138,7 +168,87 @@ type AIChatPluginConfig = PluginConfig<'aiChat', {
138
168
  removeAnchor: (options?: EditorNodesOptions) => void;
139
169
  };
140
170
  }>;
141
- declare const AIChatPlugin: any;
171
+ declare const AIChatPlugin: platejs_react0.PlatePlugin<PluginConfig<"aiChat", {
172
+ _blockChunks: string;
173
+ _blockPath: Path | null;
174
+ /** @private Using For streamInsertChunk */
175
+ _mdxName: string | null;
176
+ _replaceIds: string[];
177
+ /** @private The Editor used to generate the AI response. */
178
+ aiEditor: SlateEditor | null;
179
+ chat: UseChatHelpers<ChatMessage>;
180
+ chatNodes: TIdElement[];
181
+ chatSelection: TRange | null;
182
+ /** @deprecated Use api.aiChat.node({streaming:true}) instead */
183
+ experimental_lastTextId: string | null;
184
+ /**
185
+ * Specifies how the assistant message is handled:
186
+ *
187
+ * - 'insert': Directly inserts content into the editor without preview.
188
+ * - 'chat': Initiates an interactive session to review and refine content
189
+ * before insertion.
190
+ */
191
+ mode: AIMode;
192
+ open: boolean;
193
+ /** Whether the AI response is currently streaming. Cursor mode only. */
194
+ streaming: boolean;
195
+ toolName: AIToolName;
196
+ } & TriggerComboboxPluginOptions, {
197
+ aiChat: {
198
+ reset: OmitFirst<typeof resetAIChat>;
199
+ submit: OmitFirst<typeof submitAIChat>;
200
+ hide: (options?: {
201
+ focus?: boolean;
202
+ undo?: boolean;
203
+ }) => void;
204
+ node: (options?: EditorNodesOptions & {
205
+ anchor?: boolean;
206
+ streaming?: boolean;
207
+ }) => NodeEntry | undefined;
208
+ reload: () => void;
209
+ show: () => void;
210
+ stop: () => void;
211
+ };
212
+ } & Record<"aiChat", Pick<{
213
+ reset: OmitFirst<typeof resetAIChat>;
214
+ submit: OmitFirst<typeof submitAIChat>;
215
+ hide: (options?: {
216
+ focus?: boolean;
217
+ undo?: boolean;
218
+ }) => void;
219
+ node: (options?: EditorNodesOptions & {
220
+ anchor?: boolean;
221
+ streaming?: boolean;
222
+ }) => NodeEntry | undefined;
223
+ reload: () => void;
224
+ show: () => void;
225
+ stop: () => void;
226
+ }, "node" | "reset" | "stop" | "submit">> & Record<"aiChat", {
227
+ hide: ({
228
+ focus,
229
+ undo
230
+ }?: {
231
+ focus?: boolean;
232
+ undo?: boolean;
233
+ }) => void;
234
+ show: () => void;
235
+ }>, {
236
+ aiChat: {
237
+ accept: OmitFirst<typeof acceptAIChat>;
238
+ insertBelow: OmitFirst<typeof insertBelowAIChat>;
239
+ replaceSelection: OmitFirst<typeof replaceSelectionAIChat>;
240
+ removeAnchor: (options?: EditorNodesOptions) => void;
241
+ };
242
+ } & Record<"aiChat", {
243
+ accept: () => void;
244
+ insertBelow: (sourceEditor: SlateEditor, args_1?: {
245
+ format?: "all" | "none" | "single";
246
+ } | undefined) => void;
247
+ removeAnchor: (options?: platejs12.RemoveNodesOptions | undefined) => void;
248
+ replaceSelection: (sourceEditor: SlateEditor, args_1?: {
249
+ format?: "all" | "none" | "single";
250
+ } | undefined) => void;
251
+ }>, {}>>;
142
252
  //#endregion
143
253
  //#region src/react/ai-chat/withAIChat.d.ts
144
254
  declare const withAIChat: OverrideEditor<AIChatPluginConfig>;
@@ -152,7 +262,7 @@ declare const withAIChat: OverrideEditor<AIChatPluginConfig>;
152
262
  */
153
263
  declare const useAIChatEditor: (editor: SlateEditor, content: string, {
154
264
  parser
155
- }?: DeserializeMdOptions) => any;
265
+ }?: DeserializeMdOptions) => platejs12.Value;
156
266
  //#endregion
157
267
  //#region src/react/ai-chat/hooks/useChatChunk.d.ts
158
268
  declare const useChatChunk: ({
@@ -188,19 +298,31 @@ declare const useEditorChat: ({
188
298
  }: UseEditorChatOptions) => void;
189
299
  //#endregion
190
300
  //#region src/react/ai-chat/streaming/streamDeserializeInlineMd.d.ts
191
- declare const streamDeserializeInlineMd: (editor: PlateEditor, text: string, options?: DeserializeMdOptions) => any;
301
+ declare const streamDeserializeInlineMd: (editor: PlateEditor, text: string, options?: DeserializeMdOptions) => platejs12.Descendant[] | {
302
+ text: string;
303
+ }[];
192
304
  //#endregion
193
305
  //#region src/react/ai-chat/streaming/streamDeserializeMd.d.ts
194
- declare const streamDeserializeMd: (editor: PlateEditor, data: string, options?: DeserializeMdOptions) => TElement[];
306
+ declare const streamDeserializeMd: (editor: PlateEditor, data: string, options?: DeserializeMdOptions) => TElement[] | {
307
+ children: {
308
+ text: string;
309
+ }[];
310
+ type: string;
311
+ }[];
195
312
  //#endregion
196
313
  //#region src/react/ai-chat/streaming/streamInsertChunk.d.ts
197
314
  type SteamInsertChunkOptions = {
198
315
  elementProps?: any;
199
316
  textProps?: any;
200
317
  };
318
+ declare const getInsertPreviewStart: (editor: SlateEditor) => {
319
+ path: Path;
320
+ startBlock: platejs12.TElement | platejs12.TText | undefined;
321
+ startInEmptyParagraph: boolean;
322
+ };
201
323
  /** @experimental */
202
324
  declare function streamInsertChunk(editor: PlateEditor, chunk: string, options?: SteamInsertChunkOptions): void;
203
- declare const getCurrentBlockPath: (editor: SlateEditor) => any;
325
+ declare const getCurrentBlockPath: (editor: SlateEditor) => Path;
204
326
  //#endregion
205
327
  //#region src/react/ai-chat/streaming/streamSerializeMd.d.ts
206
328
  declare const streamSerializeMd: (editor: PlateEditor, options: SerializeMdOptions, chunk: string) => string;
@@ -209,10 +331,10 @@ declare const streamSerializeMd: (editor: PlateEditor, options: SerializeMdOptio
209
331
  declare const escapeInput: (data: string) => string;
210
332
  //#endregion
211
333
  //#region src/react/ai-chat/streaming/utils/getListNode.d.ts
212
- declare const getListNode: (editor: PlateEditor, node: TElement$1) => TElement$1;
334
+ declare const getListNode: (editor: PlateEditor, node: TElement) => TElement;
213
335
  //#endregion
214
336
  //#region src/react/ai-chat/streaming/utils/isSameNode.d.ts
215
- declare const isSameNode: (editor: PlateEditor, node1: TElement$1 | TText, node2: TElement$1 | TText) => boolean;
337
+ declare const isSameNode: (editor: PlateEditor, node1: TElement | TText, node2: TElement | TText) => boolean;
216
338
  //#endregion
217
339
  //#region src/react/ai-chat/streaming/utils/nodesWithProps.d.ts
218
340
  declare const nodesWithProps: (editor: PlateEditor, nodes: Descendant[], options: SteamInsertChunkOptions) => Descendant[];
@@ -250,8 +372,8 @@ type TableCellUpdate = {
250
372
  declare const applyTableCellSuggestion: (editor: SlateEditor, cellUpdate: TableCellUpdate) => void;
251
373
  //#endregion
252
374
  //#region src/react/ai-chat/utils/getLastAssistantMessage.d.ts
253
- declare function getLastAssistantMessage(editor: PlateEditor): any;
254
- declare function useLastAssistantMessage(): any;
375
+ declare function getLastAssistantMessage(editor: PlateEditor): ChatMessage | undefined;
376
+ declare function useLastAssistantMessage(): ChatMessage | undefined;
255
377
  //#endregion
256
378
  //#region src/react/ai-chat/utils/nestedContainerUtils.d.ts
257
379
  /** Check if nodes is a single table with single cell */
@@ -388,7 +510,86 @@ type CompletionState = {
388
510
  error?: Error | null;
389
511
  isLoading?: boolean;
390
512
  };
391
- declare const CopilotPlugin: any;
513
+ declare const CopilotPlugin: platejs_react0.PlatePlugin<PluginConfig<"copilot", CompletionState & {
514
+ /**
515
+ * AI completion options. See:
516
+ * {@link https://sdk.vercel.ai/docs/reference/ai-sdk-ui/use-completion#parameters | AI SDK UI useCompletion Parameters}
517
+ */
518
+ completeOptions?: Partial<CompleteOptions>;
519
+ /**
520
+ * Debounce delay for auto triggering AI completion.
521
+ *
522
+ * @default 0
523
+ */
524
+ debounceDelay?: number;
525
+ /** Get the next word to be inserted. */
526
+ getNextWord?: GetNextWord;
527
+ /** Render the ghost text. */
528
+ renderGhostText?: (() => React.ReactNode) | null;
529
+ shouldAbort?: boolean;
530
+ /** The node id where the suggestion is located. */
531
+ suggestionNodeId?: string | null;
532
+ /** The text of the suggestion. */
533
+ suggestionText?: string | null;
534
+ /**
535
+ * Conditions to auto trigger copilot, used in addition to triggerQuery.
536
+ * Disabling defaults to:
537
+ *
538
+ * - Block above is empty
539
+ * - Block above ends with a space
540
+ * - There is already a suggestion
541
+ */
542
+ autoTriggerQuery?: (options: {
543
+ editor: PlateEditor;
544
+ }) => boolean;
545
+ /**
546
+ * Get the prompt for AI completion.
547
+ *
548
+ * @default serializeMdNodes(editor.api.block({ highest: true }))
549
+ */
550
+ getPrompt?: (options: {
551
+ editor: PlateEditor;
552
+ }) => string;
553
+ /**
554
+ * Conditions to trigger copilot. Disabling defaults to:
555
+ *
556
+ * - Selection is expanded
557
+ * - Selection is not at the end of block
558
+ */
559
+ triggerQuery?: (options: {
560
+ editor: PlateEditor;
561
+ }) => boolean;
562
+ }, {
563
+ copilot: {
564
+ triggerSuggestion: OmitFirst<typeof triggerCopilotSuggestion>;
565
+ reject: () => false | undefined;
566
+ setBlockSuggestion: (options: {
567
+ text: string;
568
+ id?: string;
569
+ }) => void;
570
+ stop: () => void;
571
+ };
572
+ } & Record<"copilot", Omit<{
573
+ triggerSuggestion: OmitFirst<typeof triggerCopilotSuggestion>;
574
+ reject: () => false | undefined;
575
+ setBlockSuggestion: (options: {
576
+ text: string;
577
+ id?: string;
578
+ }) => void;
579
+ stop: () => void;
580
+ }, "reject">> & Record<"copilot", {
581
+ reject: () => false | undefined;
582
+ }>, {
583
+ copilot: {
584
+ accept: OmitFirst<typeof acceptCopilot>;
585
+ acceptNextWord: OmitFirst<typeof acceptCopilotNextWord>;
586
+ };
587
+ } & Record<"copilot", {
588
+ accept: () => false | undefined;
589
+ acceptNextWord: () => false | undefined;
590
+ }>, {
591
+ isSuggested?: (id: string) => boolean;
592
+ }>>;
392
593
  //#endregion
393
594
  //#region src/react/copilot/renderCopilotBelowNodes.d.ts
394
595
  declare const renderCopilotBelowNodes: ({
@@ -405,4 +606,4 @@ declare const withCopilot: OverrideEditor<CopilotPluginConfig>;
405
606
  //#region src/react/copilot/utils/withoutAbort.d.ts
406
607
  declare const withoutAbort: (editor: PlateEditor, fn: () => void) => void;
407
608
  //#endregion
408
- export { AIChatPlugin, AIChatPluginConfig, AIPlugin, AIPluginConfig, CallCompletionApiOptions, CompleteOptions, CopilotPlugin, CopilotPluginConfig, GetNextWord, SteamInsertChunkOptions, TableCellUpdate, UseEditorChatOptions, acceptAIChat, acceptAISuggestions, acceptCopilot, acceptCopilotNextWord, aiCommentToRange, applyAISuggestions, applyTableCellSuggestion, callCompletionApi, createFormattedBlocks, escapeInput, findTextRangeInBlock, getChunkTrimmed, getCurrentBlockPath, getLastAssistantMessage, getListNode, getNextWord, getTableCellChildren, insertBelowAIChat, insertBelowGenerate, isCompleteCodeBlock, isCompleteMath, isSameNode, isSingleCellTable, nodesWithProps, rejectAISuggestions, removeAnchorAIChat, renderCopilotBelowNodes, replaceSelectionAIChat, resetAIChat, streamDeserializeInlineMd, streamDeserializeMd, streamInsertChunk, streamSerializeMd, submitAIChat, triggerCopilotSuggestion, useAIChatEditor, useChatChunk, useEditorChat, useLastAssistantMessage, withAIChat, withCopilot, withTransient, withoutAbort, withoutSuggestionAndComments };
609
+ export { AIChatPlugin, AIChatPluginConfig, AIPlugin, AIPluginConfig, CallCompletionApiOptions, CompleteOptions, CopilotPlugin, CopilotPluginConfig, GetNextWord, SteamInsertChunkOptions, TableCellUpdate, UseEditorChatOptions, acceptAIChat, acceptAISuggestions, acceptCopilot, acceptCopilotNextWord, aiCommentToRange, applyAISuggestions, applyTableCellSuggestion, callCompletionApi, createFormattedBlocks, escapeInput, findTextRangeInBlock, getChunkTrimmed, getCurrentBlockPath, getInsertPreviewStart, getLastAssistantMessage, getListNode, getNextWord, getTableCellChildren, insertBelowAIChat, insertBelowGenerate, isCompleteCodeBlock, isCompleteMath, isSameNode, isSingleCellTable, nodesWithProps, rejectAISuggestions, removeAnchorAIChat, renderCopilotBelowNodes, replaceSelectionAIChat, resetAIChat, streamDeserializeInlineMd, streamDeserializeMd, streamInsertChunk, streamSerializeMd, submitAIChat, triggerCopilotSuggestion, useAIChatEditor, useChatChunk, useEditorChat, useLastAssistantMessage, withAIChat, withCopilot, withTransient, withoutAbort, withoutSuggestionAndComments };
@@ -1,5 +1,6 @@
1
- import { n as BaseAIPlugin, r as withAIBatch, t as getEditorPrompt } from "../getEditorPrompt-BnVrgUl3.js";
1
+ import { c as AI_PREVIEW_KEY, n as BaseAIPlugin, p as hasAIPreview, r as withAIBatch, t as getEditorPrompt } from "../getEditorPrompt-axDm9Fg4.js";
2
2
  import { ElementApi, KEYS, NodeApi, PathApi, RangeApi, TextApi, bindFirst, getPluginKey, getPluginType, isDefined, nanoid } from "platejs";
3
+ import cloneDeep from "lodash/cloneDeep.js";
3
4
  import { SkipSuggestionDeletes, acceptSuggestion, diffToSuggestions, getSuggestionKey, getTransientSuggestionKey, rejectSuggestion } from "@platejs/suggestion";
4
5
  import { isSelecting } from "@platejs/selection";
5
6
  import { MarkdownPlugin, deserializeInlineMd, deserializeMd, serializeInlineMd, serializeMd } from "@platejs/markdown";
@@ -7,7 +8,6 @@ import { createTPlatePlugin, getEditorPlugin, toPlatePlugin, useEditorPlugin, us
7
8
  import { distance } from "fastest-levenshtein";
8
9
  import { BlockSelectionPlugin, removeBlockSelectionNodes } from "@platejs/selection/react";
9
10
  import { SuggestionPlugin } from "@platejs/suggestion/react";
10
- import cloneDeep from "lodash/cloneDeep.js";
11
11
  import { c } from "react-compiler-runtime";
12
12
  import React, { useEffect, useMemo, useRef } from "react";
13
13
  import debounce from "lodash/debounce.js";
@@ -168,23 +168,31 @@ const acceptAISuggestions = (editor) => {
168
168
 
169
169
  //#endregion
170
170
  //#region src/react/ai-chat/transforms/acceptAIChat.ts
171
+ const getAcceptedInsertFocusPoint = (editor) => {
172
+ let endIndex = null;
173
+ editor.children.forEach((node, index) => {
174
+ if (ElementApi.isElement(node) && node[AI_PREVIEW_KEY]) endIndex = index;
175
+ });
176
+ if (endIndex === null) return null;
177
+ return editor.api.end([endIndex]) ?? null;
178
+ };
171
179
  const acceptAIChat = (editor) => {
172
180
  const mode = editor.getOption(AIChatPlugin, "mode");
173
181
  if (mode === "insert") {
174
- const { tf } = getEditorPlugin(editor, AIPlugin);
182
+ const ai = editor.getTransforms(BaseAIPlugin).ai;
175
183
  const api = editor.getApi({ key: KEYS.ai });
176
- const lastAINodePath = api.aiChat.node({
177
- at: [],
178
- reverse: true
179
- })[1];
180
- withAIBatch(editor, () => {
181
- tf.ai.removeMarks();
184
+ const focusPoint = getAcceptedInsertFocusPoint(editor);
185
+ if (!ai.acceptPreview()) withAIBatch(editor, () => {
186
+ editor.tf.unsetNodes(AI_PREVIEW_KEY, {
187
+ at: [],
188
+ match: (node) => ElementApi.isElement(node) && !!node[AI_PREVIEW_KEY]
189
+ });
190
+ ai.removeMarks();
182
191
  editor.getTransforms(AIChatPlugin).aiChat.removeAnchor();
183
192
  });
184
193
  api.aiChat.hide();
185
194
  editor.tf.focus();
186
- const focusPoint = editor.api.end(lastAINodePath);
187
- editor.tf.setSelection({
195
+ if (focusPoint) editor.tf.select({
188
196
  anchor: focusPoint,
189
197
  focus: focusPoint
190
198
  });
@@ -389,7 +397,8 @@ const resetAIChat = (editor, { undo = true } = {}) => {
389
397
  mode: "insert",
390
398
  toolName: null
391
399
  });
392
- if (undo) editor.getTransforms(AIPlugin).ai.undo();
400
+ if (undo) editor.getTransforms(BaseAIPlugin).ai.undo();
401
+ else editor.getTransforms(BaseAIPlugin).ai.discardPreview();
393
402
  };
394
403
 
395
404
  //#endregion
@@ -401,7 +410,7 @@ const submitAIChat = (editor, input, { mode, options, prompt, toolName: toolName
401
410
  if (!prompt && input?.length === 0) return;
402
411
  if (!prompt) prompt = input;
403
412
  if (!mode) mode = isSelecting(editor) ? "chat" : "insert";
404
- if (mode === "insert") editor.getTransforms(AIPlugin).ai.undo();
413
+ if (mode === "insert") editor.getTransforms(BaseAIPlugin).ai.undo();
405
414
  setOption("mode", mode);
406
415
  setOption("toolName", toolName);
407
416
  const blocks = editor.getApi(BlockSelectionPlugin).blockSelection.getNodes();
@@ -517,7 +526,7 @@ const insertBelowAIChat = (editor, sourceEditor, { format = "single" } = {}) =>
517
526
  if (toolName === "generate") return insertBelowGenerate(editor, sourceEditor, { format });
518
527
  const selectedBlocks = editor.getApi(BlockSelectionPlugin).blockSelection.getNodes();
519
528
  const selectedIds = editor.getOptions(BlockSelectionPlugin).selectedIds;
520
- editor.getTransforms(AIPlugin).ai.undo();
529
+ editor.getTransforms(BaseAIPlugin).ai.undo();
521
530
  const insertBlocksAndSelect = editor.getTransforms(BlockSelectionPlugin).blockSelection.insertBlocksAndSelect;
522
531
  if (!selectedIds || selectedIds.size === 0) return;
523
532
  const lastBlock = selectedBlocks.at(-1);
@@ -594,7 +603,7 @@ const removeAnchorAIChat = (editor, options) => {
594
603
  //#endregion
595
604
  //#region src/react/ai-chat/withAIChat.ts
596
605
  const withAIChat = ({ api, editor, getOptions, tf: { insertText, normalizeNode }, type }) => {
597
- const tf = editor.getTransforms(AIPlugin);
606
+ const ai = editor.getTransforms(BaseAIPlugin).ai;
598
607
  const matchesTrigger = (text) => {
599
608
  const { trigger } = getOptions();
600
609
  if (trigger instanceof RegExp) return trigger.test(text);
@@ -619,7 +628,7 @@ const withAIChat = ({ api, editor, getOptions, tf: { insertText, normalizeNode }
619
628
  normalizeNode(entry) {
620
629
  const [node, path] = entry;
621
630
  if (node[KEYS.ai] && !getOptions().open) {
622
- tf.ai.removeMarks({ at: path });
631
+ ai.removeMarks({ at: path });
623
632
  return;
624
633
  }
625
634
  if (ElementApi.isElement(node) && node.type === type && !getOptions().open) {
@@ -683,7 +692,7 @@ const AIChatPlugin = createTPlatePlugin({
683
692
  },
684
693
  reload: () => {
685
694
  const { chat, chatNodes, chatSelection } = getOptions();
686
- editor.getTransforms(AIPlugin).ai.undo();
695
+ editor.getTransforms(BaseAIPlugin).ai.undo();
687
696
  if (chatSelection) editor.tf.setSelection(chatSelection);
688
697
  else editor.getApi(BlockSelectionPlugin).blockSelection.set(chatNodes.map((node) => node.id));
689
698
  const blocks = editor.getApi(BlockSelectionPlugin).blockSelection.getNodes();
@@ -995,17 +1004,35 @@ const getNextPath = (path, length) => {
995
1004
  for (let i = 0; i < length; i++) result = PathApi.next(result);
996
1005
  return result;
997
1006
  };
1007
+ const withPreviewElementProps = (editor, options) => {
1008
+ if (!hasAIPreview(editor)) return options;
1009
+ return {
1010
+ ...options,
1011
+ elementProps: {
1012
+ ...options.elementProps,
1013
+ [AI_PREVIEW_KEY]: true
1014
+ }
1015
+ };
1016
+ };
1017
+ const getInsertPreviewStart = (editor) => {
1018
+ const path = getCurrentBlockPath(editor);
1019
+ const startBlock = editor.api.node(path)?.[0];
1020
+ return {
1021
+ path,
1022
+ startBlock,
1023
+ startInEmptyParagraph: !!startBlock && NodeApi.string(startBlock).length === 0 && startBlock.type === getPluginType(editor, KEYS.p)
1024
+ };
1025
+ };
998
1026
  /** @experimental */
999
1027
  function streamInsertChunk(editor, chunk, options = {}) {
1028
+ const insertOptions = withPreviewElementProps(editor, options);
1000
1029
  const { _blockChunks, _blockPath } = editor.getOptions(AIChatPlugin);
1001
1030
  if (_blockPath === null) {
1002
1031
  const blocks = streamDeserializeMd(editor, chunk);
1003
- const path = getCurrentBlockPath(editor);
1004
- const startBlock = editor.api.node(path)[0];
1005
- const startInEmptyParagraph = NodeApi.string(startBlock).length === 0 && startBlock.type === getPluginType(editor, KEYS.p);
1032
+ const { path, startInEmptyParagraph } = getInsertPreviewStart(editor);
1006
1033
  if (startInEmptyParagraph) editor.tf.removeNodes({ at: path });
1007
1034
  if (blocks.length > 0) {
1008
- editor.tf.insertNodes(nodesWithProps(editor, [blocks[0]], options), {
1035
+ editor.tf.insertNodes(nodesWithProps(editor, [blocks[0]], insertOptions), {
1009
1036
  at: path,
1010
1037
  nextBlock: !startInEmptyParagraph,
1011
1038
  select: true
@@ -1015,7 +1042,7 @@ function streamInsertChunk(editor, chunk, options = {}) {
1015
1042
  if (blocks.length > 1) {
1016
1043
  const nextBlocks = blocks.slice(1);
1017
1044
  const nextPath = getCurrentBlockPath(editor);
1018
- editor.tf.insertNodes(nodesWithProps(editor, nextBlocks, options), {
1045
+ editor.tf.insertNodes(nodesWithProps(editor, nextBlocks, insertOptions), {
1019
1046
  at: nextPath,
1020
1047
  nextBlock: true,
1021
1048
  select: true
@@ -1034,7 +1061,7 @@ function streamInsertChunk(editor, chunk, options = {}) {
1034
1061
  const currentBlock = editor.api.node(_blockPath)[0];
1035
1062
  if (isSameNode(editor, currentBlock, tempBlocks[0])) {
1036
1063
  const chunkNodes = streamDeserializeInlineMd(editor, chunk);
1037
- editor.tf.insertNodes(nodesWithProps(editor, chunkNodes, options), {
1064
+ editor.tf.insertNodes(nodesWithProps(editor, chunkNodes, insertOptions), {
1038
1065
  at: editor.api.end(_blockPath),
1039
1066
  select: true
1040
1067
  });
@@ -1042,7 +1069,7 @@ function streamInsertChunk(editor, chunk, options = {}) {
1042
1069
  const blockText = NodeApi.string(tempBlocks[0]);
1043
1070
  if (serializedBlock === tempBlockChunks && blockText === serializedBlock) editor.setOption(AIChatPlugin, "_blockChunks", tempBlockChunks);
1044
1071
  else {
1045
- editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], options), {
1072
+ editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], insertOptions), {
1046
1073
  at: _blockPath,
1047
1074
  select: true
1048
1075
  });
@@ -1051,20 +1078,20 @@ function streamInsertChunk(editor, chunk, options = {}) {
1051
1078
  }
1052
1079
  } else {
1053
1080
  const serializedBlock = streamSerializeMd(editor, { value: [tempBlocks[0]] }, tempBlockChunks);
1054
- editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], options), {
1081
+ editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], insertOptions), {
1055
1082
  at: _blockPath,
1056
1083
  select: true
1057
1084
  });
1058
1085
  editor.setOption(AIChatPlugin, "_blockChunks", serializedBlock);
1059
1086
  }
1060
1087
  } else {
1061
- editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], options), {
1088
+ editor.tf.replaceNodes(nodesWithProps(editor, [tempBlocks[0]], insertOptions), {
1062
1089
  at: _blockPath,
1063
1090
  select: true
1064
1091
  });
1065
1092
  if (tempBlocks.length > 1) {
1066
1093
  const newEndBlockPath = getNextPath(_blockPath, tempBlocks.length - 1);
1067
- editor.tf.insertNodes(nodesWithProps(editor, tempBlocks.slice(1), options), {
1094
+ editor.tf.insertNodes(nodesWithProps(editor, tempBlocks.slice(1), insertOptions), {
1068
1095
  at: PathApi.next(_blockPath),
1069
1096
  select: true
1070
1097
  });
@@ -1400,4 +1427,4 @@ const CopilotPlugin = createTPlatePlugin({
1400
1427
  });
1401
1428
 
1402
1429
  //#endregion
1403
- export { AIChatPlugin, AIPlugin, CopilotPlugin, acceptAIChat, acceptAISuggestions, acceptCopilot, acceptCopilotNextWord, aiCommentToRange, applyAISuggestions, applyTableCellSuggestion, callCompletionApi, createFormattedBlocks, escapeInput, findTextRangeInBlock, getChunkTrimmed, getCurrentBlockPath, getLastAssistantMessage, getListNode, getNextWord, getTableCellChildren, insertBelowAIChat, insertBelowGenerate, isCompleteCodeBlock, isCompleteMath, isSameNode, isSingleCellTable, nodesWithProps, rejectAISuggestions, removeAnchorAIChat, renderCopilotBelowNodes, replaceSelectionAIChat, resetAIChat, streamDeserializeInlineMd, streamDeserializeMd, streamInsertChunk, streamSerializeMd, submitAIChat, triggerCopilotSuggestion, useAIChatEditor, useChatChunk, useEditorChat, useLastAssistantMessage, withAIChat, withCopilot, withTransient, withoutAbort, withoutSuggestionAndComments };
1430
+ export { AIChatPlugin, AIPlugin, CopilotPlugin, acceptAIChat, acceptAISuggestions, acceptCopilot, acceptCopilotNextWord, aiCommentToRange, applyAISuggestions, applyTableCellSuggestion, callCompletionApi, createFormattedBlocks, escapeInput, findTextRangeInBlock, getChunkTrimmed, getCurrentBlockPath, getInsertPreviewStart, getLastAssistantMessage, getListNode, getNextWord, getTableCellChildren, insertBelowAIChat, insertBelowGenerate, isCompleteCodeBlock, isCompleteMath, isSameNode, isSingleCellTable, nodesWithProps, rejectAISuggestions, removeAnchorAIChat, renderCopilotBelowNodes, replaceSelectionAIChat, resetAIChat, streamDeserializeInlineMd, streamDeserializeMd, streamInsertChunk, streamSerializeMd, submitAIChat, triggerCopilotSuggestion, useAIChatEditor, useChatChunk, useEditorChat, useLastAssistantMessage, withAIChat, withCopilot, withTransient, withoutAbort, withoutSuggestionAndComments };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platejs/ai",
3
- "version": "52.3.8",
3
+ "version": "52.3.12",
4
4
  "description": "Text AI plugin for Plate",
5
5
  "keywords": [
6
6
  "plate",
@@ -32,15 +32,16 @@
32
32
  "fastest-levenshtein": "1.0.16",
33
33
  "lodash": "^4.17.21",
34
34
  "react-compiler-runtime": "^1.0.0",
35
- "@platejs/markdown": "^52.3.7",
36
- "@platejs/selection": "^52.3.6",
37
- "@platejs/suggestion": "^52.3.8",
38
- "@platejs/table": "^52.3.6"
35
+ "@platejs/markdown": "^52.3.10",
36
+ "@platejs/selection": "^52.3.10",
37
+ "@platejs/suggestion": "^52.3.10",
38
+ "@platejs/table": "^52.3.10"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@ai-sdk/react": "^3.0.0",
42
42
  "ai": "^6.0.0",
43
- "@plate/scripts": "1.0.0"
43
+ "@plate/scripts": "1.0.0",
44
+ "platejs": "^52.3.11"
44
45
  },
45
46
  "peerDependencies": {
46
47
  "platejs": ">=52.0.15",
@@ -1,102 +0,0 @@
1
- import { KEYS, TextApi, bindFirst, createTSlatePlugin, getPluginType } from "platejs";
2
- import { getTransientSuggestionKey } from "@platejs/suggestion";
3
- import { isSelecting } from "@platejs/selection";
4
-
5
- //#region src/lib/transforms/insertAINodes.ts
6
- const insertAINodes = (editor, nodes, { target } = {}) => {
7
- if (!target && !editor.selection?.focus.path) return;
8
- const aiNodes = nodes.map((node) => ({
9
- ...node,
10
- ai: true
11
- }));
12
- editor.tf.withoutNormalizing(() => {
13
- editor.tf.insertNodes(aiNodes, {
14
- at: editor.api.end(target || editor.selection.focus.path),
15
- select: true
16
- });
17
- editor.tf.collapse({ edge: "end" });
18
- });
19
- };
20
-
21
- //#endregion
22
- //#region src/lib/transforms/removeAIMarks.ts
23
- const removeAIMarks = (editor, { at = [] } = {}) => {
24
- const nodeType = getPluginType(editor, KEYS.ai);
25
- editor.tf.unsetNodes(nodeType, {
26
- at,
27
- match: (n) => n[nodeType]
28
- });
29
- };
30
-
31
- //#endregion
32
- //#region src/lib/transforms/removeAINodes.ts
33
- const removeAINodes = (editor, { at = [] } = {}) => {
34
- editor.tf.removeNodes({
35
- at,
36
- match: (n) => TextApi.isText(n) && !!n.ai
37
- });
38
- };
39
-
40
- //#endregion
41
- //#region src/lib/transforms/undoAI.ts
42
- const undoAI = (editor) => {
43
- const hasAINodeOrAISuggestion = editor.api.some({
44
- at: [],
45
- match: (n) => !!n.ai
46
- }) || editor.api.some({
47
- at: [],
48
- match: (n) => !!n[getTransientSuggestionKey()]
49
- });
50
- if (editor.history.undos.at(-1)?.ai && hasAINodeOrAISuggestion) {
51
- editor.undo();
52
- editor.history.redos.pop();
53
- }
54
- };
55
-
56
- //#endregion
57
- //#region src/lib/transforms/withAIBatch.ts
58
- const withAIBatch = (editor, fn, { split } = {}) => {
59
- if (split) editor.tf.withNewBatch(fn);
60
- else editor.tf.withMerging(fn);
61
- const lastBatch = editor.history.undos?.at(-1);
62
- if (lastBatch) lastBatch.ai = true;
63
- };
64
-
65
- //#endregion
66
- //#region src/lib/BaseAIPlugin.ts
67
- const BaseAIPlugin = createTSlatePlugin({
68
- key: KEYS.ai,
69
- node: {
70
- isDecoration: false,
71
- isLeaf: true
72
- }
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
- //#endregion
81
- //#region src/lib/utils/getEditorPrompt.ts
82
- const createPromptFromConfig = (config, params) => {
83
- const { isBlockSelecting, isSelecting: isSelecting$1 } = params;
84
- if (isBlockSelecting && config.blockSelecting) return config.blockSelecting ?? config.default;
85
- if (isSelecting$1 && config.selecting) return config.selecting ?? config.default;
86
- return config.default;
87
- };
88
- const getEditorPrompt = (editor, { prompt = "" }) => {
89
- const params = {
90
- editor,
91
- isBlockSelecting: editor.getOption({ key: KEYS.blockSelection }, "isSelectingSome"),
92
- isSelecting: isSelecting(editor)
93
- };
94
- let promptText = "";
95
- if (typeof prompt === "function") promptText = prompt(params);
96
- else if (typeof prompt === "object") promptText = createPromptFromConfig(prompt, params);
97
- else promptText = prompt;
98
- return promptText;
99
- };
100
-
101
- //#endregion
102
- export { removeAINodes as a, undoAI as i, BaseAIPlugin as n, removeAIMarks as o, withAIBatch as r, insertAINodes as s, getEditorPrompt as t };
@@ -1,84 +0,0 @@
1
- import { Descendant, History, OmitFirst, Path, PluginConfig, SlateEditor, TLocation } from "platejs";
2
-
3
- //#region src/lib/transforms/insertAINodes.d.ts
4
- declare const insertAINodes: (editor: SlateEditor, nodes: Descendant[], {
5
- target
6
- }?: {
7
- target?: Path;
8
- }) => void;
9
- //#endregion
10
- //#region src/lib/transforms/removeAIMarks.d.ts
11
- declare const removeAIMarks: (editor: SlateEditor, {
12
- at
13
- }?: {
14
- at?: TLocation;
15
- }) => void;
16
- //#endregion
17
- //#region src/lib/transforms/removeAINodes.d.ts
18
- declare const removeAINodes: (editor: SlateEditor, {
19
- at
20
- }?: {
21
- at?: Path;
22
- }) => void;
23
- //#endregion
24
- //#region src/lib/transforms/undoAI.d.ts
25
- declare const undoAI: (editor: SlateEditor) => void;
26
- //#endregion
27
- //#region src/lib/transforms/withAIBatch.d.ts
28
- type AIBatch = History['undos'][number] & {
29
- ai?: boolean;
30
- };
31
- declare const withAIBatch: (editor: SlateEditor, fn: () => void, {
32
- split
33
- }?: {
34
- split?: boolean;
35
- }) => void;
36
- //#endregion
37
- //#region src/lib/BaseAIPlugin.d.ts
38
- type BaseAIPluginConfig = PluginConfig<'ai', {}, {}, {
39
- ai: {
40
- insertNodes: OmitFirst<typeof insertAINodes>;
41
- removeMarks: OmitFirst<typeof removeAIMarks>;
42
- removeNodes: OmitFirst<typeof removeAINodes>;
43
- };
44
- }>;
45
- declare const BaseAIPlugin: any;
46
- //#endregion
47
- //#region src/lib/types.d.ts
48
- type AIToolName = 'comment' | 'edit' | 'generate' | null;
49
- type AIMode = 'chat' | 'insert';
50
- //#endregion
51
- //#region src/lib/utils/getEditorPrompt.d.ts
52
- type EditorPrompt = ((params: EditorPromptParams) => string) | PromptConfig | string;
53
- type EditorPromptParams = {
54
- editor: SlateEditor;
55
- isBlockSelecting: boolean;
56
- isSelecting: boolean;
57
- };
58
- type PromptConfig = {
59
- default: string;
60
- blockSelecting?: string;
61
- selecting?: string;
62
- };
63
- declare const getEditorPrompt: (editor: SlateEditor, {
64
- prompt
65
- }: {
66
- prompt?: EditorPrompt;
67
- }) => string;
68
- //#endregion
69
- //#region src/lib/utils/replacePlaceholders.d.ts
70
- type MarkdownType = 'block' | 'blockSelection' | 'blockSelectionWithBlockId' | 'blockWithBlockId' | 'editor' | 'editorWithBlockId' | 'tableCellWithId';
71
- declare const replacePlaceholders: (editor: SlateEditor, text: string, {
72
- prompt
73
- }?: {
74
- prompt?: string;
75
- }) => string;
76
- //#endregion
77
- //#region src/lib/utils/getMarkdown.d.ts
78
- declare const getMarkdown: (editor: SlateEditor, {
79
- type
80
- }: {
81
- type: MarkdownType;
82
- }) => string;
83
- //#endregion
84
- export { insertAINodes as _, EditorPromptParams as a, AIMode as c, BaseAIPluginConfig as d, AIBatch as f, removeAIMarks as g, removeAINodes as h, EditorPrompt as i, AIToolName as l, undoAI as m, MarkdownType as n, PromptConfig as o, withAIBatch as p, replacePlaceholders as r, getEditorPrompt as s, getMarkdown as t, BaseAIPlugin as u };