prosemirror-suggestcat-plugin 2.2.1 → 2.2.3
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/src/api/config.d.ts.map +1 -0
- package/dist/src/api/index.d.ts.map +1 -0
- package/dist/src/api/request.d.ts.map +1 -0
- package/dist/src/api/streaming.d.ts.map +1 -0
- package/dist/src/api/types.d.ts.map +1 -0
- package/dist/src/autoComplete/actions.d.ts.map +1 -0
- package/dist/src/autoComplete/index.d.ts.map +1 -0
- package/dist/src/autoComplete/plugin.d.ts.map +1 -0
- package/dist/src/autoComplete/streaming.d.ts.map +1 -0
- package/dist/src/autoComplete/types.d.ts.map +1 -0
- package/dist/src/completeV2/actions.d.ts.map +1 -0
- package/dist/src/completeV2/index.d.ts.map +1 -0
- package/dist/src/completeV2/plugin.d.ts.map +1 -0
- package/dist/src/completeV2/streaming.d.ts.map +1 -0
- package/dist/src/completeV2/types.d.ts.map +1 -0
- package/dist/src/createUpdatePopup.d.ts.map +1 -0
- package/dist/src/defaults.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/actions.d.ts +1 -1
- package/dist/src/grammarSuggestV2/actions.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/decorations.d.ts +1 -1
- package/dist/src/grammarSuggestV2/decorations.d.ts.map +1 -0
- package/dist/src/grammarSuggestV2/index.d.ts.map +1 -0
- package/dist/src/grammarSuggestV2/modelState.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/plugin.d.ts +1 -1
- package/dist/src/grammarSuggestV2/plugin.d.ts.map +1 -0
- package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/processor.d.ts +1 -1
- package/dist/src/grammarSuggestV2/processor.d.ts.map +1 -0
- package/dist/src/grammarSuggestV2/types.d.ts.map +1 -0
- package/dist/{index.d.ts → src/index.d.ts} +0 -1
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/{utils.test.d.ts.map → src/utils.test.d.ts.map} +1 -1
- package/package.json +11 -5
- package/dist/api/config.d.ts.map +0 -1
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/request.d.ts.map +0 -1
- package/dist/api/streaming.d.ts.map +0 -1
- package/dist/api/types.d.ts.map +0 -1
- package/dist/autoComplete/actions.d.ts.map +0 -1
- package/dist/autoComplete/index.d.ts.map +0 -1
- package/dist/autoComplete/plugin.d.ts.map +0 -1
- package/dist/autoComplete/streaming.d.ts.map +0 -1
- package/dist/autoComplete/types.d.ts.map +0 -1
- package/dist/blockRunner/defaults.d.ts +0 -4
- package/dist/blockRunner/defaults.d.ts.map +0 -1
- package/dist/blockRunner/examples/linkDetector/decorations.d.ts +0 -4
- package/dist/blockRunner/examples/linkDetector/decorations.d.ts.map +0 -1
- package/dist/blockRunner/examples/linkDetector/index.d.ts +0 -5
- package/dist/blockRunner/examples/linkDetector/index.d.ts.map +0 -1
- package/dist/blockRunner/examples/linkDetector/plugin.d.ts +0 -6
- package/dist/blockRunner/examples/linkDetector/plugin.d.ts.map +0 -1
- package/dist/blockRunner/examples/linkDetector/processor.d.ts +0 -5
- package/dist/blockRunner/examples/linkDetector/processor.d.ts.map +0 -1
- package/dist/blockRunner/examples/linkDetector/types.d.ts +0 -9
- package/dist/blockRunner/examples/linkDetector/types.d.ts.map +0 -1
- package/dist/blockRunner/examples/randomProcessor/decorations.d.ts +0 -5
- package/dist/blockRunner/examples/randomProcessor/decorations.d.ts.map +0 -1
- package/dist/blockRunner/examples/randomProcessor/index.d.ts +0 -5
- package/dist/blockRunner/examples/randomProcessor/index.d.ts.map +0 -1
- package/dist/blockRunner/examples/randomProcessor/plugin.d.ts +0 -6
- package/dist/blockRunner/examples/randomProcessor/plugin.d.ts.map +0 -1
- package/dist/blockRunner/examples/randomProcessor/processor.d.ts +0 -6
- package/dist/blockRunner/examples/randomProcessor/processor.d.ts.map +0 -1
- package/dist/blockRunner/examples/randomProcessor/types.d.ts +0 -15
- package/dist/blockRunner/examples/randomProcessor/types.d.ts.map +0 -1
- package/dist/blockRunner/examples/sentenceLength/decorations.d.ts +0 -4
- package/dist/blockRunner/examples/sentenceLength/decorations.d.ts.map +0 -1
- package/dist/blockRunner/examples/sentenceLength/index.d.ts +0 -5
- package/dist/blockRunner/examples/sentenceLength/index.d.ts.map +0 -1
- package/dist/blockRunner/examples/sentenceLength/plugin.d.ts +0 -6
- package/dist/blockRunner/examples/sentenceLength/plugin.d.ts.map +0 -1
- package/dist/blockRunner/examples/sentenceLength/processor.d.ts +0 -6
- package/dist/blockRunner/examples/sentenceLength/processor.d.ts.map +0 -1
- package/dist/blockRunner/examples/sentenceLength/types.d.ts +0 -13
- package/dist/blockRunner/examples/sentenceLength/types.d.ts.map +0 -1
- package/dist/blockRunner/examples/wordComplexity/decorations.d.ts +0 -4
- package/dist/blockRunner/examples/wordComplexity/decorations.d.ts.map +0 -1
- package/dist/blockRunner/examples/wordComplexity/index.d.ts +0 -5
- package/dist/blockRunner/examples/wordComplexity/index.d.ts.map +0 -1
- package/dist/blockRunner/examples/wordComplexity/plugin.d.ts +0 -6
- package/dist/blockRunner/examples/wordComplexity/plugin.d.ts.map +0 -1
- package/dist/blockRunner/examples/wordComplexity/processor.d.ts +0 -7
- package/dist/blockRunner/examples/wordComplexity/processor.d.ts.map +0 -1
- package/dist/blockRunner/examples/wordComplexity/types.d.ts +0 -14
- package/dist/blockRunner/examples/wordComplexity/types.d.ts.map +0 -1
- package/dist/blockRunner/executor.d.ts +0 -8
- package/dist/blockRunner/executor.d.ts.map +0 -1
- package/dist/blockRunner/executor.test.d.ts +0 -2
- package/dist/blockRunner/executor.test.d.ts.map +0 -1
- package/dist/blockRunner/index.d.ts +0 -13
- package/dist/blockRunner/index.d.ts.map +0 -1
- package/dist/blockRunner/plugin.d.ts +0 -15
- package/dist/blockRunner/plugin.d.ts.map +0 -1
- package/dist/blockRunner/state.d.ts +0 -5
- package/dist/blockRunner/state.d.ts.map +0 -1
- package/dist/blockRunner/state.test.d.ts +0 -2
- package/dist/blockRunner/state.test.d.ts.map +0 -1
- package/dist/blockRunner/testHelpers.d.ts +0 -18
- package/dist/blockRunner/testHelpers.d.ts.map +0 -1
- package/dist/blockRunner/types.d.ts +0 -163
- package/dist/blockRunner/types.d.ts.map +0 -1
- package/dist/blockRunner/utils.d.ts +0 -26
- package/dist/blockRunner/utils.d.ts.map +0 -1
- package/dist/blockRunner/utils.test.d.ts.map +0 -1
- package/dist/completeV2/actions.d.ts.map +0 -1
- package/dist/completeV2/index.d.ts.map +0 -1
- package/dist/completeV2/plugin.d.ts.map +0 -1
- package/dist/completeV2/streaming.d.ts.map +0 -1
- package/dist/completeV2/types.d.ts.map +0 -1
- package/dist/createUpdatePopup.d.ts.map +0 -1
- package/dist/defaults.d.ts.map +0 -1
- package/dist/grammarSuggestV2/actions.d.ts.map +0 -1
- package/dist/grammarSuggestV2/decorations.d.ts.map +0 -1
- package/dist/grammarSuggestV2/index.d.ts.map +0 -1
- package/dist/grammarSuggestV2/modelState.d.ts.map +0 -1
- package/dist/grammarSuggestV2/plugin.d.ts.map +0 -1
- package/dist/grammarSuggestV2/processor.d.ts.map +0 -1
- package/dist/grammarSuggestV2/types.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.test.d.ts +0 -2
- /package/dist/{api → src/api}/config.d.ts +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/request.d.ts +0 -0
- /package/dist/{api → src/api}/streaming.d.ts +0 -0
- /package/dist/{api → src/api}/types.d.ts +0 -0
- /package/dist/{autoComplete → src/autoComplete}/actions.d.ts +0 -0
- /package/dist/{autoComplete → src/autoComplete}/index.d.ts +0 -0
- /package/dist/{autoComplete → src/autoComplete}/plugin.d.ts +0 -0
- /package/dist/{autoComplete → src/autoComplete}/streaming.d.ts +0 -0
- /package/dist/{autoComplete → src/autoComplete}/types.d.ts +0 -0
- /package/dist/{completeV2 → src/completeV2}/actions.d.ts +0 -0
- /package/dist/{completeV2 → src/completeV2}/index.d.ts +0 -0
- /package/dist/{completeV2 → src/completeV2}/plugin.d.ts +0 -0
- /package/dist/{completeV2 → src/completeV2}/streaming.d.ts +0 -0
- /package/dist/{completeV2 → src/completeV2}/types.d.ts +0 -0
- /package/dist/{createUpdatePopup.d.ts → src/createUpdatePopup.d.ts} +0 -0
- /package/dist/{defaults.d.ts → src/defaults.d.ts} +0 -0
- /package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/index.d.ts +0 -0
- /package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/modelState.d.ts +0 -0
- /package/dist/{grammarSuggestV2 → src/grammarSuggestV2}/types.d.ts +0 -0
- /package/dist/{types.d.ts → src/types.d.ts} +0 -0
- /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
- /package/dist/{blockRunner → src}/utils.test.d.ts +0 -0
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 r}from"prosemirror-state";import o from"fast-diff";import{getDiff as a,isIdentity as i}from"@emergence-engineering/fast-diff-merge";export{getDiff}from"@emergence-engineering/fast-diff-merge";import{StepMap as c,Mapping as u}from"prosemirror-transform";import{DecorationSet as l,Decoration as d}from"prosemirror-view";var p,m,g,E,f,T;!function(t){t.suggestionUpdate="suggestionUpdate",t.acceptSuggestion="acceptSuggestion",t.openSuggestion="openSuggestion",t.closeSuggestion="closeSuggestion",t.discardSuggestion="discardSuggestion",t.setEnabled="setEnabled"}(p||(p={})),function(t){t.grammarSuggestPopup="grammar-suggest-popup"}(m||(m={})),function(t){t.Casual="Casual",t.Confident="Confident",t.Straightforward="Straightforward",t.Friendly="Friendly"}(g||(g={})),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"}(f||(f={})),function(t){t.ChangeTone="ChangeTone",t.Translate="Translate",t.Hint="Hint",t.Custom="Custom"}(T||(T={}));const S=f,y=T;var h,R,I;!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"}(h||(h={})),function(t){t.IDLE="idle",t.PENDING="pending",t.STREAMING="streaming",t.PREVIEW="preview",t.APPLYING="applying"}(R||(R={})),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"}(I||(I={}));const C={maxSelection:1e3},x="https://suggestion-gw5lxik4dq-uc.a.run.app",A="https://prosemirror-ai-plugin.web.app/api/suggestion",D="openai:gpt-4o-mini";function w(t){return{apiKey:t.apiKey,endpoint:t.endpoint??x,model:t.model??D}}function N(t){return{apiKey:t.apiKey,endpoint:t.endpoint??A,model:t.model??D}}async function M(t,e){const{apiKey:n,text:s,task:r,params:o,endpoint:a=x,model:i=D,signal:c}=t;let u="";const l={model:i,modelParams:{input:[s],task:r,params:o}};try{const t=await fetch(a,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},signal:c,body:JSON.stringify(l)});if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);const s=t.body?.getReader();if(!s)throw new Error("No response body");for(;;){const{done:t,value:n}=await s.read();if(t)break;if(c?.aborted)return;const r=(new TextDecoder).decode(n);u+=r,e.onChunk(r,u)}c?.aborted||e.onComplete(u)}catch(t){if(t instanceof Error&&"AbortError"===t.name)return;e.onError(t instanceof Error?t:new Error(String(t)))}}async function P(t){const{apiKey:e,text:n,endpoint:s=A,model:r=D}=t,o={model:r,modelParams:{input:[...n.split("\n")]}};try{const t=await fetch(s,{method:"POST",cache:"no-cache",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify(o)});if(!t.ok){const e=await t.text();return console.error({status:t.status,text:e}),{fixed:!1,result:n,error:!0}}const r=await t.json();return r?.length?{result:r.join("\n"),fixed:!0}:{fixed:!1,result:n}}catch(t){return console.error("Grammar request error:",t),{fixed:!1,result:n,error:!0}}}const b=new WeakMap;function O(t){const e=b.get(t);e&&(e.abort(),b.delete(t))}function U(t){return b.has(t)}const v=new n("completePluginV2"),_={status:R.IDLE,streamedResult:"",enabled:!0};function L(n,o={}){const{maxSelection:a,apiEndpoint:i,model:c}={...C,...o};let u=!1;return new s({key:v,state:{init:()=>({..._}),apply(t,e){const n=t.getMeta(v);return n?function(t,e){switch(e.type){case I.SET_ENABLED:return{...t,enabled:e.enabled,...e.enabled?{}:_};case I.START_TASK:return t.status!==R.IDLE?t:{...t,status:R.PENDING,taskType:e.taskType,params:e.params,selection:e.selection,streamedResult:"",error:void 0};case I.STREAM_UPDATE:return t.status!==R.PENDING&&t.status!==R.STREAMING?t:{...t,status:R.STREAMING,streamedResult:e.result};case I.STREAM_COMPLETE:return t.status!==R.PENDING&&t.status!==R.STREAMING?t:{...t,status:R.PREVIEW,streamedResult:e.result};case I.STREAM_ERROR:return{...t,status:R.IDLE,error:e.error,streamedResult:"",taskType:void 0,params:void 0,selection:void 0};case I.ACCEPT_RESULT:return t.status!==R.PREVIEW?t:{...t,status:R.APPLYING};case I.REJECT_RESULT:return t.status!==R.PREVIEW?t:{..._,enabled:t.enabled};case I.CANCEL_TASK:return{..._,enabled:t.enabled};case I.CLEAR_ERROR:return{...t,error:void 0};default:return t}}(e,n):e.status===R.APPLYING?{..._,enabled:e.enabled}:e}},view:()=>({update(s){const o=v.getState(s.state);if(o&&o.enabled){if(o.status===R.PENDING&&!u){const{text:t,error:e}=function(t,e,n){const{taskType:s,selection:r}=e;if(s===f.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 r?t.state.doc.textBetween(r.from,r.to).length>n?{text:"",error:"Selection is too big"}:{text:t.state.doc.textBetween(r.from,r.to,"\n")}:{text:"",error:"No selection"}}(s,o,a);if(e)return void s.dispatch(s.state.tr.setMeta(v,{type:I.STREAM_ERROR,error:e}));if(!t)return void s.dispatch(s.state.tr.setMeta(v,{type:I.STREAM_ERROR,error:"No text to process"}));u=!0,async function({view:t,pluginKey:e,apiKey:n,text:s,task:r,params:o,apiEndpoint:a,model:i}){O(t);const c=new AbortController;b.set(t,c);try{await M({apiKey:n,text:s,task:r,params:o,endpoint:a,model:i,signal:c.signal},{onChunk:(n,s)=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:I.STREAM_UPDATE,result:s}))},onComplete:n=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:I.STREAM_COMPLETE,result:n}))},onError:n=>{console.error("Streaming request error:",n),t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:I.STREAM_ERROR,error:n.message}))}})}finally{b.delete(t)}}({view:s,pluginKey:v,apiKey:n,text:t,task:o.taskType,params:o.params,apiEndpoint:i,model:c}).finally(()=>{u=!1})}o.status===R.APPLYING&&function(n,s){const{taskType:o,selection:a,streamedResult:i}=s;if(!i)return;let{tr:c}=n.state;if(o===f.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)),o=t.fromArray(s);c=c.setSelection(r.create(n.state.doc,a.from,a.to)),c.selection.replace(c,new e(o,0,0))}n.dispatch(c),n.focus()}(s,o)}},destroy(){}})})}function k(t,e,n){const s=t.state.selection;t.dispatch(t.state.tr.setMeta(v,{type:I.START_TASK,taskType:e,params:n,selection:s.empty?void 0:s}))}function G(t){t.dispatch(t.state.tr.setMeta(v,{type:I.ACCEPT_RESULT}))}function K(t){t.dispatch(t.state.tr.setMeta(v,{type:I.REJECT_RESULT}))}function F(t){O(t),t.dispatch(t.state.tr.setMeta(v,{type:I.CANCEL_TASK}))}function V(t,e){e||O(t),t.dispatch(t.state.tr.setMeta(v,{type:I.SET_ENABLED,enabled:e}))}function B(t){t.dispatch(t.state.tr.setMeta(v,{type:I.CLEAR_ERROR}))}function H(t){return v.getState(t.state)}const W={debounceMs:2e3,createUpdatePopup:(t,e,n,s,r)=>{const o=document.createElement("div");o.className="grammar-suggest-tooltip";const{spec:a}=e,i=t.dom.getBoundingClientRect();o.id=m.grammarSuggestPopup;const c=t.coordsAtPos(n),u=t.dom.scrollTop||0,l=t.dom.scrollLeft||0;o.style.left=`${c.left-i.left+l}px`,o.style.top=`${c.bottom-i.top+u+5}px`;const d=document.createElement("div");d.className="grammar-suggest-tooltip-apply",d.innerText=a.text||a.originalText,a.text||(d.style.textDecoration="line-through",d.style.color="red"),d.onclick=()=>{s(t,e)},o.appendChild(d);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=()=>{r(t,e)},o.appendChild(p),o},withYjs:!1},$={maxSelection:1e3},q=new n("completePlugin"),Q=new n("grammarSuggestPlugin"),Y=(t,e)=>{t.dispatch(t.state.tr.setMeta(Q,{type:p.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=o(t,e),s=n[0],r=s[0]===o.EQUAL?s[1].length-1:0,a=((t,e)=>{if(e[e.length-1][0]!==o.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===o.EQUAL&&(s+=n.length),e===o.DELETE&&(s+=n.length)}return s})(t,n),i=t.lastIndexOf("\n",r),c=t.indexOf("\n",a),u=-1===i?0:i+1,l=-1===c?t.length:c,d=t.slice(u,l),p=e.slice(u,e.length-(t.length-l));return{start:u,end:u+p.length,oldStart:u,oldEnd:l,oldText:d,newText:p}};var J,X,Z;!function(t){t.IDLE="IDLE",t.ACTIVE="ACTIVE"}(J||(J={})),function(t){t.DIRTY="DIRTY",t.WAITING="WAITING",t.QUEUED="QUEUED",t.PROCESSING="PROCESSING",t.DONE="DONE",t.BACKOFF="BACKOFF",t.ERROR="ERROR"}(X||(X={})),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"}(Z||(Z={}));const tt={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 et(t){return{...tt,...t,dirtyHandling:{...tt.dirtyHandling,...t.dirtyHandling}}}function nt(t,e,n){const s=[];let r="",o=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),u=t.text.slice(i-a,c-a);s.push({from:o,docPos:i}),r+=u,o+=u.length}else t.isBlock&&r.length>0&&(s.push({from:o,docPos:a}),r+="\n",o+=1)}),{text:r,mapping:s}}function st(t,e){for(let n=e.length-1;n>=0;n--)if(e[n].from<=t)return e[n].docPos+(t-e[n].from);return e[0]?.docPos??0}function rt(t,e,n,s="paragraph"){const r=Array.isArray(s)?s:[s],o=[];return t.nodesBetween(e,n,(e,n)=>{if(r.includes(e.type.name)){const s=n+e.nodeSize,r=nt(t,n,s);r.text.trim().length>0&&o.push({from:n,to:s,...r})}}),o}const ot=(t,e)=>t.map(t=>t?.map(e,0,0)).filter(t=>null!==t);function at(t,e,n,s,r=!1){const o=((t,e)=>{const n=t.doc.content.findDiffStart(e.doc.content),s=e.doc.content.findDiffEnd(t.doc.content);if(!s||!n)return;let r=new c([0,0,0]),o=n,a=s.a;return s&&n&&(s.a<n?(o=s.a,a=s.b,r=new c([s.a,0,s.b-s.a])):s.b<n?(o=s.b,a=s.a,r=new c([s.b,s.a-s.b,0])):r=new c([n,s.a-n,s.b-n])),{end:a,mapping:new u([r]),start:o}})(e,n);if(!o)return t;const{start:a,end:i,mapping:l}=o,d=Array.isArray(s.nodeTypes)?s.nodeTypes:[s.nodeTypes],p=Date.now()+s.dirtyHandling.debounceDelay,m=t.unitsInProgress?.slice().sort((t,e)=>t.from-e.from);if(m){const n=[],s=l.map(m[0].from),o=l.map(m[m.length-1].to),c=rt(e.doc,s,o,d);for(const t in m){const e=m[t],s=c[t];if(s){let{status:t}=e;!r&&s.from<=i&&s.to>=a&&(t=X.DIRTY),n.push({...e,status:t,waitUntil:r?e.waitUntil:p,...s})}else n.push({...e,status:X.DONE,from:o,to:o})}return{...t,unitsInProgress:n,decorations:ot(t.decorations,l)}}return{...t,decorations:ot(t.decorations,l)}}function it(t,e){return(e/1e3)**t*1e3}function ct(t){if(t.status!==J.ACTIVE)return{completed:0,total:0,decorations:t.decorations.length};const e=t.unitsInProgress??[];return{completed:e.filter(t=>t.status===X.DONE||t.status===X.ERROR).length,total:e.length,decorations:t.decorations.length}}function ut(t){return new Promise(e=>setTimeout(e,t))}function lt(t,e,n){t.dispatch(t.state.tr.setMeta(e,n))}function dt(t,e,n){if(e.status===J.ACTIVE)return e.unitsInProgress.find(t=>t.id===n)}function pt(t,e,n){const s=t.unitsInProgress.map(t=>t.id===e?{...t,...n}:t);return"waitUntil"in n&&s.sort((t,e)=>t.waitUntil-e.waitUntil),{...t,unitsInProgress:s}}function mt(t){return t.every(t=>t.status===X.DONE||t.status===X.ERROR)}function gt(t,e,n,s,r="paragraph"){return rt(t,e,n,r).map((t,e)=>({id:{},status:X.QUEUED,from:t.from,to:t.to,text:t.text,mapping:t.mapping,metadata:s(t,e),retryCount:0,waitUntil:0}))}function Et(t,e){lt(t,e,{type:Z.FINISH})}function ft(t,e){lt(t,e,{type:Z.RESUME})}function Tt(t){return t.status===J.IDLE&&!!t.unitsInProgress?.some(t=>t.status!==X.DONE&&t.status!==X.ERROR)}function St(t,e,n,s){switch(e.type){case Z.INIT:return function(t,e,n,s){const r=gt(n,e.onlySelection?s.from:0,e.onlySelection?s.to:n.content.size,(t,n)=>{if(void 0!==e.metadata.single)return e.metadata.single;if(void 0!==e.metadata.array)return e.metadata.array[n]??e.metadata.array[0];if(void 0!==e.metadata.factory){const n={id:{},status:X.QUEUED,from:t.from,to:t.to,text:t.text,mapping:[],metadata:{},retryCount:0,waitUntil:0};return e.metadata.factory(n)}return{}}).map(e=>({...e,status:t.options.priorityFilter(e,t.contextState)?X.QUEUED:X.WAITING}));return{...t,status:J.ACTIVE,unitsInProgress:r}}(t,e,s.doc,s.selection);case Z.UNIT_STARTED:return t.status!==J.ACTIVE?t:function(t,e){const n=t.unitsInProgress.find(t=>t.id===e);return n?pt(t,e,{status:X.PROCESSING,requestText:n.text}):t}(t,e.unitId);case Z.UNIT_SUCCESS:return t.status!==J.ACTIVE?t:function(t,e,n){const s=t.unitsInProgress.find(t=>t.id===e.unitId);if(!s)return t;const r=Date.now()+t.options.dirtyHandling.debounceDelay;if(void 0!==s.requestText&&s.requestText!==s.text)return pt(t,e.unitId,{status:X.DIRTY,requestText:void 0,waitUntil:r});const o=n(e.response,s),a=pt(t,e.unitId,{status:X.DONE,response:e.response,requestText:void 0});return{...a,decorations:[...a.decorations,...o]}}(t,e,n);case Z.UNIT_ERROR:return t.status!==J.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 pt(t,e.unitId,{status:X.DIRTY,requestText:void 0,waitUntil:s});const r=n.retryCount+1;return r>=t.options.maxRetries?pt(t,e.unitId,{status:X.ERROR,retryCount:r,requestText:void 0}):pt(t,e.unitId,{status:X.BACKOFF,retryCount:r,waitUntil:Date.now()+it(r,t.options.backoffBase),requestText:void 0})}(t,e);case Z.FINISH:return{...t,status:J.IDLE};case Z.CLEAR:return{...t,decorations:[],unitsInProgress:void 0,status:J.IDLE,selected:void 0};case Z.RESUME:return t.status!==J.IDLE?t:t.unitsInProgress?.length?{...t,status:J.ACTIVE}:t;case Z.UPDATE_CONTEXT:return{...t,contextState:e.contextState};case Z.REMOVE_DECORATION:return{...t,decorations:t.decorations.filter(t=>t.spec.id!==e.id)};case Z.SELECT_DECORATION:return{...t,selected:e.id};case Z.DESELECT_DECORATION:return{...t,selected:void 0};default:return t}}function yt(t,e){return{status:J.IDLE,decorations:[],selected:void 0,contextState:t,options:e}}async function ht(t,e,n,s){const r=t.getState(e.state);if(!r||r.status!==J.ACTIVE)return!1;const o=function(t,e){if(t.status!==J.ACTIVE)return;const n=Date.now(),s=t.unitsInProgress??[],r=t=>[X.QUEUED,X.BACKOFF,X.DIRTY].includes(t.status)&&t.waitUntil<=n,o=s.find(n=>r(n)&&e.priorityFilter(n,t.contextState));if(o)return o;const a=s.find(r);if(a)return a;return s.find(t=>[X.QUEUED,X.BACKOFF,X.DIRTY].includes(t.status)&&t.waitUntil>n)||void 0}(r,s);if(!o)return!1;lt(e,t,{type:Z.UNIT_STARTED,unitId:o.id});const a=t.getState(e.state);if(!a)return!1;const i=dt(0,a,o.id);if(i&&i.waitUntil>Date.now()){await ut(i.waitUntil-Date.now());const n=t.getState(e.state);if(!n||n.status!==J.ACTIVE)return!1}try{const s=await n(e,o);s.error?lt(e,t,{type:Z.UNIT_ERROR,unitId:o.id,error:s.error}):void 0!==s.data&<(e,t,{type:Z.UNIT_SUCCESS,unitId:o.id,response:s.data})}catch(n){lt(e,t,{type:Z.UNIT_ERROR,unitId:o.id,error:n instanceof Error?n:new Error(String(n))})}return!0}async function Rt(t,e,n,s){return!!await ht(t,e,n,s)&&Rt(t,e,n,s)}async function It(t,e,n,s){const r=Array.from({length:s.batchSize},()=>Rt(t,e,n,s));await Promise.all(r)}function Ct(t){return t.status===J.ACTIVE&&t.unitsInProgress.some(t=>t.status===X.QUEUED||t.status===X.WAITING||t.status===X.BACKOFF||t.status===X.DIRTY||t.status===X.PROCESSING)}function xt(t){const{pluginKey:e,unitProcessor:n,decorationFactory:r,decorationTransformer:o,widgetFactory:a,initialContextState:i,options:c,handleKeyDown:u}=t,d=et(c??{});let p=!1;return new s({key:e,state:{init:()=>yt(i,d),apply(t,n,s,o){const a=t.getMeta(e),i=t.docChanged&&void 0!==a&&d.dirtyHandling.skipDirtyOnSelfChange;let c=n;if(t.docChanged&&(c=at(n,t,s,d,i)),a){const t=St(c,a,r,o);return d.onUpdate&&d.onUpdate(t),t}return c}},props:{decorations(t){const n=e.getState(t);if(!n)return l.empty;let s=n.decorations.filter(t=>d.visibilityFilter(t,n.contextState));o&&(s=o(s,n));const r=[];if(a&&n.unitsInProgress)for(const t of n.unitsInProgress)if(t.status!==X.DONE&&t.status!==X.ERROR){const e=a(t);e&&r.push(e)}return l.create(t.doc,[...s,...r])},handleKeyDown:u},view:()=>({update(t){const s=e.getState(t.state);s&&s.status===J.ACTIVE&&!p&&Ct(s)&&(p=!0,It(e,t,n,d).finally(()=>{p=!1;const n=e.getState(t.state);!n||n.status!==J.ACTIVE||Ct(n)||d.dirtyHandling.shouldRecalculate||lt(t,e,{type:Z.FINISH})}))}})})}function At(t){return new n(t)}const Dt=(t,e)=>t.map(n=>{const s=st(n.from,e.mapping),r=st(n.to,e.mapping);return d.inline(s,r,{class:"link-detected",style:"color: #0066cc; text-decoration: underline; cursor: pointer;"},{id:{},unitId:e.id,originalText:n.url,response:t,url:n.url})}),wt=/https?:\/\/[^\s<>"')\]]+/g;const Nt=async(t,e)=>({data:function(t){const e=[];let n=wt.exec(t);for(;null!==n;)e.push({url:n[0],from:n.index,to:n.index+n[0].length}),n=wt.exec(t);return e}(e.text)}),Mt=At("linkDetector");function Pt(){const t=xt({pluginKey:Mt,unitProcessor:Nt,decorationFactory:Dt,initialContextState:{}});return new s({key:Mt,state:t.spec.state,props:{...t.spec.props,handleDOMEvents:{click:(t,e)=>{const n=e.target;if(n.classList.contains("link-detected")){const s=t.posAtDOM(n,0),r=Mt.getState(t.state);if(r){const t=r.decorations.find(t=>t.from<=s&&t.to>=s&&t.spec.url);if(t?.spec.url)return window.open(t.spec.url,"_blank"),e.preventDefault(),!0}}return!1}}},view:t.spec.view})}const bt={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;"}},Ot=(t,e)=>t.map(n=>{const s=st(n.from,e.mapping),r=st(n.to,e.mapping),o=bt[n.complexity];return d.inline(s,r,{class:o.class,style:o.style},{id:{},unitId:e.id,originalText:n.word,response:t,syllables:n.syllables,complexity:n.complexity})});function Ut(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 vt=/[a-zA-Z]+/g;function _t(t,e,n){const s=[];let r=vt.exec(t);for(;null!==r;){const o=r[0],a=Ut(o);a>=e&&s.push({word:o,from:r.index,to:r.index+o.length,syllables:a,complexity:a>=n?"high":"moderate"}),r=vt.exec(t)}return s}const Lt=t=>async(e,n)=>{const s=t(),r=s.moderateThreshold??3,o=s.highThreshold??4;return{data:_t(n.text,r,o)}},kt=async(t,e)=>({data:_t(e.text,3,4)}),Gt={moderateThreshold:3,highThreshold:4},Kt=At("wordComplexity");function Ft(t={}){return xt({pluginKey:Kt,unitProcessor:kt,decorationFactory:Ot,initialContextState:{...Gt,...t}})}const Vt={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;"}},Bt=(t,e)=>t.map(n=>{const s=st(n.from,e.mapping),r=st(n.to,e.mapping),o=Vt[n.severity];return d.inline(s,r,{class:o.class,style:o.style},{id:{},unitId:e.id,originalText:e.text.slice(n.from,n.to),response:t,wordCount:n.wordCount,severity:n.severity})}),Ht=/[^.!?]*[.!?]+/g,Wt=/\b\w+\b/g;function $t(t){const e=t.match(Wt);return e?e.length:0}function qt(t,e,n){const s=[];let r=Ht.exec(t);for(;null!==r;){const o=r[0],a=$t(o);a>=e&&s.push({from:r.index,to:r.index+o.length,wordCount:a,severity:a>=n?"error":"warning"}),r=Ht.exec(t)}return s}const Qt=t=>async(e,n)=>{const s=t(),r=s.warningThreshold??25,o=s.errorThreshold??40;return{data:qt(n.text,r,o)}},Yt=async(t,e)=>({data:qt(e.text,25,40)}),zt={warningThreshold:25,errorThreshold:40},jt=At("sentenceLength");function Jt(t={}){return xt({pluginKey:jt,unitProcessor:Yt,decorationFactory:Bt,initialContextState:{...zt,...t}})}const Xt=(t,e)=>[d.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})],Zt=t=>{let e,n;const s=Math.max(0,t.waitUntil-Date.now());switch(t.status){case X.QUEUED:e="⏳ Queued...",n="random-processor-widget queued";break;case X.PROCESSING:e="⚙️ Processing...",n="random-processor-widget processing";break;case X.BACKOFF:e=`🔄 Retry in ${Math.ceil(s/1e3)}s (attempt ${t.retryCount+1})`,n="random-processor-widget backoff";break;case X.ERROR:e=`❌ Failed after ${t.retryCount} retries`,n="random-processor-widget error";break;case X.DIRTY:e="⚠️ Changed",n="random-processor-widget dirty";break;case X.WAITING:e="⏸️ Waiting...",n="random-processor-widget waiting";break;default:return}let r,o;switch(t.status){case X.ERROR:r="#ffebee",o="#c62828";break;case X.DIRTY:r="#fff9c4",o="#f57f17";break;case X.BACKOFF:r="#fff3e0",o="#e65100";break;default:r="#e3f2fd",o="#1565c0"}const a=document.createElement("span");if(a.className=n,a.textContent=e,t.status===X.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: ${r};\n color: ${o};\n `,d.widget(t.from+1,a,{side:-1})};function te(t,e){return Math.floor(Math.random()*(e-t+1))+t}function ee(t){return new Promise(e=>setTimeout(e,t))}const ne=t=>async(e,n)=>{const s=t(),r=s.minDelay??500,o=s.maxDelay??3e3,a=s.errorRate??.3,i=te(r,o);await ee(i);return Math.random()<a?{error:new Error(`Random error after ${i}ms (attempt ${n.metadata.attempt})`)}:{data:{processingTime:i,success:!0,message:`Processed "${n.text.slice(0,20)}..." in ${i}ms`}}},se=async(t,e)=>{const n=te(500,3e3);await ee(n);return Math.random()<.3?{error:new Error(`Random error after ${n}ms (attempt ${e.metadata.attempt})`)}:{data:{processingTime:n,success:!0,message:`Processed "${e.text.slice(0,20)}..." in ${n}ms`}}},re={minDelay:500,maxDelay:3e3,errorRate:.3},oe=At("randomProcessor");function ae(t={}){return xt({pluginKey:oe,unitProcessor:se,decorationFactory:Xt,widgetFactory:Zt,initialContextState:{...re,...t},options:{batchSize:2,maxRetries:3,backoffBase:2e3}})}const ie=(t,e)=>t.fixed&&0!==t.suggestions.length?t.suggestions.map(n=>{const s=st(n.from,e.mapping),r=st(n.to,e.mapping),o=""===n.replacement,a={id:{},unitId:e.id,originalText:n.original,replacement:n.replacement,response:t};return d.inline(s,r,{class:"grammarSuggestionV2 "+(o?"removalSuggestionV2":"")},a)}):[],ce=(t,e)=>{const n=e.contextState.selectedSuggestionId;return n?t.map(t=>{const e=t.spec;return e.id===n?d.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 X.QUEUED:case X.WAITING:e="⏳",n="grammarWidgetV2 queued";break;case X.PROCESSING:e="🔍",n="grammarWidgetV2 processing";break;case X.BACKOFF:e=`🔄 ${Math.ceil(s/1e3)}s`,n="grammarWidgetV2 backoff";break;case X.ERROR:e="❌",n="grammarWidgetV2 error";break;case X.DIRTY:e="✏️",n="grammarWidgetV2 dirty";break;default:return}const r=document.createElement("span");if(r.className=n,r.textContent=e,t.status===X.BACKOFF&&s>0){const e=setInterval(()=>{const n=Math.max(0,t.waitUntil-Date.now());r.textContent=`🔄 ${Math.ceil(n/1e3)}s`,(n<=0||!r.isConnected)&&clearInterval(e)},1e3)}return d.widget(t.from+1,r,{side:-1})};const le=t=>{const{apiKey:e,apiEndpoint:n,model:s,modelStateManager:r}="string"==typeof t?{apiKey:t,apiEndpoint:void 0,model:void 0,modelStateManager:void 0}:t;return async(t,o)=>{try{const t=r?.getCurrentModel()??s,l=await P({apiKey:e,text:o.text,endpoint:n,model:t});if(l.error)return r?.handleFailure(),{error:new Error("Grammar API error")};if(r?.handleSuccess(),!l.fixed)return{data:{fixed:!1,originalText:o.text,fixedText:o.text,suggestions:[]}};const d=(c=o.text,u=l.result,a(c,u).filter(t=>!i(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:d.length>0,originalText:o.text,fixedText:l.result,suggestions:d}}}catch(t){return r?.handleFailure(),{error:t instanceof Error?t:new Error(String(t))}}var c,u}};function de(t,e,n){const s=e.getState(t.state);if(s)return s.decorations.find(t=>t.spec.id===n)}function pe(n,s,o){const a=de(n,s,o);if(!a)return;const i=a.spec,{replacement:c}=i,{from:u,to:l}=a,d=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(d);let{tr:m}=n.state;m=m.setSelection(r.create(n.state.doc,u,l)),m.selection.replace(m,new e(p,1,1)),m=m.setMeta(s,{type:Z.REMOVE_DECORATION,id:o}),n.dispatch(m)}function me(t,e,n){lt(t,e,{type:Z.REMOVE_DECORATION,id:n})}function ge(t,e,n){lt(t,e,{type:Z.UPDATE_CONTEXT,contextState:{selectedSuggestionId:n}})}function Ee(t,e){lt(t,e,{type:Z.UPDATE_CONTEXT,contextState:{selectedSuggestionId:void 0}})}function fe(t,e){const n=e.getState(t.state);if(n?.contextState.selectedSuggestionId)return de(t,e,n.contextState.selectedSuggestionId)}async function Te(t,e,n,s){const r=s?.endpoint??x,o=s?.modelStateManager,a=o?.getCurrentModel()??s?.model??D;return new Promise((s,i)=>{const c={previousPromptType:"Grammar",oldVersion:e,newVersion:n};M({apiKey:t,text:e,task:T.Hint,params:c,endpoint:r,model:a},{onChunk:()=>{},onComplete:t=>{o?.handleSuccess(),s(t)},onError:t=>{o?.handleFailure(),i(t)}})})}const Se={selectedSuggestionId:void 0},ye=At("grammarSuggestV2"),he=(t,e,n,s,r,o)=>{const a=e.spec,i=document.createElement("div");i.className="grammarPopupV2";const c=document.createElement("div");c.className="grammarPopupV2-mainRow";const u=document.createElement("span");u.className="grammarPopupV2-original",u.textContent=`"${a.originalText}"`;const l=document.createElement("span");l.className="grammarPopupV2-arrow",l.textContent=" → ";const d=document.createElement("span");d.className="grammarPopupV2-replacement",d.textContent=""===a.replacement?"(remove)":`"${a.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 t=>{if(t.preventDefault(),t.stopPropagation(),"none"===m.style.display){if(m.style.display="block",!g){m.innerHTML='<span class="grammarPopupV2-loading">Loading...</span>';try{const t=await o();m.innerHTML="";const e=document.createElement("span");e.className="grammarPopupV2-hintText",e.textContent=t,m.appendChild(e),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=t=>{t.preventDefault(),t.stopPropagation(),s()};const f=document.createElement("button");return f.className="grammarPopupV2-discard",f.textContent="✕",f.onclick=t=>{t.preventDefault(),t.stopPropagation(),r()},c.appendChild(u),c.appendChild(l),c.appendChild(d),c.appendChild(p),c.appendChild(E),c.appendChild(f),i.appendChild(c),i.appendChild(m),i};function Re(t,e={}){const{apiEndpoint:n,model:r,fallback:o,batchSize:a=4,maxRetries:i=3,backoffBase:c=2e3,debounceMs:u=1e3,createPopup:p=he}=e,m=o?function(t){const{primaryModel:e,fallbackModel:n,failureThreshold:s}=t;let r=0;return{getCurrentModel:()=>n&&s>0&&r>=s?n:e,handleSuccess(){r=0},handleFailure(){n&&r++}}}({primaryModel:r,fallbackModel:o.fallbackModel,failureThreshold:o.failureThreshold??3}):void 0,g=le({apiKey:t,apiEndpoint:n,model:r,modelStateManager:m}),E=xt({pluginKey:ye,unitProcessor:g,decorationFactory:ie,decorationTransformer:ce,widgetFactory:ue,initialContextState:Se,options:{batchSize:a,maxRetries:i,backoffBase:c,dirtyHandling:{debounceDelay:u}}});return new s({key:ye,state:E.spec.state,props:{...E.spec.props,decorations(e){const s=E.spec.props?.decorations,o=s?s.call(E,e):l.empty;if("react"===p)return o;const a=ye.getState(e);if(!a?.contextState.selectedSuggestionId)return o;const i=a.decorations.find(t=>t.spec.id===a.contextState.selectedSuggestionId);if(!i)return o;const c=i.spec,u=d.widget(i.from,(e,s)=>{const o=s();return void 0===o?document.createElement("div"):p(e,i,o,()=>pe(e,ye,i.spec.id),()=>me(e,ye,i.spec.id),()=>Te(t,c.originalText,c.replacement,{endpoint:n,model:r,modelStateManager:m}))},{id:i.spec.id,side:-1,stopEvent:()=>!0});return o.add(e.doc,[u])},handleClick(t,e,n){const s=ye.getState(t.state);if(!s)return!1;const r=s.decorations.find(t=>t.from<=e&&e<=t.to);return r?(r.spec.id===s.contextState.selectedSuggestionId||ge(t,ye,r.spec.id),!1):(s.contextState.selectedSuggestionId&&Ee(t,ye),!1)}},view:E.spec.view})}var Ie,Ce;!function(t){t.IDLE="idle",t.DEBOUNCING="debouncing",t.PENDING="pending",t.STREAMING="streaming",t.SHOWING="showing"}(Ie||(Ie={})),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"}(Ce||(Ce={}));const xe={debounceMs:500,maxContextLength:2e3,ghostTextClass:"autoCompleteGhostText"},Ae=new WeakMap;function De(t){const e=Ae.get(t);e&&(e.abort(),Ae.delete(t))}function we(t){return Ae.has(t)}function Ne(t,e){if(!e)return e;const{doc:n,selection:s}=t.state,r=s.from;if(r<=0)return e;const o=n.textBetween(r-1,r,""),a=e.charAt(0),i=/[\s\-]/;return!i.test(o)&&!i.test(a)?` ${e}`:e}const Me=new n("autoCompletePlugin"),Pe={status:Ie.IDLE,enabled:!0,suggestion:"",cursorPos:0};function be(t,e){switch(e.type){case Ce.SET_ENABLED:return{...Pe,enabled:e.enabled};case Ce.START_DEBOUNCE:return{...t,status:Ie.DEBOUNCING,cursorPos:e.cursorPos,suggestion:"",error:void 0};case Ce.START_REQUEST:return t.status!==Ie.DEBOUNCING?t:{...t,status:Ie.PENDING};case Ce.STREAM_UPDATE:return t.status!==Ie.PENDING&&t.status!==Ie.STREAMING?t:{...t,status:Ie.STREAMING,suggestion:e.suggestion};case Ce.STREAM_COMPLETE:return t.status!==Ie.PENDING&&t.status!==Ie.STREAMING?t:{...t,status:Ie.SHOWING,suggestion:e.suggestion};case Ce.STREAM_ERROR:return{...t,status:Ie.IDLE,error:e.error,suggestion:""};case Ce.DISMISS:case Ce.ACCEPT:return{...t,status:Ie.IDLE,suggestion:""};default:return t}}function Oe(t,e={}){const n={...xe,...e},{debounceMs:r,maxContextLength:o,apiEndpoint:a,model:i,ghostTextClass:c}=n;let u=null,p=!1,m=null,g=null;return new s({key:Me,state:{init:()=>({...Pe}),apply(t,e,n,s){const r=t.getMeta(Me);if(r)return be(e,r);if(t.docChanged&&(e.status===Ie.SHOWING||e.status===Ie.STREAMING)){if(s.selection.from!==e.cursorPos)return be(e,{type:Ce.DISMISS})}return!t.selectionSet||t.docChanged||e.status!==Ie.SHOWING&&e.status!==Ie.STREAMING?e:be(e,{type:Ce.DISMISS})}},props:{decorations(t){const e=Me.getState(t);if(!e||!e.enabled)return l.empty;if((e.status===Ie.STREAMING||e.status===Ie.SHOWING)&&e.suggestion){const n=document.createElement("span");n.className=c,n.textContent=e.suggestion,n.setAttribute("contenteditable","false");const s=d.widget(e.cursorPos,n,{side:1});return l.create(t.doc,[s])}return l.empty},handleKeyDown(t,e){const n=Me.getState(t.state);return!(!n||!n.enabled)&&("Tab"===e.key&&!e.shiftKey&&n.status===Ie.SHOWING&&n.suggestion?(e.preventDefault(),m=n.suggestion,g=n.cursorPos,t.dispatch(t.state.tr.setMeta(Me,{type:Ce.ACCEPT})),!0):"Escape"===e.key&&(n.status===Ie.STREAMING||n.status===Ie.SHOWING)&&(e.preventDefault(),De(t),t.dispatch(t.state.tr.setMeta(Me,{type:Ce.DISMISS})),!0))}},view:()=>({update(e,n){const s=Me.getState(e.state),c=Me.getState(n);if(s&&s.enabled){if(null!==m&&null!==g){const t=m,n=g;m=null,g=null;const s=e.state.tr.insertText(t,n);return e.dispatch(s),void e.focus()}if(e.state,e.state.doc!==n.doc&&c?.status!==Ie.PENDING&&c?.status!==Ie.STREAMING){e.state.tr,u&&(clearTimeout(u),u=null),De(e);const t=e.state.selection.from;e.dispatch(e.state.tr.setMeta(Me,{type:Ce.START_DEBOUNCE,cursorPos:t})),u=setTimeout(()=>{u=null;const t=Me.getState(e.state);t?.status===Ie.DEBOUNCING&&e.dispatch(e.state.tr.setMeta(Me,{type:Ce.START_REQUEST}))},r)}if(s.status===Ie.PENDING&&!p){if(!function(t){const{doc:e,selection:n}=t.state,s=n.from,r=e.resolve(s),o=r.parent,a=r.start();if(s!==a+o.content.size)return!1;const i=o.textBetween(0,s-a,"");return!!i.trim()&&!/[.!?]\s*$/.test(i)}(e))return void e.dispatch(e.state.tr.setMeta(Me,{type:Ce.DISMISS}));const n=function(t,e){const{doc:n,selection:s}=t.state,r=s.from,o=n.textBetween(0,r,"\n");return o.length>e?o.slice(-e):o}(e,o);if(!n.trim())return void e.dispatch(e.state.tr.setMeta(Me,{type:Ce.DISMISS}));p=!0,async function({view:t,pluginKey:e,apiKey:n,context:s,apiEndpoint:r,model:o}){De(t);const a=new AbortController;Ae.set(t,a);try{await M({apiKey:n,text:s,task:f.SmallComplete,endpoint:r,model:o,signal:a.signal},{onChunk:(n,s)=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:Ce.STREAM_UPDATE,suggestion:Ne(t,s)}))},onComplete:n=>{t.isDestroyed||t.dispatch(t.state.tr.setMeta(e,{type:Ce.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:Ce.STREAM_ERROR,error:n.message}))}})}finally{Ae.delete(t)}}({view:e,pluginKey:Me,apiKey:t,context:n,apiEndpoint:a,model:i}).finally(()=>{p=!1})}}else u&&(clearTimeout(u),u=null)},destroy(){u&&(clearTimeout(u),u=null)}})})}function Ue(t,e){e||De(t),t.dispatch(t.state.tr.setMeta(Me,{type:Ce.SET_ENABLED,enabled:e}))}function ve(t){const e=Me.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(Me,{type:Ce.DISMISS})),t.focus(),!0}function _e(t){De(t),t.dispatch(t.state.tr.setMeta(Me,{type:Ce.DISMISS}))}function Le(t){return Me.getState(t.state)}function ke(t){const e=Me.getState(t.state);return e?.enabled??!1}function Ge(t){const e=Me.getState(t.state);return!!e?.suggestion}const Ke=L;export{Z as ActionType,T as AiPromptsWithParam,f as AiPromptsWithoutParam,Ce as AutoCompleteActionType,Ie as AutoCompleteStatus,I as CompleteActionType,R as CompleteStatus,x as DEFAULT_COMPLETION_ENDPOINT,A as DEFAULT_GRAMMAR_ENDPOINT,D as DEFAULT_MODEL,m as GrammarSuggestElementClass,p as GrammarSuggestMetaType,g as MoodParamType,y as OpenAiPromptsWithParam,S as OpenAiPromptsWithoutParam,J as RunnerStatus,h as Status,E as TranslationTargetLanguage,X as UnitStatus,ve as acceptAutoCompletion,G as acceptResult,pe as acceptSuggestion,mt as allUnitsFinished,Me as autoCompleteKey,Oe as autoCompletePlugin,xt as blockRunnerPlugin,it as calculateBackoff,Tt as canResume,O as cancelActiveRequest,De as cancelAutoCompleteRequest,F as cancelTask,B as clearError,Ke as completePlugin,q as completePluginKey,L as completePluginV2,v as completeV2Key,Ut as countSyllables,w as createApiConfig,At as createBlockRunnerKey,N as createGrammarApiConfig,le as createGrammarProcessor,yt as createInitialState,Pt as createLinkDetectorPlugin,ne as createRandomProcessor,ae as createRandomProcessorPlugin,Jt as createSentenceLengthPlugin,Qt as createSentenceLengthProcessor,gt as createUnitsFromDocument,Ft as createWordComplexityPlugin,Lt as createWordComplexityProcessor,xe as defaultAutoCompleteOptions,$ as defaultCompleteOptions,C as defaultCompleteV2Options,W as defaultOptions,tt as defaultRunnerOptions,Ee as deselectSuggestion,me as discardSuggestion,_e as dismissAutoCompletion,lt as dispatchAction,It as executeParallel,nt as extractTextWithMapping,Le as getAutoCompleteState,j as getChangedRegions,H as getCompleteState,ct as getProgress,fe as getSelectedDecoration,dt as getUnitById,rt as getUnitsInRange,ie as grammarDecorationFactory,ce as grammarDecorationTransformer,P as grammarRequest,Q as grammarSuggestPluginKey,Re as grammarSuggestPluginV2,ye as grammarSuggestV2Key,ue as grammarWidgetFactory,St as handleAction,U as hasActiveRequest,we as hasAutoCompleteRequest,Ge as hasAutoCompletion,Ct as hasUnitsToProcess,ke as isAutoCompleteEnabled,Dt as linkDetectorDecorationFactory,Mt as linkDetectorKey,Nt as linkDetectorProcessor,et as mergeOptions,Et as pauseRunner,se as randomProcessor,Xt as randomProcessorDecorationFactory,oe as randomProcessorKey,Zt as randomProcessorWidgetFactory,K as rejectResult,at as remapPositions,Te as requestHint,ft as resumeRunner,ge as selectSuggestion,Bt as sentenceLengthDecorationFactory,jt as sentenceLengthKey,Yt as sentenceLengthProcessor,Ue as setAutoCompleteEnabled,z as setCompleteEnabled,V as setEnabled,Y as setGrammarSuggestEnabled,ut as sleep,k as startTask,M as streamingRequest,st as textToDocPos,pt as updateUnit,Ot as wordComplexityDecorationFactory,Kt as wordComplexityKey,kt 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};
|