prosemirror-suggestcat-plugin 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -299
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/playwright.config.d.ts +3 -0
- package/dist/playwright.config.d.ts.map +1 -0
- package/dist/{api → src/api}/config.d.ts +1 -0
- package/dist/src/api/config.d.ts.map +1 -0
- package/dist/{api → src/api}/index.d.ts +1 -0
- package/dist/src/api/index.d.ts.map +1 -0
- package/dist/{api → src/api}/request.d.ts +1 -0
- package/dist/src/api/request.d.ts.map +1 -0
- package/dist/{api → src/api}/streaming.d.ts +1 -0
- package/dist/src/api/streaming.d.ts.map +1 -0
- package/dist/{api → src/api}/types.d.ts +1 -0
- package/dist/src/api/types.d.ts.map +1 -0
- package/dist/{autoComplete → src/autoComplete}/actions.d.ts +1 -0
- package/dist/src/autoComplete/actions.d.ts.map +1 -0
- package/dist/{autoComplete → src/autoComplete}/index.d.ts +1 -0
- package/dist/src/autoComplete/index.d.ts.map +1 -0
- package/dist/{autoComplete → src/autoComplete}/plugin.d.ts +1 -0
- package/dist/src/autoComplete/plugin.d.ts.map +1 -0
- package/dist/{autoComplete → src/autoComplete}/streaming.d.ts +1 -0
- package/dist/src/autoComplete/streaming.d.ts.map +1 -0
- package/dist/{autoComplete → src/autoComplete}/types.d.ts +5 -4
- package/dist/src/autoComplete/types.d.ts.map +1 -0
- package/dist/{completeV2 → src/completeV2}/actions.d.ts +1 -0
- package/dist/src/completeV2/actions.d.ts.map +1 -0
- package/dist/{completeV2 → src/completeV2}/index.d.ts +1 -0
- package/dist/src/completeV2/index.d.ts.map +1 -0
- package/dist/{completeV2 → src/completeV2}/plugin.d.ts +1 -0
- package/dist/src/completeV2/plugin.d.ts.map +1 -0
- package/dist/{completeV2 → src/completeV2}/streaming.d.ts +1 -0
- package/dist/src/completeV2/streaming.d.ts.map +1 -0
- package/dist/{completeV2 → src/completeV2}/types.d.ts +1 -0
- package/dist/src/completeV2/types.d.ts.map +1 -0
- package/dist/{createUpdatePopup.d.ts → src/createUpdatePopup.d.ts} +1 -0
- package/dist/src/createUpdatePopup.d.ts.map +1 -0
- package/dist/{defaults.d.ts → src/defaults.d.ts} +1 -0
- package/dist/src/defaults.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/actions.d.ts +2 -1
- package/dist/src/grammarSuggestV2/actions.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/decorations.d.ts +2 -1
- package/dist/src/grammarSuggestV2/decorations.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/index.d.ts +1 -0
- package/dist/src/grammarSuggestV2/index.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/modelState.d.ts +1 -0
- package/dist/src/grammarSuggestV2/modelState.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/plugin.d.ts +2 -1
- package/dist/src/grammarSuggestV2/plugin.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/processor.d.ts +2 -1
- package/dist/src/grammarSuggestV2/processor.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/types.d.ts +1 -0
- package/dist/src/grammarSuggestV2/types.d.ts.map +1 -0
- package/dist/{index.d.ts → src/index.d.ts} +1 -1
- package/dist/src/index.d.ts.map +1 -0
- package/dist/{types.d.ts → src/types.d.ts} +1 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/{utils.d.ts → src/utils.d.ts} +1 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.test.d.ts +2 -0
- package/dist/src/utils.test.d.ts.map +1 -0
- package/package.json +37 -33
- package/dist/blockRunner/defaults.d.ts +0 -3
- package/dist/blockRunner/examples/linkDetector/decorations.d.ts +0 -3
- package/dist/blockRunner/examples/linkDetector/index.d.ts +0 -4
- package/dist/blockRunner/examples/linkDetector/plugin.d.ts +0 -5
- package/dist/blockRunner/examples/linkDetector/processor.d.ts +0 -4
- package/dist/blockRunner/examples/linkDetector/types.d.ts +0 -8
- package/dist/blockRunner/examples/randomProcessor/decorations.d.ts +0 -4
- package/dist/blockRunner/examples/randomProcessor/index.d.ts +0 -4
- package/dist/blockRunner/examples/randomProcessor/plugin.d.ts +0 -5
- package/dist/blockRunner/examples/randomProcessor/processor.d.ts +0 -5
- package/dist/blockRunner/examples/randomProcessor/types.d.ts +0 -14
- package/dist/blockRunner/examples/sentenceLength/decorations.d.ts +0 -3
- package/dist/blockRunner/examples/sentenceLength/index.d.ts +0 -4
- package/dist/blockRunner/examples/sentenceLength/plugin.d.ts +0 -5
- package/dist/blockRunner/examples/sentenceLength/processor.d.ts +0 -5
- package/dist/blockRunner/examples/sentenceLength/types.d.ts +0 -12
- package/dist/blockRunner/examples/wordComplexity/decorations.d.ts +0 -3
- package/dist/blockRunner/examples/wordComplexity/index.d.ts +0 -4
- package/dist/blockRunner/examples/wordComplexity/plugin.d.ts +0 -5
- package/dist/blockRunner/examples/wordComplexity/processor.d.ts +0 -6
- package/dist/blockRunner/examples/wordComplexity/types.d.ts +0 -13
- package/dist/blockRunner/executor.d.ts +0 -7
- package/dist/blockRunner/executor.test.d.ts +0 -1
- package/dist/blockRunner/index.d.ts +0 -12
- package/dist/blockRunner/plugin.d.ts +0 -14
- package/dist/blockRunner/state.d.ts +0 -4
- package/dist/blockRunner/state.test.d.ts +0 -1
- package/dist/blockRunner/testHelpers.d.ts +0 -17
- package/dist/blockRunner/types.d.ts +0 -162
- package/dist/blockRunner/utils.d.ts +0 -25
- package/dist/blockRunner/utils.test.d.ts +0 -1
- package/dist/utils.test.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,52 +1,53 @@
|
|
|
1
1
|
# prosemirror-suggestcat-plugin
|
|
2
2
|
|
|
3
|
-
[](https://emergence-engineering.com)
|
|
4
4
|
|
|
5
|
-
[**Made by Emergence
|
|
5
|
+
[**Made by Emergence Engineering**](https://emergence-engineering.com/)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> AI-powered grammar checking, text completion, and inline autocomplete for ProseMirror
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Features
|
|
10
10
|
|
|
11
|
-
- AI
|
|
12
|
-
- AI text completion, rewriting, translation, tone changes and more
|
|
11
|
+
- AI grammar and style corrections with paragraph-level processing and parallel execution
|
|
12
|
+
- AI text completion, rewriting, translation, tone changes, and more with streaming
|
|
13
13
|
- Inline autocomplete with ghost text (like GitHub Copilot)
|
|
14
|
-
- Paragraph-level processing with dirty state tracking and parallel execution
|
|
15
14
|
- Multiple AI model support with automatic fallback
|
|
15
|
+
- Dirty-state tracking -- only edited paragraphs are re-checked
|
|
16
|
+
- Works with ProseMirror and TipTap
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## Installation
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```sh
|
|
23
|
-
npm i prosemirror-suggestcat-plugin
|
|
20
|
+
```bash
|
|
21
|
+
npm install prosemirror-suggestcat-plugin
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
### Peer dependencies
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install prosemirror-model prosemirror-state prosemirror-transform prosemirror-view
|
|
28
|
+
```
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Quick Start
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Create an account on [SuggestCat](https://www.suggestcat.com/) and generate an API key, then register the plugins you need:
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
+
import { EditorState } from "prosemirror-state";
|
|
36
|
+
import { EditorView } from "prosemirror-view";
|
|
35
37
|
import {
|
|
36
38
|
grammarSuggestPluginV2,
|
|
37
39
|
grammarSuggestV2Key,
|
|
38
|
-
|
|
40
|
+
completePluginV2,
|
|
41
|
+
autoCompletePlugin,
|
|
39
42
|
} from "prosemirror-suggestcat-plugin";
|
|
43
|
+
import { ActionType } from "@emergence-engineering/prosemirror-block-runner";
|
|
40
44
|
|
|
41
45
|
const view = new EditorView(document.querySelector("#editor"), {
|
|
42
46
|
state: EditorState.create({
|
|
43
|
-
doc: schema.nodeFromJSON(initialDoc),
|
|
44
47
|
plugins: [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
batchSize: 4,
|
|
49
|
-
}),
|
|
48
|
+
grammarSuggestPluginV2("<YOUR_API_KEY>"),
|
|
49
|
+
completePluginV2("<YOUR_API_KEY>"),
|
|
50
|
+
autoCompletePlugin("<YOUR_API_KEY>"),
|
|
50
51
|
],
|
|
51
52
|
}),
|
|
52
53
|
});
|
|
@@ -60,153 +61,109 @@ view.dispatch(
|
|
|
60
61
|
);
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
### GrammarSuggestV2Options
|
|
67
|
+
|
|
68
|
+
| Option | Type | Default | Description |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| `apiKey` | `string` | -- | SuggestCat API key (required, passed as first argument) |
|
|
71
|
+
| `apiEndpoint` | `string` | SuggestCat API | Custom endpoint URL |
|
|
72
|
+
| `model` | `string \| AIModel` | `"openai:gpt-4o-mini"` | AI model to use |
|
|
73
|
+
| `fallback` | `ModelFallbackConfig` | -- | Fallback model config (`{ fallbackModel, failureThreshold? }`) |
|
|
74
|
+
| `batchSize` | `number` | `2` | Number of parallel workers |
|
|
75
|
+
| `maxRetries` | `number` | `3` | Max retries per paragraph |
|
|
76
|
+
| `backoffBase` | `number` | `2000` | Base backoff delay in ms |
|
|
77
|
+
| `debounceMs` | `number` | `1000` | Debounce delay before re-checking |
|
|
78
|
+
| `createPopup` | `"react" \| function` | -- | `"react"` for React popups, or a factory function returning an HTMLElement |
|
|
79
|
+
|
|
80
|
+
### CompleteV2Options
|
|
81
|
+
|
|
82
|
+
| Option | Type | Default | Description |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| `maxSelection` | `number` | `1000` | Max selected characters for a task |
|
|
85
|
+
| `apiEndpoint` | `string` | SuggestCat API | Custom endpoint URL |
|
|
86
|
+
| `model` | `string` | `"openai:gpt-4o-mini"` | AI model to use |
|
|
87
|
+
|
|
88
|
+
### AutoCompleteOptions
|
|
89
|
+
|
|
90
|
+
| Option | Type | Default | Description |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `debounceMs` | `number` | `500` | Debounce delay before requesting a suggestion |
|
|
93
|
+
| `maxContextLength` | `number` | `2000` | Max characters sent as context |
|
|
94
|
+
| `apiEndpoint` | `string` | SuggestCat API | Custom endpoint URL |
|
|
95
|
+
| `model` | `string` | `"openai:gpt-4o-mini"` | AI model to use |
|
|
96
|
+
| `ghostTextClass` | `string` | `"autoCompleteGhostText"` | CSS class for ghost text decoration |
|
|
97
|
+
|
|
98
|
+
## API
|
|
99
|
+
|
|
100
|
+
### Grammar plugin
|
|
101
|
+
|
|
102
|
+
| Export | Type | Description |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `grammarSuggestPluginV2(apiKey, options?)` | Plugin factory | Creates the grammar checking plugin |
|
|
105
|
+
| `grammarSuggestV2Key` | `PluginKey` | Plugin key for dispatching metas |
|
|
106
|
+
| `acceptSuggestion(view, id)` | function | Accept a grammar suggestion |
|
|
107
|
+
| `discardSuggestion(view, id)` | function | Discard a grammar suggestion |
|
|
108
|
+
| `selectSuggestion(view, id)` | function | Select a suggestion (for popup) |
|
|
109
|
+
| `deselectSuggestion(view)` | function | Deselect the current suggestion |
|
|
110
|
+
| `requestHint(view, id)` | function | Request an AI explanation for a suggestion |
|
|
111
|
+
| `getSelectedDecoration(state)` | function | Get the currently selected decoration |
|
|
112
|
+
| `setGrammarSuggestEnabled(view, enabled)` | function | Enable/disable grammar checking |
|
|
113
|
+
|
|
114
|
+
### Complete plugin
|
|
115
|
+
|
|
116
|
+
| Export | Type | Description |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `completePluginV2(apiKey, options?)` | Plugin factory | Creates the completion plugin |
|
|
119
|
+
| `completeV2Key` | `PluginKey` | Plugin key |
|
|
120
|
+
| `startTask(view, taskType, params?)` | function | Start an AI task |
|
|
121
|
+
| `acceptResult(view)` | function | Accept the streamed result |
|
|
122
|
+
| `rejectResult(view)` | function | Reject the streamed result |
|
|
123
|
+
| `cancelTask(view)` | function | Cancel an in-progress task |
|
|
124
|
+
| `getCompleteState(state)` | function | Get the current plugin state |
|
|
125
|
+
| `setEnabled(view, enabled)` | function | Enable/disable the plugin |
|
|
126
|
+
| `AiPromptsWithoutParam` | enum | `Complete`, `SmallComplete`, `Improve`, `MakeLonger`, `MakeShorter`, `Simplify`, `Explain`, `ActionItems` |
|
|
127
|
+
| `AiPromptsWithParam` | enum | `ChangeTone`, `Translate`, `Hint`, `Custom` |
|
|
128
|
+
| `MoodParamType` | enum | `Casual`, `Confident`, `Straightforward`, `Friendly` |
|
|
129
|
+
| `TranslationTargetLanguage` | enum | `English`, `Spanish`, `French`, `German`, `Italian`, `Portuguese`, `Dutch`, `Russian`, `Chinese`, `Korean`, `Japanese` |
|
|
130
|
+
|
|
131
|
+
### Autocomplete plugin
|
|
132
|
+
|
|
133
|
+
| Export | Type | Description |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `autoCompletePlugin(apiKey, options?)` | Plugin factory | Creates the autocomplete plugin |
|
|
136
|
+
| `autoCompleteKey` | `PluginKey` | Plugin key |
|
|
137
|
+
| `setAutoCompleteEnabled(view, enabled)` | function | Enable/disable autocomplete |
|
|
138
|
+
| `acceptAutoCompletion(view)` | function | Accept the ghost text |
|
|
139
|
+
| `dismissAutoCompletion(view)` | function | Dismiss the ghost text |
|
|
140
|
+
| `isAutoCompleteEnabled(view)` | function | Check if autocomplete is enabled |
|
|
141
|
+
| `hasAutoCompletion(view)` | function | Check if a suggestion is showing |
|
|
142
|
+
|
|
143
|
+
### Low-level API
|
|
144
|
+
|
|
145
|
+
| Export | Type | Description |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `streamingRequest(options, callbacks)` | function | Streaming POST with `onChunk`, `onComplete`, `onError` |
|
|
148
|
+
| `grammarRequest(options)` | function | Non-streaming POST for grammar corrections |
|
|
149
|
+
| `createApiConfig(apiKey, endpoint?, model?)` | function | Build an API config object |
|
|
150
|
+
| `createGrammarApiConfig(apiKey, endpoint?, model?)` | function | Build a grammar-specific API config |
|
|
151
|
+
| `getDiff(oldText, newText)` | function | Re-exported from `@emergence-engineering/fast-diff-merge` |
|
|
152
|
+
|
|
153
|
+
## Styles
|
|
90
154
|
|
|
91
155
|
```typescript
|
|
92
156
|
import "prosemirror-suggestcat-plugin/dist/styles/styles.css";
|
|
93
157
|
```
|
|
94
158
|
|
|
95
|
-
|
|
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
|
|
159
|
+
CSS classes for grammar decorations:
|
|
101
160
|
|
|
102
|
-
|
|
161
|
+
- `.grammarSuggestionV2` -- inline decoration on suggestions
|
|
162
|
+
- `.removalSuggestionV2` -- when the suggestion is a deletion
|
|
163
|
+
- `.grammarSuggestionV2-selected` -- currently selected suggestion
|
|
164
|
+
- `.grammarPopupV2` -- popup container
|
|
103
165
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
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
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Triggering tasks
|
|
123
|
-
|
|
124
|
-
Use action functions instead of dispatching metas directly:
|
|
125
|
-
|
|
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";
|
|
138
|
-
|
|
139
|
-
// Continue writing from cursor
|
|
140
|
-
startTask(view, AiPromptsWithoutParam.Complete);
|
|
141
|
-
|
|
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);
|
|
149
|
-
|
|
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);
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### State flow
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
IDLE -> PENDING -> STREAMING -> PREVIEW -> APPLYING -> IDLE
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
During `STREAMING` the result is built up incrementally. At `PREVIEW` you can accept or reject. Only one task runs at a time.
|
|
173
|
-
|
|
174
|
-
## Autocomplete plugin
|
|
175
|
-
|
|
176
|
-
Inline ghost-text completions that appear after the cursor as you type. Press **Tab** to accept, **Escape** to dismiss.
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
import { autoCompletePlugin } from "prosemirror-suggestcat-plugin";
|
|
180
|
-
|
|
181
|
-
const view = new EditorView(document.querySelector("#editor"), {
|
|
182
|
-
state: EditorState.create({
|
|
183
|
-
doc: schema.nodeFromJSON(initialDoc),
|
|
184
|
-
plugins: [
|
|
185
|
-
...exampleSetup({ schema }),
|
|
186
|
-
autoCompletePlugin("<YOUR_API_KEY>", {
|
|
187
|
-
debounceMs: 500,
|
|
188
|
-
maxContextLength: 2000,
|
|
189
|
-
}),
|
|
190
|
-
],
|
|
191
|
-
}),
|
|
192
|
-
});
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Options
|
|
196
|
-
|
|
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"
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Styles
|
|
208
|
-
|
|
209
|
-
Add CSS for the ghost text:
|
|
166
|
+
CSS for autocomplete ghost text (add your own):
|
|
210
167
|
|
|
211
168
|
```css
|
|
212
169
|
.autoCompleteGhostText {
|
|
@@ -216,145 +173,14 @@ Add CSS for the ghost text:
|
|
|
216
173
|
}
|
|
217
174
|
```
|
|
218
175
|
|
|
219
|
-
### Programmatic control
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
import {
|
|
223
|
-
setAutoCompleteEnabled,
|
|
224
|
-
acceptAutoCompletion,
|
|
225
|
-
dismissAutoCompletion,
|
|
226
|
-
isAutoCompleteEnabled,
|
|
227
|
-
hasAutoCompletion,
|
|
228
|
-
} from "prosemirror-suggestcat-plugin";
|
|
229
|
-
|
|
230
|
-
setAutoCompleteEnabled(view, true);
|
|
231
|
-
setAutoCompleteEnabled(view, false);
|
|
232
|
-
|
|
233
|
-
if (hasAutoCompletion(view)) {
|
|
234
|
-
acceptAutoCompletion(view);
|
|
235
|
-
// or
|
|
236
|
-
dismissAutoCompletion(view);
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## How it works
|
|
241
|
-
|
|
242
|
-
### API
|
|
243
|
-
|
|
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";
|
|
260
|
-
```
|
|
261
|
-
|
|
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
|
-
});
|
|
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
176
|
## TipTap
|
|
349
177
|
|
|
350
|
-
|
|
178
|
+
Wrap the plugins in a TipTap extension:
|
|
351
179
|
|
|
352
180
|
```typescript
|
|
353
181
|
import { Extension } from "@tiptap/core";
|
|
354
182
|
import {
|
|
355
183
|
grammarSuggestPluginV2,
|
|
356
|
-
grammarSuggestV2Key,
|
|
357
|
-
ActionType,
|
|
358
184
|
completePluginV2,
|
|
359
185
|
autoCompletePlugin,
|
|
360
186
|
} from "prosemirror-suggestcat-plugin";
|
|
@@ -371,6 +197,10 @@ const SuggestCatExtension = Extension.create({
|
|
|
371
197
|
});
|
|
372
198
|
```
|
|
373
199
|
|
|
374
|
-
##
|
|
200
|
+
## Playground
|
|
201
|
+
|
|
202
|
+
See the [interactive demo](https://emergence-engineering.github.io/emergence-tools/#suggestcat) in the monorepo playground.
|
|
203
|
+
|
|
204
|
+
## License
|
|
375
205
|
|
|
376
|
-
|
|
206
|
+
MIT
|
package/dist/index.es.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Fragment as t,Slice as e}from"prosemirror-model";import{PluginKey as n,Plugin as s,TextSelection as o}from"prosemirror-state";import{__awaiter as r}from"tslib";import a from"fast-diff";import{getDiff as i,isIdentity as c}from"@emergence-engineering/fast-diff-merge";export{getDiff}from"@emergence-engineering/fast-diff-merge";import{StepMap as l,Mapping as d}from"prosemirror-transform";import{DecorationSet as u,Decoration as p}from"prosemirror-view";var g,m,f,E,T,S;!function(t){t.suggestionUpdate="suggestionUpdate",t.acceptSuggestion="acceptSuggestion",t.openSuggestion="openSuggestion",t.closeSuggestion="closeSuggestion",t.discardSuggestion="discardSuggestion",t.setEnabled="setEnabled"}(g||(g={})),function(t){t.grammarSuggestPopup="grammar-suggest-popup"}(m||(m={})),function(t){t.Casual="Casual",t.Confident="Confident",t.Straightforward="Straightforward",t.Friendly="Friendly"}(f||(f={})),function(t){t.English="English",t.Spanish="Spanish",t.French="French",t.German="German",t.Italian="Italian",t.Portuguese="Portuguese",t.Dutch="Dutch",t.Russian="Russian",t.Chinese="Chinese",t.Korean="Korean",t.Japanese="Japanese"}(E||(E={})),function(t){t.Complete="Complete",t.SmallComplete="SmallComplete",t.Improve="Improve",t.MakeLonger="MakeLonger",t.MakeShorter="MakeShorter",t.Simplify="Simplify",t.Explain="Explain",t.ActionItems="ActionItems"}(T||(T={})),function(t){t.ChangeTone="ChangeTone",t.Translate="Translate",t.Hint="Hint",t.Custom="Custom"}(S||(S={}));const h=T,y=S;var R,I,C;!function(t){t.idle="idle",t.new="new",t.streaming="streaming",t.finished="finished",t.accepted="accepted",t.cancelled="cancelled",t.rejected="rejected",t.done="done",t.error="error"}(R||(R={})),function(t){t.IDLE="idle",t.PENDING="pending",t.STREAMING="streaming",t.PREVIEW="preview",t.APPLYING="applying"}(I||(I={})),function(t){t.START_TASK="START_TASK",t.STREAM_UPDATE="STREAM_UPDATE",t.STREAM_COMPLETE="STREAM_COMPLETE",t.STREAM_ERROR="STREAM_ERROR",t.ACCEPT_RESULT="ACCEPT_RESULT",t.REJECT_RESULT="REJECT_RESULT",t.CANCEL_TASK="CANCEL_TASK",t.CLEAR_ERROR="CLEAR_ERROR",t.SET_ENABLED="SET_ENABLED"}(C||(C={}));const b={maxSelection:1e3},O="https://suggestion-gw5lxik4dq-uc.a.run.app",v="https://prosemirror-ai-plugin.web.app/api/suggestion",x="openai:gpt-4o-mini";function A(t){var e,n;return{apiKey:t.apiKey,endpoint:null!==(e=t.endpoint)&&void 0!==e?e:O,model:null!==(n=t.model)&&void 0!==n?n:x}}function D(t){var e,n;return{apiKey:t.apiKey,endpoint:null!==(e=t.endpoint)&&void 0!==e?e:v,model:null!==(n=t.model)&&void 0!==n?n:x}}function N(t,e){var n;return r(this,void 0,void 0,(function*(){const{apiKey:s,text:o,task:r,params:a,endpoint:i=O,model:c=x,signal:l}=t;let d="";const u={model:c,modelParams:{input:[o],task:r,params:a}};try{const t=yield fetch(i,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},signal:l,body:JSON.stringify(u)});if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);const o=null===(n=t.body)||void 0===n?void 0:n.getReader();if(!o)throw new Error("No response body");for(;;){const{done:t,value:n}=yield o.read();if(t)break;if(null==l?void 0:l.aborted)return;const s=(new TextDecoder).decode(n);d+=s,e.onChunk(s,d)}(null==l?void 0:l.aborted)||e.onComplete(d)}catch(t){if(t instanceof Error&&"AbortError"===t.name)return;e.onError(t instanceof Error?t:new Error(String(t)))}}))}function w(t){return r(this,void 0,void 0,(function*(){const{apiKey:e,text:n,endpoint:s=v,model:o=x}=t,r={model:o,modelParams:{input:[...n.split("\n")]}};try{const t=yield fetch(s,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify(r)});if(!t.ok){const e=yield t.text();return console.error({status:t.status,text:e}),{fixed:!1,result:n,error:!0}}const o=yield t.json();return(null==o?void 0:o.length)?{result:o.join("\n"),fixed:!0}:{fixed:!1,result:n}}catch(t){return console.error("Grammar request error:",t),{fixed:!1,result:n,error:!0}}}))}const M=new WeakMap;function P(t){const e=M.get(t);e&&(e.abort(),M.delete(t))}function U(t){return M.has(t)}const j=new n("completePluginV2"),_={status:I.IDLE,streamedResult:"",enabled:!0};function L(n,a={}){const{maxSelection:i,apiEndpoint:c,model:l}=Object.assign(Object.assign({},b),a);let d=!1;return new s({key:j,state:{init:()=>Object.assign({},_),apply(t,e){const n=t.getMeta(j);return n?function(t,e){switch(e.type){case C.SET_ENABLED:return Object.assign(Object.assign(Object.assign({},t),{enabled:e.enabled}),e.enabled?{}:_);case C.START_TASK:return t.status!==I.IDLE?t:Object.assign(Object.assign({},t),{status:I.PENDING,taskType:e.taskType,params:e.params,selection:e.selection,streamedResult:"",error:void 0});case C.STREAM_UPDATE:return t.status!==I.PENDING&&t.status!==I.STREAMING?t:Object.assign(Object.assign({},t),{status:I.STREAMING,streamedResult:e.result});case C.STREAM_COMPLETE:return t.status!==I.PENDING&&t.status!==I.STREAMING?t:Object.assign(Object.assign({},t),{status:I.PREVIEW,streamedResult:e.result});case C.STREAM_ERROR:return Object.assign(Object.assign({},t),{status:I.IDLE,error:e.error,streamedResult:"",taskType:void 0,params:void 0,selection:void 0});case C.ACCEPT_RESULT:return t.status!==I.PREVIEW?t:Object.assign(Object.assign({},t),{status:I.APPLYING});case C.REJECT_RESULT:return t.status!==I.PREVIEW?t:Object.assign(Object.assign({},_),{enabled:t.enabled});case C.CANCEL_TASK:return Object.assign(Object.assign({},_),{enabled:t.enabled});case C.CLEAR_ERROR:return Object.assign(Object.assign({},t),{error:void 0});default:return t}}(e,n):e.status===I.APPLYING?Object.assign(Object.assign({},_),{enabled:e.enabled}):e}},view:()=>({update(s){const a=j.getState(s.state);if(a&&a.enabled){if(a.status===I.PENDING&&!d){const{text:t,error:e}=function(t,e,n){const{taskType:s,selection:o}=e;if(s===T.Complete){const{doc:e}=t.state,n=[];return e.descendants((t=>{"paragraph"===t.type.name&&n.push(t.textContent)})),{text:n.length>=2?n.slice(-2).join(" "):n.join(" ")}}return o?t.state.doc.textBetween(o.from,o.to).length>n?{text:"",error:"Selection is too big"}:{text:t.state.doc.textBetween(o.from,o.to,"\n")}:{text:"",error:"No selection"}}(s,a,i);if(e)return void s.dispatch(s.state.tr.setMeta(j,{type:C.STREAM_ERROR,error:e}));if(!t)return void s.dispatch(s.state.tr.setMeta(j,{type:C.STREAM_ERROR,error:"No text to process"}));d=!0,function({view:t,pluginKey:e,apiKey:n,text:s,task:o,params:a,apiEndpoint:i,model:c}){return r(this,void 0,void 0,(function*(){P(t);const r=new AbortController;M.set(t,r);try{yield N({apiKey:n,text:s,task:o,params:a,endpoint:i,model:c,signal:r.signal},{onChunk:(n,s)=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:C.STREAM_UPDATE,result:s}))},onComplete:n=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:C.STREAM_COMPLETE,result:n}))},onError:n=>{console.error("Streaming request error:",n),t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:C.STREAM_ERROR,error:n.message}))}})}finally{M.delete(t)}}))}({view:s,pluginKey:j,apiKey:n,text:t,task:a.taskType,params:a.params,apiEndpoint:c,model:l}).finally((()=>{d=!1}))}a.status===I.APPLYING&&function(n,s){const{taskType:r,selection:a,streamedResult:i}=s;if(!i)return;let{tr:c}=n.state;if(r===T.Complete)c=c.insertText(i,n.state.doc.nodeSize-2);else if(a){const s=i.split("\n\n").map((t=>n.state.schema.node("paragraph",null,t?n.state.schema.text(t):void 0))),r=t.fromArray(s);c=c.setSelection(o.create(n.state.doc,a.from,a.to)),c.selection.replace(c,new e(r,0,0))}n.dispatch(c),n.focus()}(s,a)}},destroy(){}})})}function k(t,e,n){const s=t.state.selection;t.dispatch(t.state.tr.setMeta(j,{type:C.START_TASK,taskType:e,params:n,selection:s.empty?void 0:s}))}function G(t){t.dispatch(t.state.tr.setMeta(j,{type:C.ACCEPT_RESULT}))}function K(t){t.dispatch(t.state.tr.setMeta(j,{type:C.REJECT_RESULT}))}function F(t){P(t),t.dispatch(t.state.tr.setMeta(j,{type:C.CANCEL_TASK}))}function V(t,e){e||P(t),t.dispatch(t.state.tr.setMeta(j,{type:C.SET_ENABLED,enabled:e}))}function B(t){t.dispatch(t.state.tr.setMeta(j,{type:C.CLEAR_ERROR}))}function H(t){return j.getState(t.state)}const W={debounceMs:2e3,createUpdatePopup:(t,e,n,s,o)=>{const r=document.createElement("div");r.className="grammar-suggest-tooltip";const{spec:a}=e,i=t.dom.getBoundingClientRect();r.id=m.grammarSuggestPopup;const c=t.coordsAtPos(n),l=t.dom.scrollTop||0,d=t.dom.scrollLeft||0;r.style.left=`${c.left-i.left+d}px`,r.style.top=`${c.bottom-i.top+l+5}px`;const u=document.createElement("div");u.className="grammar-suggest-tooltip-apply",u.innerText=a.text||a.originalText,a.text||(u.style.textDecoration="line-through",u.style.color="red"),u.onclick=()=>{s(t,e)},r.appendChild(u);const p=document.createElement("div");return p.innerHTML="<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'><path d='M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 10.5858L14.8284 7.75736L16.2426 9.17157L13.4142 12L16.2426 14.8284L14.8284 16.2426L12 13.4142L9.17157 16.2426L7.75736 14.8284L10.5858 12L7.75736 9.17157L9.17157 7.75736L12 10.5858Z'></path></svg>",p.className="grammar-suggest-tooltip-discard",p.onclick=()=>{o(t,e)},r.appendChild(p),r},withYjs:!1},$={maxSelection:1e3},q=new n("completePlugin"),Q=new n("grammarSuggestPlugin"),Y=(t,e)=>{t.dispatch(t.state.tr.setMeta(Q,{type:g.setEnabled,enabled:e}))},z=(t,e)=>{t.dispatch(t.state.tr.setMeta(q,{type:"setEnabled",enabled:e}))},J=(t,e)=>{if(t===e)return{start:t.length,end:t.length,oldStart:t.length,oldEnd:t.length,oldText:"",newText:""};const n=a(t,e),s=n[0],o=s[0]===a.EQUAL?s[1].length-1:0,r=((t,e)=>{if(e[e.length-1][0]!==a.EQUAL)return t.length;const n=e.slice(0,e.length-1);let s=0;for(const t of n){const[e,n]=t;e===a.EQUAL&&(s+=n.length),e===a.DELETE&&(s+=n.length)}return s})(t,n),i=t.lastIndexOf("\n",o),c=t.indexOf("\n",r),l=-1===i?0:i+1,d=-1===c?t.length:c,u=t.slice(l,d),p=e.slice(l,e.length-(t.length-d));return{start:l,end:l+p.length,oldStart:l,oldEnd:d,oldText:u,newText:p}};var X,Z,tt;!function(t){t.IDLE="IDLE",t.ACTIVE="ACTIVE"}(X||(X={})),function(t){t.DIRTY="DIRTY",t.WAITING="WAITING",t.QUEUED="QUEUED",t.PROCESSING="PROCESSING",t.DONE="DONE",t.BACKOFF="BACKOFF",t.ERROR="ERROR"}(Z||(Z={})),function(t){t.INIT="INIT",t.FINISH="FINISH",t.CLEAR="CLEAR",t.RESUME="RESUME",t.UNIT_STARTED="UNIT_STARTED",t.UNIT_SUCCESS="UNIT_SUCCESS",t.UNIT_ERROR="UNIT_ERROR",t.UPDATE_CONTEXT="UPDATE_CONTEXT",t.REMOVE_DECORATION="REMOVE_DECORATION",t.SELECT_DECORATION="SELECT_DECORATION",t.DESELECT_DECORATION="DESELECT_DECORATION"}(tt||(tt={}));const et={nodeTypes:"paragraph",priorityFilter:()=>!0,visibilityFilter:()=>!0,getGroupId:()=>"default",batchSize:4,maxRetries:3,backoffBase:1e3,dirtyHandling:{shouldRecalculate:!0,debounceDelay:2e3,skipDirtyOnSelfChange:!0},forceRerender:()=>{},onUpdate:void 0};function nt(t){return Object.assign(Object.assign(Object.assign({},et),t),{dirtyHandling:Object.assign(Object.assign({},et.dirtyHandling),t.dirtyHandling)})}function st(t,e,n){const s=[];let o="",r=0;return t.nodesBetween(e,n,((t,a)=>{if(t.isText&&t.text){const i=Math.max(e,a),c=Math.min(n,a+t.nodeSize),l=t.text.slice(i-a,c-a);s.push({from:r,docPos:i}),o+=l,r+=l.length}else t.isBlock&&o.length>0&&(s.push({from:r,docPos:a}),o+="\n",r+=1)})),{text:o,mapping:s}}function ot(t,e){var n,s;for(let n=e.length-1;n>=0;n--)if(e[n].from<=t)return e[n].docPos+(t-e[n].from);return null!==(s=null===(n=e[0])||void 0===n?void 0:n.docPos)&&void 0!==s?s:0}function rt(t,e,n,s="paragraph"){const o=Array.isArray(s)?s:[s],r=[];return t.nodesBetween(e,n,((e,n)=>{if(o.includes(e.type.name)){const s=n+e.nodeSize,o=st(t,n,s);o.text.trim().length>0&&r.push(Object.assign({from:n,to:s},o))}})),r}const at=(t,e)=>t.map((t=>null==t?void 0:t.map(e,0,0))).filter((t=>null!==t)),it=(t,e)=>{const n=t.doc.content.findDiffStart(e.doc.content),s=e.doc.content.findDiffEnd(t.doc.content);if(!s||!n)return;let o=new l([0,0,0]),r=n,a=s.a;return s&&n&&(s.a<n?(r=s.a,a=s.b,o=new l([s.a,0,s.b-s.a])):s.b<n?(r=s.b,a=s.a,o=new l([s.b,s.a-s.b,0])):o=new l([n,s.a-n,s.b-n])),{end:a,mapping:new d([o]),start:r}};function ct(t,e,n,s,o=!1){var r;const a=it(e,n);if(!a)return t;const{start:i,end:c,mapping:l}=a,d=Array.isArray(s.nodeTypes)?s.nodeTypes:[s.nodeTypes],u=Date.now()+s.dirtyHandling.debounceDelay,p=null===(r=t.unitsInProgress)||void 0===r?void 0:r.slice().sort(((t,e)=>t.from-e.from));if(p){const n=[],s=l.map(p[0].from),r=l.map(p[p.length-1].to),a=rt(e.doc,s,r,d);for(const t in p){const e=p[t],s=a[t];if(s){let{status:t}=e;!o&&s.from<=c&&s.to>=i&&(t=Z.DIRTY),n.push(Object.assign(Object.assign(Object.assign({},e),{status:t,waitUntil:o?e.waitUntil:u}),s))}else n.push(Object.assign(Object.assign({},e),{status:Z.DONE,from:r,to:r}))}return Object.assign(Object.assign({},t),{unitsInProgress:n,decorations:at(t.decorations,l)})}return Object.assign(Object.assign({},t),{decorations:at(t.decorations,l)})}function lt(t,e){return 1e3*Math.pow(e/1e3,t)}function dt(t){var e;if(t.status!==X.ACTIVE)return{completed:0,total:0,decorations:t.decorations.length};const n=null!==(e=t.unitsInProgress)&&void 0!==e?e:[];return{completed:n.filter((t=>t.status===Z.DONE||t.status===Z.ERROR)).length,total:n.length,decorations:t.decorations.length}}function ut(t){return new Promise((e=>setTimeout(e,t)))}function pt(t,e,n){t.dispatch(t.state.tr.setMeta(e,n))}function gt(t,e,n){if(e.status===X.ACTIVE)return e.unitsInProgress.find((t=>t.id===n))}function mt(t,e,n){const s=t.unitsInProgress.map((t=>t.id===e?Object.assign(Object.assign({},t),n):t));return"waitUntil"in n&&s.sort(((t,e)=>t.waitUntil-e.waitUntil)),Object.assign(Object.assign({},t),{unitsInProgress:s})}function ft(t){return t.every((t=>t.status===Z.DONE||t.status===Z.ERROR))}function Et(t,e,n,s,o="paragraph"){return rt(t,e,n,o).map(((t,e)=>({id:{},status:Z.QUEUED,from:t.from,to:t.to,text:t.text,mapping:t.mapping,metadata:s(t,e),retryCount:0,waitUntil:0})))}function Tt(t,e){pt(t,e,{type:tt.FINISH})}function St(t,e){pt(t,e,{type:tt.RESUME})}function ht(t){var e;return t.status===X.IDLE&&!!(null===(e=t.unitsInProgress)||void 0===e?void 0:e.some((t=>t.status!==Z.DONE&&t.status!==Z.ERROR)))}function yt(t,e,n,s){var o;switch(e.type){case tt.INIT:return function(t,e,n,s){const o=Et(n,e.onlySelection?s.from:0,e.onlySelection?s.to:n.content.size,((t,n)=>{var s;if(void 0!==e.metadata.single)return e.metadata.single;if(void 0!==e.metadata.array)return null!==(s=e.metadata.array[n])&&void 0!==s?s:e.metadata.array[0];if(void 0!==e.metadata.factory){const n={id:{},status:Z.QUEUED,from:t.from,to:t.to,text:t.text,mapping:[],metadata:{},retryCount:0,waitUntil:0};return e.metadata.factory(n)}return{}})).map((e=>Object.assign(Object.assign({},e),{status:t.options.priorityFilter(e,t.contextState)?Z.QUEUED:Z.WAITING})));return Object.assign(Object.assign({},t),{status:X.ACTIVE,unitsInProgress:o})}(t,e,s.doc,s.selection);case tt.UNIT_STARTED:return t.status!==X.ACTIVE?t:function(t,e){const n=t.unitsInProgress.find((t=>t.id===e));return n?mt(t,e,{status:Z.PROCESSING,requestText:n.text}):t}(t,e.unitId);case tt.UNIT_SUCCESS:return t.status!==X.ACTIVE?t:function(t,e,n){const s=t.unitsInProgress.find((t=>t.id===e.unitId));if(!s)return t;const o=Date.now()+t.options.dirtyHandling.debounceDelay;if(void 0!==s.requestText&&s.requestText!==s.text)return mt(t,e.unitId,{status:Z.DIRTY,requestText:void 0,waitUntil:o});const r=n(e.response,s),a=mt(t,e.unitId,{status:Z.DONE,response:e.response,requestText:void 0});return Object.assign(Object.assign({},a),{decorations:[...a.decorations,...r]})}(t,e,n);case tt.UNIT_ERROR:return t.status!==X.ACTIVE?t:function(t,e){const n=t.unitsInProgress.find((t=>t.id===e.unitId));if(!n)return t;const s=Date.now()+t.options.dirtyHandling.debounceDelay;if(void 0!==n.requestText&&n.requestText!==n.text)return mt(t,e.unitId,{status:Z.DIRTY,requestText:void 0,waitUntil:s});const o=n.retryCount+1;return o>=t.options.maxRetries?mt(t,e.unitId,{status:Z.ERROR,retryCount:o,requestText:void 0}):mt(t,e.unitId,{status:Z.BACKOFF,retryCount:o,waitUntil:Date.now()+lt(o,t.options.backoffBase),requestText:void 0})}(t,e);case tt.FINISH:return Object.assign(Object.assign({},t),{status:X.IDLE});case tt.CLEAR:return Object.assign(Object.assign({},t),{decorations:[],unitsInProgress:void 0,status:X.IDLE,selected:void 0});case tt.RESUME:return t.status!==X.IDLE?t:(null===(o=t.unitsInProgress)||void 0===o?void 0:o.length)?Object.assign(Object.assign({},t),{status:X.ACTIVE}):t;case tt.UPDATE_CONTEXT:return Object.assign(Object.assign({},t),{contextState:e.contextState});case tt.REMOVE_DECORATION:return Object.assign(Object.assign({},t),{decorations:t.decorations.filter((t=>t.spec.id!==e.id))});case tt.SELECT_DECORATION:return Object.assign(Object.assign({},t),{selected:e.id});case tt.DESELECT_DECORATION:return Object.assign(Object.assign({},t),{selected:void 0});default:return t}}function Rt(t,e){return{status:X.IDLE,decorations:[],selected:void 0,contextState:t,options:e}}function It(t,e,n,s){return r(this,void 0,void 0,(function*(){const o=t.getState(e.state);if(!o||o.status!==X.ACTIVE)return!1;const r=function(t,e){var n;if(t.status!==X.ACTIVE)return;const s=Date.now(),o=null!==(n=t.unitsInProgress)&&void 0!==n?n:[],r=t=>[Z.QUEUED,Z.BACKOFF,Z.DIRTY].includes(t.status)&&t.waitUntil<=s,a=o.find((n=>r(n)&&e.priorityFilter(n,t.contextState)));if(a)return a;const i=o.find(r);if(i)return i;return o.find((t=>[Z.QUEUED,Z.BACKOFF,Z.DIRTY].includes(t.status)&&t.waitUntil>s))||void 0}(o,s);if(!r)return!1;pt(e,t,{type:tt.UNIT_STARTED,unitId:r.id});const a=t.getState(e.state);if(!a)return!1;const i=gt(0,a,r.id);if(i&&i.waitUntil>Date.now()){yield ut(i.waitUntil-Date.now());const n=t.getState(e.state);if(!n||n.status!==X.ACTIVE)return!1}try{const s=yield n(e,r);s.error?pt(e,t,{type:tt.UNIT_ERROR,unitId:r.id,error:s.error}):void 0!==s.data&&pt(e,t,{type:tt.UNIT_SUCCESS,unitId:r.id,response:s.data})}catch(n){pt(e,t,{type:tt.UNIT_ERROR,unitId:r.id,error:n instanceof Error?n:new Error(String(n))})}return!0}))}function Ct(t,e,n,s){return r(this,void 0,void 0,(function*(){return!!(yield It(t,e,n,s))&&Ct(t,e,n,s)}))}function bt(t,e,n,s){return r(this,void 0,void 0,(function*(){const o=Array.from({length:s.batchSize},(()=>Ct(t,e,n,s)));yield Promise.all(o)}))}function Ot(t){return t.status===X.ACTIVE&&t.unitsInProgress.some((t=>t.status===Z.QUEUED||t.status===Z.WAITING||t.status===Z.BACKOFF||t.status===Z.DIRTY||t.status===Z.PROCESSING))}function vt(t){const{pluginKey:e,unitProcessor:n,decorationFactory:o,decorationTransformer:r,widgetFactory:a,initialContextState:i,options:c,handleKeyDown:l}=t,d=nt(null!=c?c:{});let p=!1;return new s({key:e,state:{init:()=>Rt(i,d),apply(t,n,s,r){const a=t.getMeta(e),i=t.docChanged&&void 0!==a&&d.dirtyHandling.skipDirtyOnSelfChange;let c=n;if(t.docChanged&&(c=ct(n,t,s,d,i)),a){const t=yt(c,a,o,r);return d.onUpdate&&d.onUpdate(t),t}return c}},props:{decorations(t){const n=e.getState(t);if(!n)return u.empty;let s=n.decorations.filter((t=>d.visibilityFilter(t,n.contextState)));r&&(s=r(s,n));const o=[];if(a&&n.unitsInProgress)for(const t of n.unitsInProgress)if(t.status!==Z.DONE&&t.status!==Z.ERROR){const e=a(t);e&&o.push(e)}return u.create(t.doc,[...s,...o])},handleKeyDown:l},view:()=>({update(t){const s=e.getState(t.state);s&&s.status===X.ACTIVE&&!p&&Ot(s)&&(p=!0,bt(e,t,n,d).finally((()=>{p=!1;const n=e.getState(t.state);!n||n.status!==X.ACTIVE||Ot(n)||d.dirtyHandling.shouldRecalculate||pt(t,e,{type:tt.FINISH})})))}})})}function xt(t){return new n(t)}const At=(t,e)=>t.map((n=>{const s=ot(n.from,e.mapping),o=ot(n.to,e.mapping);return p.inline(s,o,{class:"link-detected",style:"color: #0066cc; text-decoration: underline; cursor: pointer;"},{id:{},unitId:e.id,originalText:n.url,response:t,url:n.url})})),Dt=/https?:\/\/[^\s<>"')\]]+/g;const Nt=(t,e)=>r(void 0,void 0,void 0,(function*(){return{data:function(t){const e=[];let n=Dt.exec(t);for(;null!==n;)e.push({url:n[0],from:n.index,to:n.index+n[0].length}),n=Dt.exec(t);return e}(e.text)}})),wt=xt("linkDetector");function Mt(){const t=vt({pluginKey:wt,unitProcessor:Nt,decorationFactory:At,initialContextState:{}});return new s({key:wt,state:t.spec.state,props:Object.assign(Object.assign({},t.spec.props),{handleDOMEvents:{click:(t,e)=>{const n=e.target;if(n.classList.contains("link-detected")){const s=t.posAtDOM(n,0),o=wt.getState(t.state);if(o){const t=o.decorations.find((t=>t.from<=s&&t.to>=s&&t.spec.url));if(null==t?void 0:t.spec.url)return window.open(t.spec.url,"_blank"),e.preventDefault(),!0}}return!1}}}),view:t.spec.view})}const Pt={moderate:{class:"word-complexity-moderate",style:"background-color: rgba(255, 193, 7, 0.3); border-radius: 2px;"},high:{class:"word-complexity-high",style:"background-color: rgba(244, 67, 54, 0.3); border-radius: 2px;"}},Ut=(t,e)=>t.map((n=>{const s=ot(n.from,e.mapping),o=ot(n.to,e.mapping),r=Pt[n.complexity];return p.inline(s,o,{class:r.class,style:r.style},{id:{},unitId:e.id,originalText:n.word,response:t,syllables:n.syllables,complexity:n.complexity})}));function jt(t){let e=t.toLowerCase().replace(/[^a-z]/g,"");if(e.length<=3)return 1;e=e.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/,""),e=e.replace(/^y/,"");const n=e.match(/[aeiouy]{1,2}/g);return n?Math.max(1,n.length):1}const _t=/[a-zA-Z]+/g;function Lt(t,e,n){const s=[];let o=_t.exec(t);for(;null!==o;){const r=o[0],a=jt(r);a>=e&&s.push({word:r,from:o.index,to:o.index+r.length,syllables:a,complexity:a>=n?"high":"moderate"}),o=_t.exec(t)}return s}const kt=t=>(e,n)=>r(void 0,void 0,void 0,(function*(){var e,s;const o=t(),r=null!==(e=o.moderateThreshold)&&void 0!==e?e:3,a=null!==(s=o.highThreshold)&&void 0!==s?s:4;return{data:Lt(n.text,r,a)}})),Gt=(t,e)=>r(void 0,void 0,void 0,(function*(){return{data:Lt(e.text,3,4)}})),Kt={moderateThreshold:3,highThreshold:4},Ft=xt("wordComplexity");function Vt(t={}){return vt({pluginKey:Ft,unitProcessor:Gt,decorationFactory:Ut,initialContextState:Object.assign(Object.assign({},Kt),t)})}const Bt={warning:{class:"sentence-too-long-warning",style:"background-color: rgba(255, 152, 0, 0.2); border-radius: 2px;"},error:{class:"sentence-too-long-error",style:"background-color: rgba(244, 67, 54, 0.2); border-radius: 2px;"}},Ht=(t,e)=>t.map((n=>{const s=ot(n.from,e.mapping),o=ot(n.to,e.mapping),r=Bt[n.severity];return p.inline(s,o,{class:r.class,style:r.style},{id:{},unitId:e.id,originalText:e.text.slice(n.from,n.to),response:t,wordCount:n.wordCount,severity:n.severity})})),Wt=/[^.!?]*[.!?]+/g,$t=/\b\w+\b/g;function qt(t){const e=t.match($t);return e?e.length:0}function Qt(t,e,n){const s=[];let o=Wt.exec(t);for(;null!==o;){const r=o[0],a=qt(r);a>=e&&s.push({from:o.index,to:o.index+r.length,wordCount:a,severity:a>=n?"error":"warning"}),o=Wt.exec(t)}return s}const Yt=t=>(e,n)=>r(void 0,void 0,void 0,(function*(){var e,s;const o=t(),r=null!==(e=o.warningThreshold)&&void 0!==e?e:25,a=null!==(s=o.errorThreshold)&&void 0!==s?s:40;return{data:Qt(n.text,r,a)}})),zt=(t,e)=>r(void 0,void 0,void 0,(function*(){return{data:Qt(e.text,25,40)}})),Jt={warningThreshold:25,errorThreshold:40},Xt=xt("sentenceLength");function Zt(t={}){return vt({pluginKey:Xt,unitProcessor:zt,decorationFactory:Ht,initialContextState:Object.assign(Object.assign({},Jt),t)})}const te=(t,e)=>[p.inline(e.from+1,e.to-1,{class:"random-processor-success",style:"background-color: rgba(76, 175, 80, 0.2); border-radius: 2px;"},{id:{},unitId:e.id,originalText:e.text,response:t,processingTime:t.processingTime})],ee=t=>{let e,n;const s=Math.max(0,t.waitUntil-Date.now());switch(t.status){case Z.QUEUED:e="⏳ Queued...",n="random-processor-widget queued";break;case Z.PROCESSING:e="⚙️ Processing...",n="random-processor-widget processing";break;case Z.BACKOFF:e=`🔄 Retry in ${Math.ceil(s/1e3)}s (attempt ${t.retryCount+1})`,n="random-processor-widget backoff";break;case Z.ERROR:e=`❌ Failed after ${t.retryCount} retries`,n="random-processor-widget error";break;case Z.DIRTY:e="⚠️ Changed",n="random-processor-widget dirty";break;case Z.WAITING:e="⏸️ Waiting...",n="random-processor-widget waiting";break;default:return}let o,r;switch(t.status){case Z.ERROR:o="#ffebee",r="#c62828";break;case Z.DIRTY:o="#fff9c4",r="#f57f17";break;case Z.BACKOFF:o="#fff3e0",r="#e65100";break;default:o="#e3f2fd",r="#1565c0"}const a=document.createElement("span");if(a.className=n,a.textContent=e,t.status===Z.BACKOFF&&s>0){const e=setInterval((()=>{const n=Math.max(0,t.waitUntil-Date.now());a.textContent=`🔄 Retry in ${Math.ceil(n/1e3)}s (attempt ${t.retryCount+1})`,(n<=0||!a.isConnected)&&clearInterval(e)}),1e3)}return a.style.cssText=`\n display: inline-block;\n padding: 2px 8px;\n margin-right: 8px;\n font-size: 12px;\n border-radius: 4px;\n background: ${o};\n color: ${r};\n `,p.widget(t.from+1,a,{side:-1})};function ne(t,e){return Math.floor(Math.random()*(e-t+1))+t}function se(t){return new Promise((e=>setTimeout(e,t)))}const oe=t=>(e,n)=>r(void 0,void 0,void 0,(function*(){var e,s,o;const r=t(),a=null!==(e=r.minDelay)&&void 0!==e?e:500,i=null!==(s=r.maxDelay)&&void 0!==s?s:3e3,c=null!==(o=r.errorRate)&&void 0!==o?o:.3,l=ne(a,i);yield se(l);return Math.random()<c?{error:new Error(`Random error after ${l}ms (attempt ${n.metadata.attempt})`)}:{data:{processingTime:l,success:!0,message:`Processed "${n.text.slice(0,20)}..." in ${l}ms`}}})),re=(t,e)=>r(void 0,void 0,void 0,(function*(){const t=ne(500,3e3);yield se(t);return Math.random()<.3?{error:new Error(`Random error after ${t}ms (attempt ${e.metadata.attempt})`)}:{data:{processingTime:t,success:!0,message:`Processed "${e.text.slice(0,20)}..." in ${t}ms`}}})),ae={minDelay:500,maxDelay:3e3,errorRate:.3},ie=xt("randomProcessor");function ce(t={}){return vt({pluginKey:ie,unitProcessor:re,decorationFactory:te,widgetFactory:ee,initialContextState:Object.assign(Object.assign({},ae),t),options:{batchSize:2,maxRetries:3,backoffBase:2e3}})}const le=(t,e)=>t.fixed&&0!==t.suggestions.length?t.suggestions.map((n=>{const s=ot(n.from,e.mapping),o=ot(n.to,e.mapping),r=""===n.replacement,a={id:{},unitId:e.id,originalText:n.original,replacement:n.replacement,response:t};return p.inline(s,o,{class:"grammarSuggestionV2 "+(r?"removalSuggestionV2":"")},a)})):[],de=(t,e)=>{const n=e.contextState.selectedSuggestionId;return n?t.map((t=>{const e=t.spec;return e.id===n?p.inline(t.from,t.to,{class:"grammarSuggestionV2 grammarSuggestionV2-selected"},e):t})):t},ue=t=>{let e,n;const s=Math.max(0,t.waitUntil-Date.now());switch(t.status){case Z.QUEUED:case Z.WAITING:e="⏳",n="grammarWidgetV2 queued";break;case Z.PROCESSING:e="🔍",n="grammarWidgetV2 processing";break;case Z.BACKOFF:e=`🔄 ${Math.ceil(s/1e3)}s`,n="grammarWidgetV2 backoff";break;case Z.ERROR:e="❌",n="grammarWidgetV2 error";break;case Z.DIRTY:e="✏️",n="grammarWidgetV2 dirty";break;default:return}const o=document.createElement("span");if(o.className=n,o.textContent=e,t.status===Z.BACKOFF&&s>0){const e=setInterval((()=>{const n=Math.max(0,t.waitUntil-Date.now());o.textContent=`🔄 ${Math.ceil(n/1e3)}s`,(n<=0||!o.isConnected)&&clearInterval(e)}),1e3)}return p.widget(t.from+1,o,{side:-1})};const pe=t=>{const{apiKey:e,apiEndpoint:n,model:s,modelStateManager:o}="string"==typeof t?{apiKey:t,apiEndpoint:void 0,model:void 0,modelStateManager:void 0}:t;return(t,a)=>r(void 0,void 0,void 0,(function*(){var t,r,l;try{const d=null!==(t=null==o?void 0:o.getCurrentModel())&&void 0!==t?t:s,u=yield w({apiKey:e,text:a.text,endpoint:n,model:d});if(u.error)return null==o||o.handleFailure(),{error:new Error("Grammar API error")};if(null==o||o.handleSuccess(),!u.fixed)return{data:{fixed:!1,originalText:a.text,fixedText:a.text,suggestions:[]}};const p=(r=a.text,l=u.result,i(r,l).filter((t=>!c(t))).filter((t=>t.original!==`${t.replacement}\n`)).map((t=>({from:t.from,to:t.replacement.endsWith("\n")?t.to-1:t.to,original:t.original,replacement:t.replacement.endsWith("\n")?t.replacement.slice(0,-1):t.replacement}))));return{data:{fixed:p.length>0,originalText:a.text,fixedText:u.result,suggestions:p}}}catch(t){return null==o||o.handleFailure(),{error:t instanceof Error?t:new Error(String(t))}}}))};function ge(t,e,n){const s=e.getState(t.state);if(s)return s.decorations.find((t=>t.spec.id===n))}function me(n,s,r){const a=ge(n,s,r);if(!a)return;const i=a.spec,{replacement:c}=i,{from:l,to:d}=a,u=c.split("\n").map((t=>t?n.state.schema.node("paragraph",null,n.state.schema.text(t)):n.state.schema.node("paragraph"))),p=t.fromArray(u);let{tr:g}=n.state;g=g.setSelection(o.create(n.state.doc,l,d)),g.selection.replace(g,new e(p,1,1)),g=g.setMeta(s,{type:tt.REMOVE_DECORATION,id:r}),n.dispatch(g)}function fe(t,e,n){pt(t,e,{type:tt.REMOVE_DECORATION,id:n})}function Ee(t,e,n){pt(t,e,{type:tt.UPDATE_CONTEXT,contextState:{selectedSuggestionId:n}})}function Te(t,e){pt(t,e,{type:tt.UPDATE_CONTEXT,contextState:{selectedSuggestionId:void 0}})}function Se(t,e){const n=e.getState(t.state);if(null==n?void 0:n.contextState.selectedSuggestionId)return ge(t,e,n.contextState.selectedSuggestionId)}function he(t,e,n,s){var o,a,i;return r(this,void 0,void 0,(function*(){const r=null!==(o=null==s?void 0:s.endpoint)&&void 0!==o?o:O,c=null==s?void 0:s.modelStateManager,l=null!==(i=null!==(a=null==c?void 0:c.getCurrentModel())&&void 0!==a?a:null==s?void 0:s.model)&&void 0!==i?i:x;return new Promise(((s,o)=>{const a={previousPromptType:"Grammar",oldVersion:e,newVersion:n};N({apiKey:t,text:e,task:S.Hint,params:a,endpoint:r,model:l},{onChunk:()=>{},onComplete:t=>{null==c||c.handleSuccess(),s(t)},onError:t=>{null==c||c.handleFailure(),o(t)}})}))}))}const ye={selectedSuggestionId:void 0},Re=xt("grammarSuggestV2"),Ie=(t,e,n,s,o,a)=>{const i=e.spec,c=document.createElement("div");c.className="grammarPopupV2";const l=document.createElement("div");l.className="grammarPopupV2-mainRow";const d=document.createElement("span");d.className="grammarPopupV2-original",d.textContent=`"${i.originalText}"`;const u=document.createElement("span");u.className="grammarPopupV2-arrow",u.textContent=" → ";const p=document.createElement("span");p.className="grammarPopupV2-replacement",p.textContent=""===i.replacement?"(remove)":`"${i.replacement}"`;const g=document.createElement("button");g.className="grammarPopupV2-hint",g.textContent="?",g.title="Why this suggestion?";const m=document.createElement("div");m.className="grammarPopupV2-hintArea",m.style.display="none";let f=!1;g.onclick=t=>r(void 0,void 0,void 0,(function*(){if(t.preventDefault(),t.stopPropagation(),"none"===m.style.display){if(m.style.display="block",!f){m.innerHTML='<span class="grammarPopupV2-loading">Loading...</span>';try{const t=yield a();m.innerHTML="";const e=document.createElement("span");e.className="grammarPopupV2-hintText",e.textContent=t,m.appendChild(e),f=!0}catch(t){m.innerHTML='<span class="grammarPopupV2-hintError">Could not load hint</span>'}}}else m.style.display="none"}));const E=document.createElement("button");E.className="grammarPopupV2-accept",E.textContent="✓",E.onclick=t=>{t.preventDefault(),t.stopPropagation(),s()};const T=document.createElement("button");return T.className="grammarPopupV2-discard",T.textContent="✕",T.onclick=t=>{t.preventDefault(),t.stopPropagation(),o()},l.appendChild(d),l.appendChild(u),l.appendChild(p),l.appendChild(g),l.appendChild(E),l.appendChild(T),c.appendChild(l),c.appendChild(m),c};function Ce(t,e={}){var n;const{apiEndpoint:o,model:r,fallback:a,batchSize:i=4,maxRetries:c=3,backoffBase:l=2e3,debounceMs:d=1e3,createPopup:g=Ie}=e,m=a?function(t){const{primaryModel:e,fallbackModel:n,failureThreshold:s}=t;let o=0;return{getCurrentModel:()=>n&&s>0&&o>=s?n:e,handleSuccess(){o=0},handleFailure(){n&&o++}}}({primaryModel:r,fallbackModel:a.fallbackModel,failureThreshold:null!==(n=a.failureThreshold)&&void 0!==n?n:3}):void 0,f=pe({apiKey:t,apiEndpoint:o,model:r,modelStateManager:m}),E=vt({pluginKey:Re,unitProcessor:f,decorationFactory:le,decorationTransformer:de,widgetFactory:ue,initialContextState:ye,options:{batchSize:i,maxRetries:c,backoffBase:l,dirtyHandling:{debounceDelay:d}}});return new s({key:Re,state:E.spec.state,props:Object.assign(Object.assign({},E.spec.props),{decorations(e){var n;const s=null===(n=E.spec.props)||void 0===n?void 0:n.decorations,a=s?s.call(E,e):u.empty;if("react"===g)return a;const i=Re.getState(e);if(!(null==i?void 0:i.contextState.selectedSuggestionId))return a;const c=i.decorations.find((t=>t.spec.id===i.contextState.selectedSuggestionId));if(!c)return a;const l=c.spec,d=p.widget(c.from,((e,n)=>{const s=n();return void 0===s?document.createElement("div"):g(e,c,s,(()=>me(e,Re,c.spec.id)),(()=>fe(e,Re,c.spec.id)),(()=>he(t,l.originalText,l.replacement,{endpoint:o,model:r,modelStateManager:m})))}),{id:c.spec.id,side:-1,stopEvent:()=>!0});return a.add(e.doc,[d])},handleClick(t,e,n){const s=Re.getState(t.state);if(!s)return!1;const o=s.decorations.find((t=>t.from<=e&&e<=t.to));return o?(o.spec.id===s.contextState.selectedSuggestionId||Ee(t,Re,o.spec.id),!1):(s.contextState.selectedSuggestionId&&Te(t,Re),!1)}}),view:E.spec.view})}var be,Oe;!function(t){t.IDLE="idle",t.DEBOUNCING="debouncing",t.PENDING="pending",t.STREAMING="streaming",t.SHOWING="showing"}(be||(be={})),function(t){t.SET_ENABLED="SET_ENABLED",t.START_DEBOUNCE="START_DEBOUNCE",t.START_REQUEST="START_REQUEST",t.STREAM_UPDATE="STREAM_UPDATE",t.STREAM_COMPLETE="STREAM_COMPLETE",t.STREAM_ERROR="STREAM_ERROR",t.DISMISS="DISMISS",t.ACCEPT="ACCEPT"}(Oe||(Oe={}));const ve={debounceMs:500,maxContextLength:2e3,ghostTextClass:"autoCompleteGhostText"},xe=new WeakMap;function Ae(t){const e=xe.get(t);e&&(e.abort(),xe.delete(t))}function De(t){return xe.has(t)}function Ne(t,e){if(!e)return e;const{doc:n,selection:s}=t.state,o=s.from;if(o<=0)return e;const r=n.textBetween(o-1,o,""),a=e.charAt(0),i=/[\s\-]/;return!i.test(r)&&!i.test(a)?` ${e}`:e}const we=new n("autoCompletePlugin"),Me={status:be.IDLE,enabled:!0,suggestion:"",cursorPos:0};function Pe(t,e){switch(e.type){case Oe.SET_ENABLED:return Object.assign(Object.assign({},Me),{enabled:e.enabled});case Oe.START_DEBOUNCE:return Object.assign(Object.assign({},t),{status:be.DEBOUNCING,cursorPos:e.cursorPos,suggestion:"",error:void 0});case Oe.START_REQUEST:return t.status!==be.DEBOUNCING?t:Object.assign(Object.assign({},t),{status:be.PENDING});case Oe.STREAM_UPDATE:return t.status!==be.PENDING&&t.status!==be.STREAMING?t:Object.assign(Object.assign({},t),{status:be.STREAMING,suggestion:e.suggestion});case Oe.STREAM_COMPLETE:return t.status!==be.PENDING&&t.status!==be.STREAMING?t:Object.assign(Object.assign({},t),{status:be.SHOWING,suggestion:e.suggestion});case Oe.STREAM_ERROR:return Object.assign(Object.assign({},t),{status:be.IDLE,error:e.error,suggestion:""});case Oe.DISMISS:case Oe.ACCEPT:return Object.assign(Object.assign({},t),{status:be.IDLE,suggestion:""});default:return t}}function Ue(t,e={}){const n=Object.assign(Object.assign({},ve),e),{debounceMs:o,maxContextLength:a,apiEndpoint:i,model:c,ghostTextClass:l}=n;let d=null,g=!1,m=null,f=null;return new s({key:we,state:{init:()=>Object.assign({},Me),apply(t,e,n,s){const o=t.getMeta(we);if(o)return Pe(e,o);if(t.docChanged&&(e.status===be.SHOWING||e.status===be.STREAMING)){if(s.selection.from!==e.cursorPos)return Pe(e,{type:Oe.DISMISS})}return!t.selectionSet||t.docChanged||e.status!==be.SHOWING&&e.status!==be.STREAMING?e:Pe(e,{type:Oe.DISMISS})}},props:{decorations(t){const e=we.getState(t);if(!e||!e.enabled)return u.empty;if((e.status===be.STREAMING||e.status===be.SHOWING)&&e.suggestion){const n=document.createElement("span");n.className=l,n.textContent=e.suggestion,n.setAttribute("contenteditable","false");const s=p.widget(e.cursorPos,n,{side:1});return u.create(t.doc,[s])}return u.empty},handleKeyDown(t,e){const n=we.getState(t.state);return!(!n||!n.enabled)&&("Tab"===e.key&&!e.shiftKey&&n.status===be.SHOWING&&n.suggestion?(e.preventDefault(),m=n.suggestion,f=n.cursorPos,t.dispatch(t.state.tr.setMeta(we,{type:Oe.ACCEPT})),!0):"Escape"===e.key&&(n.status===be.STREAMING||n.status===be.SHOWING)&&(e.preventDefault(),Ae(t),t.dispatch(t.state.tr.setMeta(we,{type:Oe.DISMISS})),!0))}},view:()=>({update(e,n){const s=we.getState(e.state),l=we.getState(n);if(s&&s.enabled){if(null!==m&&null!==f){const t=m,n=f;m=null,f=null;const s=e.state.tr.insertText(t,n);return e.dispatch(s),void e.focus()}if(e.state,e.state.doc!==n.doc&&(null==l?void 0:l.status)!==be.PENDING&&(null==l?void 0:l.status)!==be.STREAMING){e.state.tr,d&&(clearTimeout(d),d=null),Ae(e);const t=e.state.selection.from;e.dispatch(e.state.tr.setMeta(we,{type:Oe.START_DEBOUNCE,cursorPos:t})),d=setTimeout((()=>{d=null;const t=we.getState(e.state);(null==t?void 0:t.status)===be.DEBOUNCING&&e.dispatch(e.state.tr.setMeta(we,{type:Oe.START_REQUEST}))}),o)}if(s.status===be.PENDING&&!g){if(!function(t){const{doc:e,selection:n}=t.state,s=n.from,o=e.resolve(s),r=o.parent,a=o.start();if(s!==a+r.content.size)return!1;const i=r.textBetween(0,s-a,"");return!!i.trim()&&!/[.!?]\s*$/.test(i)}(e))return void e.dispatch(e.state.tr.setMeta(we,{type:Oe.DISMISS}));const n=function(t,e){const{doc:n,selection:s}=t.state,o=s.from,r=n.textBetween(0,o,"\n");return r.length>e?r.slice(-e):r}(e,a);if(!n.trim())return void e.dispatch(e.state.tr.setMeta(we,{type:Oe.DISMISS}));g=!0,function({view:t,pluginKey:e,apiKey:n,context:s,apiEndpoint:o,model:a}){return r(this,void 0,void 0,(function*(){Ae(t);const r=new AbortController;xe.set(t,r);try{yield N({apiKey:n,text:s,task:T.SmallComplete,endpoint:o,model:a,signal:r.signal},{onChunk:(n,s)=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:Oe.STREAM_UPDATE,suggestion:Ne(t,s)}))},onComplete:n=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:Oe.STREAM_COMPLETE,suggestion:Ne(t,n)}))},onError:n=>{console.error("Auto-complete streaming error:",n),t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:Oe.STREAM_ERROR,error:n.message}))}})}finally{xe.delete(t)}}))}({view:e,pluginKey:we,apiKey:t,context:n,apiEndpoint:i,model:c}).finally((()=>{g=!1}))}}else d&&(clearTimeout(d),d=null)},destroy(){d&&(clearTimeout(d),d=null)}})})}function je(t,e){e||Ae(t),t.dispatch(t.state.tr.setMeta(we,{type:Oe.SET_ENABLED,enabled:e}))}function _e(t){const e=we.getState(t.state);if(!e||!e.suggestion)return!1;const n=t.state.tr.insertText(e.suggestion,e.cursorPos);return t.dispatch(n),t.dispatch(t.state.tr.setMeta(we,{type:Oe.DISMISS})),t.focus(),!0}function Le(t){Ae(t),t.dispatch(t.state.tr.setMeta(we,{type:Oe.DISMISS}))}function ke(t){return we.getState(t.state)}function Ge(t){var e;const n=we.getState(t.state);return null!==(e=null==n?void 0:n.enabled)&&void 0!==e&&e}function Ke(t){const e=we.getState(t.state);return!!(null==e?void 0:e.suggestion)}const Fe=L;export{tt as ActionType,S as AiPromptsWithParam,T as AiPromptsWithoutParam,Oe as AutoCompleteActionType,be as AutoCompleteStatus,C as CompleteActionType,I as CompleteStatus,O as DEFAULT_COMPLETION_ENDPOINT,v as DEFAULT_GRAMMAR_ENDPOINT,x as DEFAULT_MODEL,m as GrammarSuggestElementClass,g as GrammarSuggestMetaType,f as MoodParamType,y as OpenAiPromptsWithParam,h as OpenAiPromptsWithoutParam,X as RunnerStatus,R as Status,E as TranslationTargetLanguage,Z as UnitStatus,_e as acceptAutoCompletion,G as acceptResult,me as acceptSuggestion,ft as allUnitsFinished,we as autoCompleteKey,Ue as autoCompletePlugin,vt as blockRunnerPlugin,lt as calculateBackoff,ht as canResume,P as cancelActiveRequest,Ae as cancelAutoCompleteRequest,F as cancelTask,B as clearError,Fe as completePlugin,q as completePluginKey,L as completePluginV2,j as completeV2Key,jt as countSyllables,A as createApiConfig,xt as createBlockRunnerKey,D as createGrammarApiConfig,pe as createGrammarProcessor,Rt as createInitialState,Mt as createLinkDetectorPlugin,oe as createRandomProcessor,ce as createRandomProcessorPlugin,Zt as createSentenceLengthPlugin,Yt as createSentenceLengthProcessor,Et as createUnitsFromDocument,Vt as createWordComplexityPlugin,kt as createWordComplexityProcessor,ve as defaultAutoCompleteOptions,$ as defaultCompleteOptions,b as defaultCompleteV2Options,W as defaultOptions,et as defaultRunnerOptions,Te as deselectSuggestion,fe as discardSuggestion,Le as dismissAutoCompletion,pt as dispatchAction,bt as executeParallel,st as extractTextWithMapping,ke as getAutoCompleteState,J as getChangedRegions,H as getCompleteState,dt as getProgress,Se as getSelectedDecoration,gt as getUnitById,rt as getUnitsInRange,le as grammarDecorationFactory,de as grammarDecorationTransformer,w as grammarRequest,Q as grammarSuggestPluginKey,Ce as grammarSuggestPluginV2,Re as grammarSuggestV2Key,ue as grammarWidgetFactory,yt as handleAction,U as hasActiveRequest,De as hasAutoCompleteRequest,Ke as hasAutoCompletion,Ot as hasUnitsToProcess,Ge as isAutoCompleteEnabled,At as linkDetectorDecorationFactory,wt as linkDetectorKey,Nt as linkDetectorProcessor,nt as mergeOptions,Tt as pauseRunner,re as randomProcessor,te as randomProcessorDecorationFactory,ie as randomProcessorKey,ee as randomProcessorWidgetFactory,K as rejectResult,ct as remapPositions,he as requestHint,St as resumeRunner,Ee as selectSuggestion,Ht as sentenceLengthDecorationFactory,Xt as sentenceLengthKey,zt as sentenceLengthProcessor,je as setAutoCompleteEnabled,z as setCompleteEnabled,V as setEnabled,Y as setGrammarSuggestEnabled,ut as sleep,k as startTask,N as streamingRequest,ot as textToDocPos,mt as updateUnit,Ut as wordComplexityDecorationFactory,Ft as wordComplexityKey,Gt as wordComplexityProcessor};
|
|
1
|
+
import{Fragment as e,Slice as t}from"prosemirror-model";import{PluginKey as n,Plugin as s,TextSelection as a}from"prosemirror-state";import r from"fast-diff";import{getDiff as o,isIdentity as i}from"@emergence-engineering/fast-diff-merge";export{getDiff}from"@emergence-engineering/fast-diff-merge";import{Decoration as c,DecorationSet as l}from"prosemirror-view";import{textPosToDocPos as d,UnitStatus as u,ActionType as p,dispatchAction as m,createBlockRunnerKey as g,blockRunnerPlugin as E}from"@emergence-engineering/prosemirror-block-runner";var S,f,T,h,R,y;!function(e){e.suggestionUpdate="suggestionUpdate",e.acceptSuggestion="acceptSuggestion",e.openSuggestion="openSuggestion",e.closeSuggestion="closeSuggestion",e.discardSuggestion="discardSuggestion",e.setEnabled="setEnabled"}(S||(S={})),function(e){e.grammarSuggestPopup="grammar-suggest-popup"}(f||(f={})),function(e){e.Casual="Casual",e.Confident="Confident",e.Straightforward="Straightforward",e.Friendly="Friendly"}(T||(T={})),function(e){e.English="English",e.Spanish="Spanish",e.French="French",e.German="German",e.Italian="Italian",e.Portuguese="Portuguese",e.Dutch="Dutch",e.Russian="Russian",e.Chinese="Chinese",e.Korean="Korean",e.Japanese="Japanese"}(h||(h={})),function(e){e.Complete="Complete",e.SmallComplete="SmallComplete",e.Improve="Improve",e.MakeLonger="MakeLonger",e.MakeShorter="MakeShorter",e.Simplify="Simplify",e.Explain="Explain",e.ActionItems="ActionItems"}(R||(R={})),function(e){e.ChangeTone="ChangeTone",e.Translate="Translate",e.Hint="Hint",e.Custom="Custom"}(y||(y={}));const C=R,A=y;var M,x,I;!function(e){e.idle="idle",e.new="new",e.streaming="streaming",e.finished="finished",e.accepted="accepted",e.cancelled="cancelled",e.rejected="rejected",e.done="done",e.error="error"}(M||(M={})),function(e){e.IDLE="idle",e.PENDING="pending",e.STREAMING="streaming",e.PREVIEW="preview",e.APPLYING="applying"}(x||(x={})),function(e){e.START_TASK="START_TASK",e.STREAM_UPDATE="STREAM_UPDATE",e.STREAM_COMPLETE="STREAM_COMPLETE",e.STREAM_ERROR="STREAM_ERROR",e.ACCEPT_RESULT="ACCEPT_RESULT",e.REJECT_RESULT="REJECT_RESULT",e.CANCEL_TASK="CANCEL_TASK",e.CLEAR_ERROR="CLEAR_ERROR",e.SET_ENABLED="SET_ENABLED"}(I||(I={}));const N={maxSelection:1e3},P="https://suggestion-gw5lxik4dq-uc.a.run.app",D="https://prosemirror-ai-plugin.web.app/api/suggestion",w="openai:gpt-4o-mini";function L(e){return{apiKey:e.apiKey,endpoint:e.endpoint??P,model:e.model??w}}function b(e){return{apiKey:e.apiKey,endpoint:e.endpoint??D,model:e.model??w}}async function _(e,t){const{apiKey:n,text:s,task:a,params:r,endpoint:o=P,model:i=w,signal:c}=e;let l="";const d={model:i,modelParams:{input:[s],task:a,params:r}};try{const e=await fetch(o,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},signal:c,body:JSON.stringify(d)});if(!e.ok)throw new Error(`HTTP error! status: ${e.status}`);const s=e.body?.getReader();if(!s)throw new Error("No response body");for(;;){const{done:e,value:n}=await s.read();if(e)break;if(c?.aborted)return;const a=(new TextDecoder).decode(n);l+=a,t.onChunk(a,l)}c?.aborted||t.onComplete(l)}catch(e){if(e instanceof Error&&"AbortError"===e.name)return;t.onError(e instanceof Error?e:new Error(String(e)))}}async function v(e){const{apiKey:t,text:n,endpoint:s=D,model:a=w}=e,r={model:a,modelParams:{input:[...n.split("\n")]}};try{const e=await fetch(s,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(r)});if(!e.ok){const t=await e.text();return console.error({status:e.status,text:t}),{fixed:!1,result:n,error:!0}}const a=await e.json();return a?.length?{result:a.join("\n"),fixed:!0}:{fixed:!1,result:n}}catch(e){return console.error("Grammar request error:",e),{fixed:!1,result:n,error:!0}}}const O=new WeakMap;function k(e){const t=O.get(e);t&&(t.abort(),O.delete(e))}function G(e){return O.has(e)}const U=new n("completePluginV2"),K={status:x.IDLE,streamedResult:"",enabled:!0};function V(n,r={}){const{maxSelection:o,apiEndpoint:i,model:c}={...N,...r};let l=!1;return new s({key:U,state:{init:()=>({...K}),apply(e,t){const n=e.getMeta(U);return n?function(e,t){switch(t.type){case I.SET_ENABLED:return{...e,enabled:t.enabled,...t.enabled?{}:K};case I.START_TASK:return e.status!==x.IDLE?e:{...e,status:x.PENDING,taskType:t.taskType,params:t.params,selection:t.selection,streamedResult:"",error:void 0};case I.STREAM_UPDATE:return e.status!==x.PENDING&&e.status!==x.STREAMING?e:{...e,status:x.STREAMING,streamedResult:t.result};case I.STREAM_COMPLETE:return e.status!==x.PENDING&&e.status!==x.STREAMING?e:{...e,status:x.PREVIEW,streamedResult:t.result};case I.STREAM_ERROR:return{...e,status:x.IDLE,error:t.error,streamedResult:"",taskType:void 0,params:void 0,selection:void 0};case I.ACCEPT_RESULT:return e.status!==x.PREVIEW?e:{...e,status:x.APPLYING};case I.REJECT_RESULT:return e.status!==x.PREVIEW?e:{...K,enabled:e.enabled};case I.CANCEL_TASK:return{...K,enabled:e.enabled};case I.CLEAR_ERROR:return{...e,error:void 0};default:return e}}(t,n):t.status===x.APPLYING?{...K,enabled:t.enabled}:t}},view:()=>({update(s){const r=U.getState(s.state);if(r&&r.enabled){if(r.status===x.PENDING&&!l){const{text:e,error:t}=function(e,t,n){const{taskType:s,selection:a}=t;if(s===R.Complete){const{doc:t}=e.state,n=[];return t.descendants(e=>{"paragraph"===e.type.name&&n.push(e.textContent)}),{text:n.length>=2?n.slice(-2).join(" "):n.join(" ")}}return a?e.state.doc.textBetween(a.from,a.to).length>n?{text:"",error:"Selection is too big"}:{text:e.state.doc.textBetween(a.from,a.to,"\n")}:{text:"",error:"No selection"}}(s,r,o);if(t)return void s.dispatch(s.state.tr.setMeta(U,{type:I.STREAM_ERROR,error:t}));if(!e)return void s.dispatch(s.state.tr.setMeta(U,{type:I.STREAM_ERROR,error:"No text to process"}));l=!0,async function({view:e,pluginKey:t,apiKey:n,text:s,task:a,params:r,apiEndpoint:o,model:i}){k(e);const c=new AbortController;O.set(e,c);try{await _({apiKey:n,text:s,task:a,params:r,endpoint:o,model:i,signal:c.signal},{onChunk:(n,s)=>{e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:I.STREAM_UPDATE,result:s}))},onComplete:n=>{e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:I.STREAM_COMPLETE,result:n}))},onError:n=>{console.error("Streaming request error:",n),e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:I.STREAM_ERROR,error:n.message}))}})}finally{O.delete(e)}}({view:s,pluginKey:U,apiKey:n,text:e,task:r.taskType,params:r.params,apiEndpoint:i,model:c}).finally(()=>{l=!1})}r.status===x.APPLYING&&function(n,s){const{taskType:r,selection:o,streamedResult:i}=s;if(!i)return;let{tr:c}=n.state;if(r===R.Complete)c=c.insertText(i,n.state.doc.nodeSize-2);else if(o){const s=i.split("\n\n").map(e=>n.state.schema.node("paragraph",null,e?n.state.schema.text(e):void 0)),r=e.fromArray(s);c=c.setSelection(a.create(n.state.doc,o.from,o.to)),c.selection.replace(c,new t(r,0,0))}n.dispatch(c),n.focus()}(s,r)}},destroy(){}})})}function B(e,t,n){const s=e.state.selection;e.dispatch(e.state.tr.setMeta(U,{type:I.START_TASK,taskType:t,params:n,selection:s.empty?void 0:s}))}function W(e){e.dispatch(e.state.tr.setMeta(U,{type:I.ACCEPT_RESULT}))}function H(e){e.dispatch(e.state.tr.setMeta(U,{type:I.REJECT_RESULT}))}function F(e){k(e),e.dispatch(e.state.tr.setMeta(U,{type:I.CANCEL_TASK}))}function $(e,t){t||k(e),e.dispatch(e.state.tr.setMeta(U,{type:I.SET_ENABLED,enabled:t}))}function j(e){e.dispatch(e.state.tr.setMeta(U,{type:I.CLEAR_ERROR}))}function q(e){return U.getState(e.state)}const J={debounceMs:2e3,createUpdatePopup:(e,t,n,s,a)=>{const r=document.createElement("div");r.className="grammar-suggest-tooltip";const{spec:o}=t,i=e.dom.getBoundingClientRect();r.id=f.grammarSuggestPopup;const c=e.coordsAtPos(n),l=e.dom.scrollTop||0,d=e.dom.scrollLeft||0;r.style.left=`${c.left-i.left+d}px`,r.style.top=`${c.bottom-i.top+l+5}px`;const u=document.createElement("div");u.className="grammar-suggest-tooltip-apply",u.innerText=o.text||o.originalText,o.text||(u.style.textDecoration="line-through",u.style.color="red"),u.onclick=()=>{s(e,t)},r.appendChild(u);const p=document.createElement("div");return p.innerHTML="<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'><path d='M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 10.5858L14.8284 7.75736L16.2426 9.17157L13.4142 12L16.2426 14.8284L14.8284 16.2426L12 13.4142L9.17157 16.2426L7.75736 14.8284L10.5858 12L7.75736 9.17157L9.17157 7.75736L12 10.5858Z'></path></svg>",p.className="grammar-suggest-tooltip-discard",p.onclick=()=>{a(e,t)},r.appendChild(p),r},withYjs:!1},Q={maxSelection:1e3},z=new n("completePlugin"),Y=new n("grammarSuggestPlugin"),Z=(e,t)=>{e.dispatch(e.state.tr.setMeta(Y,{type:S.setEnabled,enabled:t}))},X=(e,t)=>{e.dispatch(e.state.tr.setMeta(z,{type:"setEnabled",enabled:t}))},ee=(e,t)=>{if(e===t)return{start:e.length,end:e.length,oldStart:e.length,oldEnd:e.length,oldText:"",newText:""};const n=r(e,t),s=n[0],a=s[0]===r.EQUAL?s[1].length-1:0,o=((e,t)=>{if(t[t.length-1][0]!==r.EQUAL)return e.length;const n=t.slice(0,t.length-1);let s=0;for(const e of n){const[t,n]=e;t===r.EQUAL&&(s+=n.length),t===r.DELETE&&(s+=n.length)}return s})(e,n),i=e.lastIndexOf("\n",a),c=e.indexOf("\n",o),l=-1===i?0:i+1,d=-1===c?e.length:c,u=e.slice(l,d),p=t.slice(l,t.length-(e.length-d));return{start:l,end:l+p.length,oldStart:l,oldEnd:d,oldText:u,newText:p}},te=(e,t)=>e.fixed&&0!==e.suggestions.length?e.suggestions.map(n=>{const s=t.from+1+d(n.from,t.mapping),a=t.from+1+d(n.to,t.mapping),r=""===n.replacement,o={id:{},unitId:t.id,originalText:n.original,replacement:n.replacement,response:e};return c.inline(s,a,{class:"grammarSuggestionV2 "+(r?"removalSuggestionV2":"")},o)}):[],ne=(e,t)=>{const n=t.contextState.selectedSuggestionId;return n?e.map(e=>{const t=e.spec;return t.id===n?c.inline(e.from,e.to,{class:"grammarSuggestionV2 grammarSuggestionV2-selected"},t):e}):e},se=e=>{let t,n;const s=Math.max(0,e.waitUntil-Date.now());switch(e.status){case u.QUEUED:case u.WAITING:t="⏳",n="grammarWidgetV2 queued";break;case u.PROCESSING:t="🔍",n="grammarWidgetV2 processing";break;case u.BACKOFF:t=`🔄 ${Math.ceil(s/1e3)}s`,n="grammarWidgetV2 backoff";break;case u.ERROR:t="❌",n="grammarWidgetV2 error";break;case u.DIRTY:t="✏️",n="grammarWidgetV2 dirty";break;default:return}const a=document.createElement("span");if(a.className=n,a.textContent=t,e.status===u.BACKOFF&&s>0){const t=setInterval(()=>{const n=Math.max(0,e.waitUntil-Date.now());a.textContent=`🔄 ${Math.ceil(n/1e3)}s`,(n<=0||!a.isConnected)&&clearInterval(t)},1e3)}return c.widget(e.from+1,a,{side:-1})};const ae=e=>{const{apiKey:t,apiEndpoint:n,model:s,modelStateManager:a}="string"==typeof e?{apiKey:e,apiEndpoint:void 0,model:void 0,modelStateManager:void 0}:e;return async(e,r)=>{try{const e=a?.getCurrentModel()??s,d=await v({apiKey:t,text:r.text,endpoint:n,model:e});if(d.error)return a?.handleFailure(),{error:new Error("Grammar API error")};if(a?.handleSuccess(),!d.fixed)return{data:{fixed:!1,originalText:r.text,fixedText:r.text,suggestions:[]}};const u=(c=r.text,l=d.result,o(c,l).filter(e=>!i(e)).filter(e=>e.original!==`${e.replacement}\n`).map(e=>({from:e.from,to:e.replacement.endsWith("\n")?e.to-1:e.to,original:e.original,replacement:e.replacement.endsWith("\n")?e.replacement.slice(0,-1):e.replacement})));return{data:{fixed:u.length>0,originalText:r.text,fixedText:d.result,suggestions:u}}}catch(e){return a?.handleFailure(),{error:e instanceof Error?e:new Error(String(e))}}var c,l}};function re(e,t,n){const s=t.getState(e.state);if(s)return s.decorations.find(e=>e.spec.id===n)}function oe(n,s,r){const o=re(n,s,r);if(!o)return;const i=o.spec,{replacement:c}=i,{from:l,to:d}=o,u=c.split("\n").map(e=>e?n.state.schema.node("paragraph",null,n.state.schema.text(e)):n.state.schema.node("paragraph")),m=e.fromArray(u);let{tr:g}=n.state;g=g.setSelection(a.create(n.state.doc,l,d)),g.selection.replace(g,new t(m,1,1)),g=g.setMeta(s,{type:p.REMOVE_DECORATION,id:r}),n.dispatch(g)}function ie(e,t,n){m(e,t,{type:p.REMOVE_DECORATION,id:n})}function ce(e,t,n){m(e,t,{type:p.UPDATE_CONTEXT,contextState:{selectedSuggestionId:n}})}function le(e,t){m(e,t,{type:p.UPDATE_CONTEXT,contextState:{selectedSuggestionId:void 0}})}function de(e,t){const n=t.getState(e.state);if(n?.contextState.selectedSuggestionId)return re(e,t,n.contextState.selectedSuggestionId)}async function ue(e,t,n,s){const a=s?.endpoint??P,r=s?.modelStateManager,o=r?.getCurrentModel()??s?.model??w;return new Promise((s,i)=>{const c={previousPromptType:"Grammar",oldVersion:t,newVersion:n};_({apiKey:e,text:t,task:y.Hint,params:c,endpoint:a,model:o},{onChunk:()=>{},onComplete:e=>{r?.handleSuccess(),s(e)},onError:e=>{r?.handleFailure(),i(e)}})})}const pe={selectedSuggestionId:void 0},me=g("grammarSuggestV2"),ge=(e,t,n,s,a,r)=>{const o=t.spec,i=document.createElement("div");i.className="grammarPopupV2";const c=document.createElement("div");c.className="grammarPopupV2-mainRow";const l=document.createElement("span");l.className="grammarPopupV2-original",l.textContent=`"${o.originalText}"`;const d=document.createElement("span");d.className="grammarPopupV2-arrow",d.textContent=" → ";const u=document.createElement("span");u.className="grammarPopupV2-replacement",u.textContent=""===o.replacement?"(remove)":`"${o.replacement}"`;const p=document.createElement("button");p.className="grammarPopupV2-hint",p.textContent="?",p.title="Why this suggestion?";const m=document.createElement("div");m.className="grammarPopupV2-hintArea",m.style.display="none";let g=!1;p.onclick=async e=>{if(e.preventDefault(),e.stopPropagation(),"none"===m.style.display){if(m.style.display="block",!g){m.innerHTML='<span class="grammarPopupV2-loading">Loading...</span>';try{const e=await r();m.innerHTML="";const t=document.createElement("span");t.className="grammarPopupV2-hintText",t.textContent=e,m.appendChild(t),g=!0}catch{m.innerHTML='<span class="grammarPopupV2-hintError">Could not load hint</span>'}}}else m.style.display="none"};const E=document.createElement("button");E.className="grammarPopupV2-accept",E.textContent="✓",E.onclick=e=>{e.preventDefault(),e.stopPropagation(),s()};const S=document.createElement("button");return S.className="grammarPopupV2-discard",S.textContent="✕",S.onclick=e=>{e.preventDefault(),e.stopPropagation(),a()},c.appendChild(l),c.appendChild(d),c.appendChild(u),c.appendChild(p),c.appendChild(E),c.appendChild(S),i.appendChild(c),i.appendChild(m),i};function Ee(e,t={}){const{apiEndpoint:n,model:a,fallback:r,batchSize:o=4,maxRetries:i=3,backoffBase:d=2e3,debounceMs:u=1e3,createPopup:p=ge}=t,m=r?function(e){const{primaryModel:t,fallbackModel:n,failureThreshold:s}=e;let a=0;return{getCurrentModel:()=>n&&s>0&&a>=s?n:t,handleSuccess(){a=0},handleFailure(){n&&a++}}}({primaryModel:a,fallbackModel:r.fallbackModel,failureThreshold:r.failureThreshold??3}):void 0,g=ae({apiKey:e,apiEndpoint:n,model:a,modelStateManager:m}),S=E({pluginKey:me,unitProcessor:g,decorationFactory:te,decorationTransformer:ne,widgetFactory:se,initialContextState:pe,options:{batchSize:o,maxRetries:i,backoffBase:d,dirtyHandling:{debounceDelay:u}}});return new s({key:me,state:S.spec.state,props:{...S.spec.props,decorations(t){const s=S.spec.props?.decorations,r=s?s.call(S,t):l.empty;if("react"===p)return r;const o=me.getState(t);if(!o?.contextState.selectedSuggestionId)return r;const i=o.decorations.find(e=>e.spec.id===o.contextState.selectedSuggestionId);if(!i)return r;const d=i.spec,u=c.widget(i.from,(t,s)=>{const r=s();return void 0===r?document.createElement("div"):p(t,i,r,()=>oe(t,me,i.spec.id),()=>ie(t,me,i.spec.id),()=>ue(e,d.originalText,d.replacement,{endpoint:n,model:a,modelStateManager:m}))},{id:i.spec.id,side:-1,stopEvent:()=>!0});return r.add(t.doc,[u])},handleClick(e,t,n){const s=me.getState(e.state);if(!s)return!1;const a=s.decorations.find(e=>e.from<=t&&t<=e.to);return a?(a.spec.id===s.contextState.selectedSuggestionId||ce(e,me,a.spec.id),!1):(s.contextState.selectedSuggestionId&&le(e,me),!1)}},view:S.spec.view})}var Se,fe;!function(e){e.IDLE="idle",e.DEBOUNCING="debouncing",e.PENDING="pending",e.STREAMING="streaming",e.SHOWING="showing"}(Se||(Se={})),function(e){e.SET_ENABLED="SET_ENABLED",e.START_DEBOUNCE="START_DEBOUNCE",e.START_REQUEST="START_REQUEST",e.STREAM_UPDATE="STREAM_UPDATE",e.STREAM_COMPLETE="STREAM_COMPLETE",e.STREAM_ERROR="STREAM_ERROR",e.DISMISS="DISMISS",e.ACCEPT="ACCEPT"}(fe||(fe={}));const Te={debounceMs:500,maxContextLength:2e3,ghostTextClass:"autoCompleteGhostText"},he=new WeakMap;function Re(e){const t=he.get(e);t&&(t.abort(),he.delete(e))}function ye(e){return he.has(e)}function Ce(e,t){if(!t)return t;const{doc:n,selection:s}=e.state,a=s.from;if(a<=0)return t;const r=n.textBetween(a-1,a,""),o=t.charAt(0),i=/[\s\-]/;return!i.test(r)&&!i.test(o)?` ${t}`:t}const Ae=new n("autoCompletePlugin"),Me={status:Se.IDLE,enabled:!0,suggestion:"",cursorPos:0};function xe(e,t){switch(t.type){case fe.SET_ENABLED:return{...Me,enabled:t.enabled};case fe.START_DEBOUNCE:return{...e,status:Se.DEBOUNCING,cursorPos:t.cursorPos,suggestion:"",error:void 0};case fe.START_REQUEST:return e.status!==Se.DEBOUNCING?e:{...e,status:Se.PENDING};case fe.STREAM_UPDATE:return e.status!==Se.PENDING&&e.status!==Se.STREAMING?e:{...e,status:Se.STREAMING,suggestion:t.suggestion};case fe.STREAM_COMPLETE:return e.status!==Se.PENDING&&e.status!==Se.STREAMING?e:{...e,status:Se.SHOWING,suggestion:t.suggestion};case fe.STREAM_ERROR:return{...e,status:Se.IDLE,error:t.error,suggestion:""};case fe.DISMISS:case fe.ACCEPT:return{...e,status:Se.IDLE,suggestion:""};default:return e}}function Ie(e,t={}){const n={...Te,...t},{debounceMs:a,maxContextLength:r,apiEndpoint:o,model:i,ghostTextClass:d}=n;let u=null,p=!1,m=null,g=null;return new s({key:Ae,state:{init:()=>({...Me}),apply(e,t,n,s){const a=e.getMeta(Ae);if(a)return xe(t,a);if(e.docChanged&&(t.status===Se.SHOWING||t.status===Se.STREAMING)){if(s.selection.from!==t.cursorPos)return xe(t,{type:fe.DISMISS})}return!e.selectionSet||e.docChanged||t.status!==Se.SHOWING&&t.status!==Se.STREAMING?t:xe(t,{type:fe.DISMISS})}},props:{decorations(e){const t=Ae.getState(e);if(!t||!t.enabled)return l.empty;if((t.status===Se.STREAMING||t.status===Se.SHOWING)&&t.suggestion){const n=document.createElement("span");n.className=d,n.textContent=t.suggestion,n.setAttribute("contenteditable","false");const s=c.widget(t.cursorPos,n,{side:1});return l.create(e.doc,[s])}return l.empty},handleKeyDown(e,t){const n=Ae.getState(e.state);return!(!n||!n.enabled)&&("Tab"===t.key&&!t.shiftKey&&n.status===Se.SHOWING&&n.suggestion?(t.preventDefault(),m=n.suggestion,g=n.cursorPos,e.dispatch(e.state.tr.setMeta(Ae,{type:fe.ACCEPT})),!0):"Escape"===t.key&&(n.status===Se.STREAMING||n.status===Se.SHOWING)&&(t.preventDefault(),Re(e),e.dispatch(e.state.tr.setMeta(Ae,{type:fe.DISMISS})),!0))}},view:()=>({update(t,n){const s=Ae.getState(t.state),c=Ae.getState(n);if(s&&s.enabled){if(null!==m&&null!==g){const e=m,n=g;m=null,g=null;const s=t.state.tr.insertText(e,n);return t.dispatch(s),void t.focus()}if(t.state,t.state.doc!==n.doc&&c?.status!==Se.PENDING&&c?.status!==Se.STREAMING){t.state.tr,u&&(clearTimeout(u),u=null),Re(t);const e=t.state.selection.from;t.dispatch(t.state.tr.setMeta(Ae,{type:fe.START_DEBOUNCE,cursorPos:e})),u=setTimeout(()=>{u=null;const e=Ae.getState(t.state);e?.status===Se.DEBOUNCING&&t.dispatch(t.state.tr.setMeta(Ae,{type:fe.START_REQUEST}))},a)}if(s.status===Se.PENDING&&!p){if(!function(e){const{doc:t,selection:n}=e.state,s=n.from,a=t.resolve(s),r=a.parent,o=a.start();if(s!==o+r.content.size)return!1;const i=r.textBetween(0,s-o,"");return!!i.trim()&&!/[.!?]\s*$/.test(i)}(t))return void t.dispatch(t.state.tr.setMeta(Ae,{type:fe.DISMISS}));const n=function(e,t){const{doc:n,selection:s}=e.state,a=s.from,r=n.textBetween(0,a,"\n");return r.length>t?r.slice(-t):r}(t,r);if(!n.trim())return void t.dispatch(t.state.tr.setMeta(Ae,{type:fe.DISMISS}));p=!0,async function({view:e,pluginKey:t,apiKey:n,context:s,apiEndpoint:a,model:r}){Re(e);const o=new AbortController;he.set(e,o);try{await _({apiKey:n,text:s,task:R.SmallComplete,endpoint:a,model:r,signal:o.signal},{onChunk:(n,s)=>{e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:fe.STREAM_UPDATE,suggestion:Ce(e,s)}))},onComplete:n=>{e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:fe.STREAM_COMPLETE,suggestion:Ce(e,n)}))},onError:n=>{console.error("Auto-complete streaming error:",n),e.isDestroyed||e.dispatch(e.state.tr.setMeta(t,{type:fe.STREAM_ERROR,error:n.message}))}})}finally{he.delete(e)}}({view:t,pluginKey:Ae,apiKey:e,context:n,apiEndpoint:o,model:i}).finally(()=>{p=!1})}}else u&&(clearTimeout(u),u=null)},destroy(){u&&(clearTimeout(u),u=null)}})})}function Ne(e,t){t||Re(e),e.dispatch(e.state.tr.setMeta(Ae,{type:fe.SET_ENABLED,enabled:t}))}function Pe(e){const t=Ae.getState(e.state);if(!t||!t.suggestion)return!1;const n=e.state.tr.insertText(t.suggestion,t.cursorPos);return e.dispatch(n),e.dispatch(e.state.tr.setMeta(Ae,{type:fe.DISMISS})),e.focus(),!0}function De(e){Re(e),e.dispatch(e.state.tr.setMeta(Ae,{type:fe.DISMISS}))}function we(e){return Ae.getState(e.state)}function Le(e){const t=Ae.getState(e.state);return t?.enabled??!1}function be(e){const t=Ae.getState(e.state);return!!t?.suggestion}const _e=V;export{y as AiPromptsWithParam,R as AiPromptsWithoutParam,fe as AutoCompleteActionType,Se as AutoCompleteStatus,I as CompleteActionType,x as CompleteStatus,P as DEFAULT_COMPLETION_ENDPOINT,D as DEFAULT_GRAMMAR_ENDPOINT,w as DEFAULT_MODEL,f as GrammarSuggestElementClass,S as GrammarSuggestMetaType,T as MoodParamType,A as OpenAiPromptsWithParam,C as OpenAiPromptsWithoutParam,M as Status,h as TranslationTargetLanguage,Pe as acceptAutoCompletion,W as acceptResult,oe as acceptSuggestion,Ae as autoCompleteKey,Ie as autoCompletePlugin,k as cancelActiveRequest,Re as cancelAutoCompleteRequest,F as cancelTask,j as clearError,_e as completePlugin,z as completePluginKey,V as completePluginV2,U as completeV2Key,L as createApiConfig,b as createGrammarApiConfig,ae as createGrammarProcessor,Te as defaultAutoCompleteOptions,Q as defaultCompleteOptions,N as defaultCompleteV2Options,J as defaultOptions,le as deselectSuggestion,ie as discardSuggestion,De as dismissAutoCompletion,we as getAutoCompleteState,ee as getChangedRegions,q as getCompleteState,de as getSelectedDecoration,te as grammarDecorationFactory,ne as grammarDecorationTransformer,v as grammarRequest,Y as grammarSuggestPluginKey,Ee as grammarSuggestPluginV2,me as grammarSuggestV2Key,se as grammarWidgetFactory,G as hasActiveRequest,ye as hasAutoCompleteRequest,be as hasAutoCompletion,Le as isAutoCompleteEnabled,H as rejectResult,ue as requestHint,ce as selectSuggestion,Ne as setAutoCompleteEnabled,X as setCompleteEnabled,$ as setEnabled,Z as setGrammarSuggestEnabled,B as startTask,_ as streamingRequest};
|