prosemirror-suggestcat-plugin 1.1.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +291 -158
  2. package/dist/api/config.d.ts +7 -0
  3. package/dist/api/index.d.ts +4 -0
  4. package/dist/api/request.d.ts +16 -0
  5. package/dist/api/streaming.d.ts +16 -0
  6. package/dist/api/types.d.ts +30 -0
  7. package/dist/autoComplete/actions.d.ts +33 -0
  8. package/dist/autoComplete/index.d.ts +35 -0
  9. package/dist/autoComplete/plugin.d.ts +14 -0
  10. package/dist/autoComplete/streaming.d.ts +48 -0
  11. package/dist/autoComplete/types.d.ts +68 -0
  12. package/dist/blockRunner/defaults.d.ts +3 -0
  13. package/dist/blockRunner/examples/linkDetector/decorations.d.ts +3 -0
  14. package/dist/blockRunner/examples/linkDetector/index.d.ts +4 -0
  15. package/dist/blockRunner/examples/linkDetector/plugin.d.ts +5 -0
  16. package/dist/blockRunner/examples/linkDetector/processor.d.ts +4 -0
  17. package/dist/blockRunner/examples/linkDetector/types.d.ts +8 -0
  18. package/dist/blockRunner/examples/randomProcessor/decorations.d.ts +4 -0
  19. package/dist/blockRunner/examples/randomProcessor/index.d.ts +4 -0
  20. package/dist/blockRunner/examples/randomProcessor/plugin.d.ts +5 -0
  21. package/dist/blockRunner/examples/randomProcessor/processor.d.ts +5 -0
  22. package/dist/blockRunner/examples/randomProcessor/types.d.ts +14 -0
  23. package/dist/blockRunner/examples/sentenceLength/decorations.d.ts +3 -0
  24. package/dist/blockRunner/examples/sentenceLength/index.d.ts +4 -0
  25. package/dist/blockRunner/examples/sentenceLength/plugin.d.ts +5 -0
  26. package/dist/blockRunner/examples/sentenceLength/processor.d.ts +5 -0
  27. package/dist/blockRunner/examples/sentenceLength/types.d.ts +12 -0
  28. package/dist/blockRunner/examples/wordComplexity/decorations.d.ts +3 -0
  29. package/dist/blockRunner/examples/wordComplexity/index.d.ts +4 -0
  30. package/dist/blockRunner/examples/wordComplexity/plugin.d.ts +5 -0
  31. package/dist/blockRunner/examples/wordComplexity/processor.d.ts +6 -0
  32. package/dist/blockRunner/examples/wordComplexity/types.d.ts +13 -0
  33. package/dist/blockRunner/executor.d.ts +7 -0
  34. package/dist/blockRunner/executor.test.d.ts +1 -0
  35. package/dist/blockRunner/index.d.ts +12 -0
  36. package/dist/blockRunner/plugin.d.ts +14 -0
  37. package/dist/blockRunner/state.d.ts +4 -0
  38. package/dist/blockRunner/state.test.d.ts +1 -0
  39. package/dist/blockRunner/testHelpers.d.ts +17 -0
  40. package/dist/blockRunner/types.d.ts +162 -0
  41. package/dist/blockRunner/utils.d.ts +25 -0
  42. package/dist/blockRunner/utils.test.d.ts +1 -0
  43. package/dist/completeV2/actions.d.ts +30 -0
  44. package/dist/completeV2/index.d.ts +4 -0
  45. package/dist/completeV2/plugin.d.ts +7 -0
  46. package/dist/completeV2/streaming.d.ts +26 -0
  47. package/dist/completeV2/types.d.ts +71 -0
  48. package/dist/grammarSuggestV2/actions.d.ts +17 -0
  49. package/dist/grammarSuggestV2/decorations.d.ts +5 -0
  50. package/dist/grammarSuggestV2/index.d.ts +5 -0
  51. package/dist/grammarSuggestV2/modelState.d.ts +11 -0
  52. package/dist/grammarSuggestV2/plugin.d.ts +7 -0
  53. package/dist/grammarSuggestV2/processor.d.ts +11 -0
  54. package/dist/grammarSuggestV2/types.d.ts +42 -0
  55. package/dist/index.d.ts +9 -5
  56. package/dist/index.es.js +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/styles/styles.css +142 -43
  59. package/dist/types.d.ts +32 -8
  60. package/dist/utils.d.ts +3 -0
  61. package/package.json +16 -3
  62. package/dist/completePlugin.d.ts +0 -3
  63. package/dist/eventHandlers.d.ts +0 -9
  64. package/dist/makeRequest.d.ts +0 -4
  65. package/dist/makeTaksRequest.d.ts +0 -6
  66. package/dist/plugin.d.ts +0 -3
package/README.md CHANGED
@@ -8,21 +8,34 @@
8
8
 
9
9
  ![feature-gif](https://suggestcat.com/suggestcat.gif)
10
10
 
11
- - Adds AI features to your ProseMirror editor
12
- - YJS support
13
- - Text completion, rewriting content to make it shorter or longer.
11
+ - AI-powered grammar and style corrections for ProseMirror
12
+ - AI text completion, rewriting, translation, tone changes and more — with streaming
13
+ - Inline autocomplete with ghost text (like GitHub Copilot)
14
+ - Paragraph-level processing with dirty state tracking and parallel execution
15
+ - Multiple AI model support with automatic fallback
14
16
 
15
- ## How to use?
17
+ ## Getting started
16
18
 
17
- - Create your API_KEY on [SuggestCat](https://www.suggestcat.com/)
18
- - Add `grammarSuggestPlugin`
19
- - Add your api key
20
- - And `defaultOptions` which you can override
19
+ 1. Create an account on [SuggestCat](https://www.suggestcat.com/) and generate an API key
20
+ 2. Install the package
21
+
22
+ ```sh
23
+ npm i prosemirror-suggestcat-plugin
24
+ ```
25
+
26
+ 3. Add the plugins you need to your ProseMirror (or TipTap) editor using your API key
27
+ 4. Import the styles or write your own CSS
28
+ 5. Track your usage and manage API keys on your [admin dashboard](https://www.suggestcat.com/)
29
+
30
+ ## Grammar suggestion plugin
31
+
32
+ Checks your text for grammar and style issues paragraph by paragraph. Only edited paragraphs are re-checked, and multiple paragraphs are processed in parallel.
21
33
 
22
34
  ```typescript
23
35
  import {
24
- grammarSuggestPlugin,
25
- defaultOptions,
36
+ grammarSuggestPluginV2,
37
+ grammarSuggestV2Key,
38
+ ActionType,
26
39
  } from "prosemirror-suggestcat-plugin";
27
40
 
28
41
  const view = new EditorView(document.querySelector("#editor"), {
@@ -30,214 +43,334 @@ const view = new EditorView(document.querySelector("#editor"), {
30
43
  doc: schema.nodeFromJSON(initialDoc),
31
44
  plugins: [
32
45
  ...exampleSetup({ schema }),
33
- grammarSuggestPlugin(PROSEMIRROR_SUGGESTCAT_PLUGIN_API_KEY, {
34
- ...defaultOptions,
46
+ grammarSuggestPluginV2("<YOUR_API_KEY>", {
47
+ debounceMs: 1000,
48
+ batchSize: 4,
35
49
  }),
36
50
  ],
37
51
  }),
38
52
  });
53
+
54
+ // Initialize the grammar checker
55
+ view.dispatch(
56
+ view.state.tr.setMeta(grammarSuggestV2Key, {
57
+ type: ActionType.INIT,
58
+ metadata: {},
59
+ }),
60
+ );
39
61
  ```
40
62
 
41
63
  ### Options
42
64
 
43
- - `GrammarSuggestPluginOptions`
44
-
45
65
  ```typescript
46
- export type GrammarSuggestPluginOptions = {
47
- debounceMs: number;
48
- createUpdatePopup: (
66
+ interface GrammarSuggestV2Options {
67
+ apiKey: string;
68
+ apiEndpoint?: string;
69
+ model?: string | AIModel;
70
+ fallback?: {
71
+ fallbackModel: string | AIModel;
72
+ failureThreshold?: number; // default: 3
73
+ };
74
+ batchSize?: number; // parallel workers, default: 4
75
+ maxRetries?: number; // default: 3
76
+ backoffBase?: number; // default: 2000ms
77
+ debounceMs?: number; // default: 1000ms
78
+ createPopup?: (
49
79
  view: EditorView,
50
80
  decoration: Decoration,
51
81
  pos: number,
52
- applySuggestion: (view: EditorView, decoration: Decoration) => void,
53
- discardSuggestion: (view: EditorView, decoration: Decoration) => void,
82
+ applySuggestion: () => void,
83
+ discardSuggestion: () => void,
84
+ requestHint: () => Promise<string>,
54
85
  ) => HTMLElement;
55
- };
86
+ }
56
87
  ```
57
88
 
58
- - `defaultOptions` which you can import from our library
89
+ ### Styles
59
90
 
60
91
  ```typescript
61
- export const defaultOptions: GrammarSuggestPluginOptions = {
62
- debounceMs: 2000,
63
- createUpdatePopup,
64
- };
92
+ import "prosemirror-suggestcat-plugin/dist/styles/styles.css";
65
93
  ```
66
94
 
67
- ### Styles
95
+ Or style the decorations yourself using these CSS classes:
96
+
97
+ - `.grammarSuggestionV2` — inline decoration on suggestions
98
+ - `.removalSuggestionV2` — when the suggestion is a deletion
99
+ - `.grammarSuggestionV2-selected` — currently selected suggestion
100
+ - `.grammarPopupV2` — popup container
68
101
 
69
- - Add our css for the popup, or you can create your own using `createUpdatePopup` option
102
+ ## Complete plugin
103
+
104
+ AI text completion and transformation with streaming support. Use it to complete, shorten, lengthen, simplify, explain, translate text and more.
70
105
 
71
106
  ```typescript
72
- import "prosemirror-suggestcat-plugin/dist/styles/styles.css";
107
+ import { completePluginV2 } from "prosemirror-suggestcat-plugin";
108
+
109
+ const view = new EditorView(document.querySelector("#editor"), {
110
+ state: EditorState.create({
111
+ doc: schema.nodeFromJSON(initialDoc),
112
+ plugins: [
113
+ ...exampleSetup({ schema }),
114
+ completePluginV2("<YOUR_API_KEY>", {
115
+ maxSelection: 1000,
116
+ }),
117
+ ],
118
+ }),
119
+ });
73
120
  ```
74
121
 
75
- - Our popup structure:
122
+ ### Triggering tasks
76
123
 
77
- ```html
124
+ Use action functions instead of dispatching metas directly:
78
125
 
79
- <div classname="grammar-suggest-tooltip ProseMirror-widget">
80
- <div classname="grammar-suggest-tooltip-apply">
81
- suggestion
82
- </div
83
- <div classname="grammar-suggest-tooltip-discard">
84
- <svg/>
85
- </div
86
- </div
87
- ```
126
+ ```typescript
127
+ import {
128
+ startTask,
129
+ acceptResult,
130
+ rejectResult,
131
+ cancelTask,
132
+ getCompleteState,
133
+ AiPromptsWithoutParam,
134
+ AiPromptsWithParam,
135
+ MoodParamType,
136
+ TranslationTargetLanguage,
137
+ } from "prosemirror-suggestcat-plugin";
88
138
 
89
- - Style the editor decorations with the follwing classnames
139
+ // Continue writing from cursor
140
+ startTask(view, AiPromptsWithoutParam.Complete);
90
141
 
91
- ```css
92
- .grammarSuggestion {
93
- background-color: green;
94
- }
142
+ // Transform selected text
143
+ startTask(view, AiPromptsWithoutParam.MakeShorter);
144
+ startTask(view, AiPromptsWithoutParam.MakeLonger);
145
+ startTask(view, AiPromptsWithoutParam.Simplify);
146
+ startTask(view, AiPromptsWithoutParam.Explain);
147
+ startTask(view, AiPromptsWithoutParam.ActionItems);
148
+ startTask(view, AiPromptsWithoutParam.Improve);
95
149
 
96
- .grammarSuggestion .removalSuggestion {
97
- background-color: red;
98
- }
150
+ // Tasks with parameters
151
+ startTask(view, AiPromptsWithParam.ChangeTone, {
152
+ mood: MoodParamType.Friendly,
153
+ });
154
+ startTask(view, AiPromptsWithParam.Translate, {
155
+ targetLanguage: TranslationTargetLanguage.Spanish,
156
+ });
157
+
158
+ // Accept or reject the result once streaming finishes
159
+ acceptResult(view);
160
+ rejectResult(view);
161
+
162
+ // Cancel an in-progress task
163
+ cancelTask(view);
99
164
  ```
100
165
 
101
- ### AI feature to complete text, or make it longer/shorter
166
+ ### State flow
102
167
 
103
- - you can use another plugin from this package called `completePlugin`
104
- - with prosemirror meta calls you can transform your existing content, or generate more content based on your existing content
168
+ ```
169
+ IDLE -> PENDING -> STREAMING -> PREVIEW -> APPLYING -> IDLE
170
+ ```
105
171
 
106
- #### Usage
172
+ During `STREAMING` the result is built up incrementally. At `PREVIEW` you can accept or reject. Only one task runs at a time.
107
173
 
108
- - import the `completePlugin` and provide your API key, and optional options
174
+ ## Autocomplete plugin
109
175
 
110
- ```ts
111
- import {
112
- completePluginKey,
113
- completePlugin,
114
- defaultCompleteOptions,
115
- completePluginKey,
116
- } from "prosemirror-suggestcat-plugin";
176
+ Inline ghost-text completions that appear after the cursor as you type. Press **Tab** to accept, **Escape** to dismiss.
117
177
 
118
- const v = new EditorView(document.querySelector("#editor"), {
178
+ ```typescript
179
+ import { autoCompletePlugin } from "prosemirror-suggestcat-plugin";
180
+
181
+ const view = new EditorView(document.querySelector("#editor"), {
119
182
  state: EditorState.create({
120
183
  doc: schema.nodeFromJSON(initialDoc),
121
184
  plugins: [
122
185
  ...exampleSetup({ schema }),
123
- completePlugin(<YOUR_API_KEY>, defaultCompleteOptions),
186
+ autoCompletePlugin("<YOUR_API_KEY>", {
187
+ debounceMs: 500,
188
+ maxContextLength: 2000,
189
+ }),
124
190
  ],
125
191
  }),
126
192
  });
127
193
  ```
128
194
 
129
- - `DefaultCompleteOptions`:
130
- - `maxSelection` defaults to 1000 - can controll how long text will be sent to AI to transform it
195
+ ### Options
131
196
 
132
- ```ts
133
- export interface DefaultCompleteOptions {
134
- maxSelection: number;
197
+ ```typescript
198
+ interface AutoCompleteOptions {
199
+ debounceMs: number; // default: 500
200
+ maxContextLength: number; // default: 2000
201
+ apiEndpoint?: string;
202
+ model?: string;
203
+ ghostTextClass?: string; // default: "autoCompleteGhostText"
135
204
  }
136
205
  ```
137
206
 
138
- #### How it works?
207
+ ### Styles
139
208
 
140
- - the plugin's initial state is `{status: "idle"}`
141
- - send the plugin a task using `setMeta` using the `completePluginKey` plugin key
209
+ Add CSS for the ghost text:
142
210
 
143
- ```ts
144
- view.dispatch(
145
- view.state.tr.setMeta(completePluginKey, {
146
- type: "Complete",
147
- status: "new",
148
- }),
149
- );
211
+ ```css
212
+ .autoCompleteGhostText {
213
+ color: #9ca3af;
214
+ opacity: 0.7;
215
+ pointer-events: none;
216
+ }
150
217
  ```
151
218
 
152
- - pluginState will change to `{type: "Complete", status: "streaming", result: "some string being streamed...", }`
219
+ ### Programmatic control
153
220
 
154
- - when the AI finishes the pluginState's status changes to `{status: "finished"}`
155
- - at this point you can either accept or reject the completion
221
+ ```typescript
222
+ import {
223
+ setAutoCompleteEnabled,
224
+ acceptAutoCompletion,
225
+ dismissAutoCompletion,
226
+ isAutoCompleteEnabled,
227
+ hasAutoCompletion,
228
+ } from "prosemirror-suggestcat-plugin";
156
229
 
157
- ```ts
158
- view.dispatch(
159
- view.state.tr.setMeta(completePluginKey, {
160
- type: "Complete",
161
- status: "accpeted",
162
- }),
163
- );
164
- ```
230
+ setAutoCompleteEnabled(view, true);
231
+ setAutoCompleteEnabled(view, false);
165
232
 
166
- - after accepting it, your completion will be placed at the end of your document and the pluginState changes to `{status: "idle"}`waiting for a new task
167
- - only one task can be ran at once
168
- - only pluginState with `{status: "idle"}` can handle a new task
169
- - if pluginState has an error like `{status: "error", error: "selection is too big"}` you can clear the error dispatching an `accepted` meta like above
170
- - the plugin takes care of replacing existing text, or appending the completion result to the end of your document
171
- - `MakeLonger/MakeShorter` - requires a selection, which content to make shorter or longer
172
-
173
- ```ts
174
- export enum TaskType {
175
- Complete = "Complete",
176
- Improve = "Improve",
177
- MakeLonger = "MakeLonger",
178
- MakeShorter = "MakeShorter",
179
- Simplify = "Simplify",
180
- Explain = "Explain",
181
- ActionItems = "ActionItems",
182
- }
183
- export enum OpenAiPromptsWithParam {
184
- ChangeTone = "ChangeTone",
185
- Translate = "Translate",
233
+ if (hasAutoCompletion(view)) {
234
+ acceptAutoCompletion(view);
235
+ // or
236
+ dismissAutoCompletion(view);
186
237
  }
238
+ ```
187
239
 
188
- export enum Status {
189
- idle = "idle",
190
- new = "new",
191
- streaming = "streaming",
192
- finished = "finished",
193
- accepted = "accepted",
194
- rejected = "rejected",
195
- done = "done",
196
- error = "error",
197
- }
240
+ ## How it works
198
241
 
199
- export interface CompletePluginState {
200
- type?: TaskType;
201
- status?: Status;
202
- result?: string;
203
- selection?: TextSelection;
204
- error?: string;
205
- }
242
+ ### API
206
243
 
207
- export interface TaskMeta {
208
- type: TaskType;
209
- status: Status.new | Status.accepted | Status.rejected;
210
- }
244
+ All plugins communicate with the SuggestCat backend through a shared API module. You can configure the endpoint and model per plugin, or use the defaults (`openai:gpt-4o-mini`).
245
+
246
+ The module exposes two request functions if you need to use them directly:
247
+
248
+ - `grammarRequest(options)` — a non-streaming POST that returns the corrected text and a list of modifications
249
+ - `streamingRequest(options, callbacks)` — a streaming request with `onChunk`, `onComplete` and `onError` callbacks, used by the complete and autocomplete plugins. Supports cancellation via `AbortSignal`.
250
+
251
+ Both accept an `ApiConfig` (`apiKey`, optional `endpoint`, optional `model`). You can use `createApiConfig` or `createGrammarApiConfig` to fill in defaults.
252
+
253
+ ```typescript
254
+ import {
255
+ streamingRequest,
256
+ grammarRequest,
257
+ createApiConfig,
258
+ createGrammarApiConfig,
259
+ } from "prosemirror-suggestcat-plugin";
211
260
  ```
212
261
 
213
- - Example for completion
214
-
215
- ```ts
216
- const getStuff = useCallback(() => {
217
- if (!view) {
218
- return;
219
- }
220
-
221
- view.dispatch(
222
- view.state.tr.setMeta(completePluginKey, {
223
- type: "Complete",
224
- status: "new",
225
- }),
226
- );
227
- }, [view]);
228
-
229
- const completeStuff = useCallback(() => {
230
- if (!view) {
231
- return;
232
- }
233
- const state = completePluginKey.getState(view.state);
234
-
235
- if (state?.status === "finished")
236
- view.dispatch(
237
- view.state.tr.setMeta(completePluginKey, {
238
- type: "Complete",
239
- status: "accepted",
240
- }),
241
- );
242
- }, [view]);
262
+ ### Block runner
263
+
264
+ The grammar suggestion plugin is built on top of a generic **block runner** framework. Instead of sending the entire document to the API, it splits the document into processing units (paragraphs by default) and processes them in parallel.
265
+
266
+ Key properties:
267
+
268
+ - **Paragraph-level processing** — each paragraph is an independent unit, so large documents don't result in oversized API calls
269
+ - **Dirty state tracking** — when a paragraph is edited, only that unit is marked dirty and re-processed after a debounce delay. The plugin skips dirty marking for its own document changes (e.g. applying a suggestion)
270
+ - **Parallel execution** — multiple units are processed concurrently, controlled by `batchSize`. A unit goes through `QUEUED → PROCESSING → DONE` (or `BACKOFF → retry` on failure)
271
+ - **Retry with backoff** — failed units are retried up to `maxRetries` times with exponential backoff
272
+
273
+ The block runner is also exported as a standalone building block. You can use it to build your own paragraph-level processing plugins. The package includes a few example plugins (link detector, word complexity, sentence length) that demonstrate how to wire up a custom processor.
274
+
275
+ ```typescript
276
+ import {
277
+ blockRunnerPlugin,
278
+ createBlockRunnerKey,
279
+ ActionType,
280
+ dispatchAction,
281
+ } from "prosemirror-suggestcat-plugin";
282
+ ```
283
+
284
+ To create a block runner plugin, provide a `pluginKey`, a `unitProcessor` (async function that receives a processing unit and returns a result), a `decorationFactory` (turns results into ProseMirror decorations), and optionally a `widgetFactory` (shows per-unit status indicators) and a `decorationTransformer` (filters/modifies decorations based on context state).
285
+
286
+ #### Helper functions
287
+
288
+ The block runner exports utility functions you can use in your processor or elsewhere:
289
+
290
+ - `extractTextWithMapping(doc, from, to)` — extracts text from a document range and builds a position mapping between text offsets and doc positions
291
+ - `textToDocPos(textPos, mapping)` — converts a text-space position back to a document position using the mapping
292
+ - `getUnitsInRange(doc, from, to, nodeTypes?)` — finds all matching nodes in a range
293
+ - `createUnitsFromDocument(doc, from, to, metadataFactory, nodeTypes?)` — creates processing units from document nodes
294
+ - `getProgress(state)` — returns `{ completed, total, decorations }` for progress tracking
295
+ - `calculateBackoff(retryCount, baseMs)` — computes the backoff delay for retries
296
+ - `allUnitsFinished(units)` — checks if all units are in a terminal state (DONE or ERROR)
297
+
298
+ #### Manual transactions
299
+
300
+ You can control the block runner by dispatching actions via `dispatchAction(view, pluginKey, action)`:
301
+
302
+ ```typescript
303
+ import { dispatchAction, ActionType } from "prosemirror-suggestcat-plugin";
304
+
305
+ // Initialize — creates units from the document and starts processing
306
+ dispatchAction(view, myPluginKey, {
307
+ type: ActionType.INIT,
308
+ metadata: {},
309
+ });
310
+
311
+ // Pause processing
312
+ dispatchAction(view, myPluginKey, { type: ActionType.FINISH });
313
+
314
+ // Resume paused processing
315
+ dispatchAction(view, myPluginKey, { type: ActionType.RESUME });
316
+
317
+ // Clear all state
318
+ dispatchAction(view, myPluginKey, { type: ActionType.CLEAR });
319
+
320
+ // Remove a specific decoration
321
+ dispatchAction(view, myPluginKey, {
322
+ type: ActionType.REMOVE_DECORATION,
323
+ id: decorationId,
324
+ });
325
+
326
+ // Update context state (e.g. for filtering or selection)
327
+ dispatchAction(view, myPluginKey, {
328
+ type: ActionType.UPDATE_CONTEXT,
329
+ contextState: { selectedSuggestionId: someId },
330
+ });
243
331
  ```
332
+
333
+ There are also convenience wrappers: `pauseRunner(view, pluginKey)`, `resumeRunner(view, pluginKey)`, and `canResume(state)`.
334
+
335
+ ## Available models
336
+
337
+ All plugins accept a `model` option:
338
+
339
+ ```typescript
340
+ type AIModel =
341
+ | "openai:gpt-4o"
342
+ | "openai:gpt-4o-mini" // default
343
+ | "cerebras:llama-3.1-8b"
344
+ | "cerebras:llama-3.3-70b"
345
+ | "cerebras:qwen-3-32b";
346
+ ```
347
+
348
+ ## TipTap
349
+
350
+ All plugins work with TipTap by wrapping them in an extension:
351
+
352
+ ```typescript
353
+ import { Extension } from "@tiptap/core";
354
+ import {
355
+ grammarSuggestPluginV2,
356
+ grammarSuggestV2Key,
357
+ ActionType,
358
+ completePluginV2,
359
+ autoCompletePlugin,
360
+ } from "prosemirror-suggestcat-plugin";
361
+
362
+ const SuggestCatExtension = Extension.create({
363
+ name: "suggestcat",
364
+ addProseMirrorPlugins() {
365
+ return [
366
+ grammarSuggestPluginV2("<YOUR_API_KEY>"),
367
+ completePluginV2("<YOUR_API_KEY>"),
368
+ autoCompletePlugin("<YOUR_API_KEY>"),
369
+ ];
370
+ },
371
+ });
372
+ ```
373
+
374
+ ## React UI
375
+
376
+ For a ready-made React UI with a slash menu, suggestion overlay and "Ask AI" tooltip, see [prosemirror-suggestcat-plugin-react](https://github.com/emergence-engineering/prosemirror-suggestcat-plugin-react).
@@ -0,0 +1,7 @@
1
+ import type { ApiConfig } from "./types";
2
+ export declare const DEFAULT_COMPLETION_ENDPOINT = "https://suggestion-gw5lxik4dq-uc.a.run.app";
3
+ export declare const DEFAULT_GRAMMAR_ENDPOINT = "https://prosemirror-ai-plugin.web.app/api/suggestion";
4
+ export declare const DEFAULT_MODEL = "openai:gpt-4o-mini";
5
+ export type AIModel = "openai:gpt-4o" | "openai:gpt-4o-mini" | "cerebras:llama-3.1-8b" | "cerebras:llama-3.3-70b" | "cerebras:qwen-3-32b";
6
+ export declare function createApiConfig(config: ApiConfig): Required<ApiConfig>;
7
+ export declare function createGrammarApiConfig(config: ApiConfig): Required<ApiConfig>;
@@ -0,0 +1,4 @@
1
+ export { DEFAULT_COMPLETION_ENDPOINT, DEFAULT_GRAMMAR_ENDPOINT, DEFAULT_MODEL, createApiConfig, createGrammarApiConfig, } from "./config";
2
+ export type { ApiConfig, ApiRequestBody, StreamingCallbacks, GrammarApiResponse, } from "./types";
3
+ export { streamingRequest, type StreamingRequestOptions } from "./streaming";
4
+ export { grammarRequest, type GrammarRequestOptions, type GrammarRequestResult, } from "./request";
@@ -0,0 +1,16 @@
1
+ export interface GrammarRequestOptions {
2
+ apiKey: string;
3
+ text: string;
4
+ endpoint?: string;
5
+ model?: string;
6
+ }
7
+ export interface GrammarRequestResult {
8
+ result: string;
9
+ fixed: boolean;
10
+ error?: boolean;
11
+ }
12
+ /**
13
+ * Make a non-streaming request to the grammar API.
14
+ * Returns the corrected text.
15
+ */
16
+ export declare function grammarRequest(options: GrammarRequestOptions): Promise<GrammarRequestResult>;
@@ -0,0 +1,16 @@
1
+ import type { TaskType, TaskParams } from "../types";
2
+ import type { StreamingCallbacks } from "./types";
3
+ export interface StreamingRequestOptions {
4
+ apiKey: string;
5
+ text: string;
6
+ task: TaskType;
7
+ params?: TaskParams;
8
+ endpoint?: string;
9
+ model?: string;
10
+ signal?: AbortSignal;
11
+ }
12
+ /**
13
+ * Make a streaming request to the AI API.
14
+ * Returns a promise that resolves when the stream completes.
15
+ */
16
+ export declare function streamingRequest(options: StreamingRequestOptions, callbacks: StreamingCallbacks): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import type { TaskType, TaskParams } from "../types";
2
+ import { AIModel } from "./config";
3
+ export interface ApiConfig {
4
+ apiKey: string;
5
+ endpoint?: string;
6
+ model?: string | AIModel;
7
+ }
8
+ export interface ApiRequestBody {
9
+ model: string;
10
+ modelParams: {
11
+ input: string[];
12
+ task?: TaskType;
13
+ params?: TaskParams;
14
+ };
15
+ }
16
+ export interface StreamingCallbacks {
17
+ onChunk: (chunk: string, accumulated: string) => void;
18
+ onComplete: (result: string) => void;
19
+ onError: (error: Error) => void;
20
+ }
21
+ export interface GrammarApiResponse {
22
+ result: string;
23
+ fixed: boolean;
24
+ mod?: {
25
+ original: string;
26
+ fixed: string;
27
+ position: number;
28
+ type: string;
29
+ }[];
30
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Public action functions for the auto-complete plugin
3
+ *
4
+ * These functions provide a clean API for controlling the plugin
5
+ * from outside (e.g., from React components or other plugins).
6
+ */
7
+ import { EditorView } from "prosemirror-view";
8
+ import { AutoCompleteState } from "./types";
9
+ /**
10
+ * Enable or disable the auto-complete plugin
11
+ */
12
+ export declare function setAutoCompleteEnabled(view: EditorView, enabled: boolean): void;
13
+ /**
14
+ * Accept the current completion (if showing)
15
+ * This is typically triggered by pressing Tab, but can be called programmatically
16
+ */
17
+ export declare function acceptAutoCompletion(view: EditorView): boolean;
18
+ /**
19
+ * Dismiss the current completion without accepting it
20
+ */
21
+ export declare function dismissAutoCompletion(view: EditorView): void;
22
+ /**
23
+ * Get the current auto-complete plugin state
24
+ */
25
+ export declare function getAutoCompleteState(view: EditorView): AutoCompleteState | undefined;
26
+ /**
27
+ * Check if auto-complete is currently enabled
28
+ */
29
+ export declare function isAutoCompleteEnabled(view: EditorView): boolean;
30
+ /**
31
+ * Check if a completion is currently being shown
32
+ */
33
+ export declare function hasAutoCompletion(view: EditorView): boolean;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Auto-complete plugin module
3
+ *
4
+ * Shows AI-powered inline completion suggestions as ghost text
5
+ * after the user stops typing.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { autoCompletePlugin, setAutoCompleteEnabled } from 'prosemirror-suggestcat-plugin';
10
+ *
11
+ * const plugins = [
12
+ * autoCompletePlugin('your-api-key', {
13
+ * debounceMs: 500,
14
+ * maxContextLength: 2000,
15
+ * }),
16
+ * // ... other plugins
17
+ * ];
18
+ *
19
+ * // To toggle the plugin:
20
+ * setAutoCompleteEnabled(view, false);
21
+ * ```
22
+ *
23
+ * CSS for ghost text (user responsibility):
24
+ * ```css
25
+ * .autoCompleteGhostText {
26
+ * color: #9ca3af;
27
+ * opacity: 0.7;
28
+ * pointer-events: none;
29
+ * }
30
+ * ```
31
+ */
32
+ export { AutoCompleteStatus, AutoCompleteActionType, type AutoCompleteState, type AutoCompleteAction, type AutoCompleteOptions, type AutoCompleteSetEnabledAction, type AutoCompleteStartDebounceAction, type AutoCompleteStartRequestAction, type AutoCompleteStreamUpdateAction, type AutoCompleteStreamCompleteAction, type AutoCompleteStreamErrorAction, type AutoCompleteDismissAction, type AutoCompleteAcceptAction, defaultAutoCompleteOptions, } from "./types";
33
+ export { autoCompletePlugin, autoCompleteKey } from "./plugin";
34
+ export { setAutoCompleteEnabled, acceptAutoCompletion, dismissAutoCompletion, getAutoCompleteState, isAutoCompleteEnabled, hasAutoCompletion, } from "./actions";
35
+ export { cancelActiveRequest as cancelAutoCompleteRequest, hasActiveRequest as hasAutoCompleteRequest, } from "./streaming";