@consilioweb/spellcheck 0.10.1
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/LICENSE +21 -0
- package/README.md +567 -0
- package/dist/client.cjs +1711 -0
- package/dist/client.d.cts +77 -0
- package/dist/client.d.ts +77 -0
- package/dist/client.js +1702 -0
- package/dist/index.cjs +1691 -0
- package/dist/index.d.cts +268 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +1677 -0
- package/dist/views.cjs +32 -0
- package/dist/views.d.cts +11 -0
- package/dist/views.d.ts +11 -0
- package/dist/views.js +30 -0
- package/package.json +102 -0
- package/scripts/debug-check.mjs +295 -0
- package/scripts/install.mjs +236 -0
- package/scripts/uninstall.mjs +350 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ConsilioWEB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://readme-typing-svg.demolab.com?font=Fira+Code&weight=600&size=28&pause=1000&color=2563EB¢er=true&vCenter=true&width=600&lines=@consilioweb/spellcheck;Payload+CMS+Spellcheck+Plugin;LanguageTool+%2B+Claude+AI;Dashboard+%2B+Sidebar+%2B+Auto-check" alt="Typing SVG" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@consilioweb/spellcheck"><img src="https://img.shields.io/npm/v/@consilioweb/spellcheck?color=2563eb&label=npm" alt="npm version" /></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/@consilioweb/spellcheck"><img src="https://img.shields.io/npm/dm/@consilioweb/spellcheck?color=22c55e" alt="npm downloads" /></a>
|
|
8
|
+
<img src="https://img.shields.io/badge/Payload_CMS-3.x-blue" alt="Payload CMS 3.x" />
|
|
9
|
+
<img src="https://img.shields.io/badge/LanguageTool-API-green" alt="LanguageTool" />
|
|
10
|
+
<img src="https://img.shields.io/badge/i18n-FR%20%2F%20EN-purple" alt="i18n" />
|
|
11
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow" alt="MIT License" /></a>
|
|
12
|
+
<img src="https://img.shields.io/badge/TypeScript-5.x-3178c6" alt="TypeScript" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
16
|
+
|
|
17
|
+
## About
|
|
18
|
+
|
|
19
|
+
**@consilioweb/spellcheck** is a Payload CMS 3 plugin that adds real-time spelling and grammar checking to your admin panel. Powered by [LanguageTool](https://languagetool.org/) with optional Claude AI semantic analysis.
|
|
20
|
+
|
|
21
|
+
| Feature | Description |
|
|
22
|
+
|---------|-------------|
|
|
23
|
+
| **Dashboard** | Full admin view at `/admin/spellcheck` with bulk scanning |
|
|
24
|
+
| **Sidebar Field** | Real-time spellcheck score + issues in the editor |
|
|
25
|
+
| **Auto-check** | Fire-and-forget hook checks content on every save |
|
|
26
|
+
| **One-click Fix** | Apply corrections directly in Lexical JSON |
|
|
27
|
+
| **LanguageTool** | Grammar, spelling, punctuation via free API |
|
|
28
|
+
| **Claude AI** | Optional semantic analysis (coherence, tone, phrasing) |
|
|
29
|
+
| **Custom Dictionary** | Whitelist tech terms, brand names, proper nouns |
|
|
30
|
+
| **Dynamic Dictionary** | Add/remove words from admin UI, persists in DB |
|
|
31
|
+
| **Offset-based Fix** | Precise corrections using LanguageTool offsets |
|
|
32
|
+
| **Ignore Issues** | Dismiss false positives (persists across reloads) |
|
|
33
|
+
| **i18n** | French and English UI translations |
|
|
34
|
+
|
|
35
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
36
|
+
|
|
37
|
+
## Table of Contents
|
|
38
|
+
|
|
39
|
+
- [Features](#features)
|
|
40
|
+
- [Installation](#installation)
|
|
41
|
+
- [Quick Start](#quick-start)
|
|
42
|
+
- [Configuration](#configuration)
|
|
43
|
+
- [Admin Views](#admin-views)
|
|
44
|
+
- [Dynamic Dictionary](#dynamic-dictionary)
|
|
45
|
+
- [API Endpoints](#api-endpoints)
|
|
46
|
+
- [Engine](#engine)
|
|
47
|
+
- [Package Exports](#package-exports)
|
|
48
|
+
- [Uninstall](#uninstall)
|
|
49
|
+
- [Changelog](#changelog)
|
|
50
|
+
- [License](#license)
|
|
51
|
+
|
|
52
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
### Dashboard (`/admin/spellcheck`)
|
|
57
|
+
|
|
58
|
+
- **Tabbed interface** — Results tab + Dictionary tab
|
|
59
|
+
- **Selective scan** — Check specific pages or all documents at once
|
|
60
|
+
- **Checkbox selection** — Pick individual documents to scan
|
|
61
|
+
- **Collection filter** — Filter by collection (pages, posts, etc.)
|
|
62
|
+
- **Sortable table** — Sort by score, issues, word count, last checked
|
|
63
|
+
- **Expandable rows** — Click a document to see all issues inline
|
|
64
|
+
- **Before/After diff** — Visual comparison of original vs corrected text
|
|
65
|
+
- **Multiple suggestions** — Dropdown to choose between alternative corrections
|
|
66
|
+
- **One-click fix** — Apply corrections directly (issue removed from UI + DB)
|
|
67
|
+
- **Ignore button** — Dismiss false positives (persists in DB across reloads)
|
|
68
|
+
- **Add to dictionary** — Whitelist a word directly from an issue card
|
|
69
|
+
- **Summary cards** — Total documents, average score, issues count
|
|
70
|
+
|
|
71
|
+
### Sidebar Field
|
|
72
|
+
|
|
73
|
+
- **Score badge** — Color-coded score (green/yellow/red) in the editor sidebar
|
|
74
|
+
- **Issue list** — All issues with context, suggestions, and fix buttons
|
|
75
|
+
- **Manual check** — "Vérifier" button for on-demand analysis
|
|
76
|
+
- **Auto-check** — Results loaded automatically from last check
|
|
77
|
+
|
|
78
|
+
### Auto-check on Save
|
|
79
|
+
|
|
80
|
+
- **Non-blocking** — Fire-and-forget async (IIFE pattern, does not slow down saves)
|
|
81
|
+
- **Upsert results** — Stores/updates results in `spellcheck-results` collection
|
|
82
|
+
- **Configurable** — Enable/disable via `checkOnSave` option
|
|
83
|
+
|
|
84
|
+
### LanguageTool Engine
|
|
85
|
+
|
|
86
|
+
- **Free API** — No API key required (public LanguageTool API)
|
|
87
|
+
- **Rate-limited** — 3-second delay between requests for bulk scans
|
|
88
|
+
- **18K char limit** — Automatic text truncation for API compliance
|
|
89
|
+
- **Smart filtering** — Skip premium rules, typography, style-only issues
|
|
90
|
+
- **Custom dictionary** — Whitelist words that shouldn't be flagged
|
|
91
|
+
|
|
92
|
+
### Claude AI Fallback (Optional)
|
|
93
|
+
|
|
94
|
+
- **Semantic analysis** — Checks coherence, tone, phrasing, missing words
|
|
95
|
+
- **Complementary** — Does NOT duplicate LanguageTool (no spelling/grammar)
|
|
96
|
+
- **Cost-efficient** — Uses Claude Haiku for fast, cheap analysis
|
|
97
|
+
- **Opt-in** — Disabled by default, enable via `enableAiFallback: true`
|
|
98
|
+
|
|
99
|
+
### Dynamic Dictionary
|
|
100
|
+
|
|
101
|
+
- **Admin UI** — Manage dictionary from the "Dictionnaire" tab in the dashboard
|
|
102
|
+
- **Add words** — Single word or comma-separated bulk input
|
|
103
|
+
- **Import** — Paste a list of words (one per line or comma-separated)
|
|
104
|
+
- **Export** — Download all dictionary words as a `.txt` file
|
|
105
|
+
- **Search & filter** — Find words in the dictionary
|
|
106
|
+
- **Bulk delete** — Select and remove multiple words at once
|
|
107
|
+
- **Merged sources** — Config `customDictionary` (defaults) + DB dictionary (dynamic)
|
|
108
|
+
- **5-min cache** — Dictionary loaded from DB with in-memory TTL cache
|
|
109
|
+
- **Auto-schema** — Plugin auto-creates missing DB columns on init (SQLite/Postgres)
|
|
110
|
+
|
|
111
|
+
### Lexical JSON Support
|
|
112
|
+
|
|
113
|
+
- **Recursive extraction** — Traverses Lexical AST to extract plain text
|
|
114
|
+
- **Code block skip** — Ignores code blocks (not natural language)
|
|
115
|
+
- **Offset-based fixes** — Precise corrections using LanguageTool offsets (v0.8.0+)
|
|
116
|
+
- **Legacy fallback** — Substring search for backwards compatibility
|
|
117
|
+
- **Multi-field** — Extracts from hero, content, layout blocks, columns
|
|
118
|
+
|
|
119
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
120
|
+
|
|
121
|
+
## Installation
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# npm
|
|
125
|
+
npm install @consilioweb/spellcheck
|
|
126
|
+
|
|
127
|
+
# pnpm
|
|
128
|
+
pnpm add @consilioweb/spellcheck
|
|
129
|
+
|
|
130
|
+
# yarn
|
|
131
|
+
yarn add @consilioweb/spellcheck
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Peer Dependency | Version |
|
|
135
|
+
|----------------|---------|
|
|
136
|
+
| `payload` | `^3.0.0` |
|
|
137
|
+
| `@payloadcms/next` | `^3.0.0` |
|
|
138
|
+
| `@payloadcms/ui` | `^3.0.0` |
|
|
139
|
+
| `react` | `^18.0.0 \|\| ^19.0.0` |
|
|
140
|
+
|
|
141
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
142
|
+
|
|
143
|
+
## Quick Start
|
|
144
|
+
|
|
145
|
+
Add the plugin to your Payload config:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// src/plugins/index.ts (or payload.config.ts)
|
|
149
|
+
import { spellcheckPlugin } from '@consilioweb/spellcheck'
|
|
150
|
+
|
|
151
|
+
export default buildConfig({
|
|
152
|
+
plugins: [
|
|
153
|
+
spellcheckPlugin({
|
|
154
|
+
collections: ['pages', 'posts'],
|
|
155
|
+
language: 'fr',
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then regenerate the import map:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npx payload generate:importmap
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
That's it! The plugin automatically:
|
|
168
|
+
- Creates `spellcheck-results` and `spellcheck-dictionary` collections (hidden from admin nav)
|
|
169
|
+
- Registers API endpoints (`validate`, `fix`, `bulk`, `status`, `dictionary`)
|
|
170
|
+
- Adds a sidebar field to your target collections
|
|
171
|
+
- Creates a dashboard view at `/admin/spellcheck` (Results + Dictionary tabs)
|
|
172
|
+
- Adds an `afterChange` hook for auto-checking on save
|
|
173
|
+
- Auto-fixes missing DB columns on init (SQLite/Postgres `push:true` compatibility)
|
|
174
|
+
|
|
175
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
176
|
+
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
spellcheckPlugin({
|
|
181
|
+
// Target collections (default: ['pages', 'posts'])
|
|
182
|
+
collections: ['pages', 'posts'],
|
|
183
|
+
|
|
184
|
+
// Rich text field name (default: 'content')
|
|
185
|
+
contentField: 'content',
|
|
186
|
+
|
|
187
|
+
// LanguageTool language (default: 'fr')
|
|
188
|
+
language: 'fr',
|
|
189
|
+
|
|
190
|
+
// Auto-check on save (default: true)
|
|
191
|
+
checkOnSave: true,
|
|
192
|
+
|
|
193
|
+
// Sidebar field in editor (default: true)
|
|
194
|
+
addSidebarField: true,
|
|
195
|
+
|
|
196
|
+
// Dashboard view at /admin/spellcheck (default: true)
|
|
197
|
+
addDashboardView: true,
|
|
198
|
+
|
|
199
|
+
// Base path for API endpoints (default: '/spellcheck')
|
|
200
|
+
endpointBasePath: '/spellcheck',
|
|
201
|
+
|
|
202
|
+
// ── Filtering ──────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
// LanguageTool rule IDs to skip
|
|
205
|
+
skipRules: ['FR_SPELLING_RULE', 'WHITESPACE_RULE'],
|
|
206
|
+
|
|
207
|
+
// LanguageTool categories to skip
|
|
208
|
+
skipCategories: ['TYPOGRAPHY', 'STYLE'],
|
|
209
|
+
|
|
210
|
+
// Words to never flag as errors
|
|
211
|
+
customDictionary: [
|
|
212
|
+
'Next.js', 'Payload', 'TypeScript', 'SEO',
|
|
213
|
+
'Corrèze', 'Limoges', 'ConsilioWEB',
|
|
214
|
+
],
|
|
215
|
+
|
|
216
|
+
// Minimum score threshold for warnings (default: 80)
|
|
217
|
+
warningThreshold: 80,
|
|
218
|
+
|
|
219
|
+
// ── Claude AI Fallback (optional) ──────────────────
|
|
220
|
+
|
|
221
|
+
// Enable semantic analysis via Claude (default: false)
|
|
222
|
+
enableAiFallback: false,
|
|
223
|
+
|
|
224
|
+
// Anthropic API key (required if enableAiFallback is true)
|
|
225
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Options Reference
|
|
230
|
+
|
|
231
|
+
| Option | Type | Default | Description |
|
|
232
|
+
|--------|------|---------|-------------|
|
|
233
|
+
| `collections` | `string[]` | `['pages', 'posts']` | Collections to check |
|
|
234
|
+
| `contentField` | `string` | `'content'` | Rich text field name |
|
|
235
|
+
| `language` | `string` | `'fr'` | LanguageTool language code |
|
|
236
|
+
| `checkOnSave` | `boolean` | `true` | Auto-check on document save |
|
|
237
|
+
| `addSidebarField` | `boolean` | `true` | Add sidebar field in editor |
|
|
238
|
+
| `addDashboardView` | `boolean` | `true` | Add `/admin/spellcheck` view |
|
|
239
|
+
| `endpointBasePath` | `string` | `'/spellcheck'` | Base path for API endpoints |
|
|
240
|
+
| `enableAiFallback` | `boolean` | `false` | Enable Claude AI semantic analysis |
|
|
241
|
+
| `anthropicApiKey` | `string` | — | Anthropic API key for Claude |
|
|
242
|
+
| `skipRules` | `string[]` | `[]` | LanguageTool rule IDs to skip |
|
|
243
|
+
| `skipCategories` | `string[]` | `[]` | LanguageTool categories to skip |
|
|
244
|
+
| `customDictionary` | `string[]` | `[]` | Words to never flag |
|
|
245
|
+
| `warningThreshold` | `number` | `80` | Score below which a warning is shown |
|
|
246
|
+
|
|
247
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
248
|
+
|
|
249
|
+
## Admin Views
|
|
250
|
+
|
|
251
|
+
### Dashboard (`/admin/spellcheck`)
|
|
252
|
+
|
|
253
|
+
The dashboard provides a complete overview of your content's spelling quality:
|
|
254
|
+
|
|
255
|
+
- **Summary cards** — Document count, average score, total issues, error-free count
|
|
256
|
+
- **Sortable table** — Click column headers to sort by score, issues, words, date
|
|
257
|
+
- **Expandable rows** — Click any row to see detailed issues with context and suggestions
|
|
258
|
+
- **Bulk scan** — "Scanner tout" analyzes all published documents sequentially
|
|
259
|
+
- **One-click fix** — Apply a correction directly from the expanded issue view
|
|
260
|
+
|
|
261
|
+
### Sidebar Field
|
|
262
|
+
|
|
263
|
+
The sidebar field appears in the editor for every target collection:
|
|
264
|
+
|
|
265
|
+
- **Score badge** — Color-coded (green ≥95, yellow ≥80, red <80)
|
|
266
|
+
- **Stats bar** — Word count, issue count, last check time
|
|
267
|
+
- **Issue cards** — Each issue shows message, context with highlighted error, suggestion
|
|
268
|
+
- **Fix button** — Applies the suggestion directly in the Lexical JSON
|
|
269
|
+
- **Ignore button** — Removes the issue from the current view
|
|
270
|
+
|
|
271
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
272
|
+
|
|
273
|
+
## API Endpoints
|
|
274
|
+
|
|
275
|
+
All endpoints require authentication (Payload admin user).
|
|
276
|
+
|
|
277
|
+
| Endpoint | Method | Description |
|
|
278
|
+
|----------|--------|-------------|
|
|
279
|
+
| `/api/spellcheck/validate` | `POST` | Check a single document or raw text |
|
|
280
|
+
| `/api/spellcheck/fix` | `POST` | Apply a correction in Lexical JSON (offset-based) |
|
|
281
|
+
| `/api/spellcheck/bulk` | `POST` | Scan all documents (sequential, rate-limited) |
|
|
282
|
+
| `/api/spellcheck/status` | `GET` | Get current bulk scan progress |
|
|
283
|
+
| `/api/spellcheck/dictionary` | `GET` | List all dictionary words |
|
|
284
|
+
| `/api/spellcheck/dictionary` | `POST` | Add word(s) to dictionary |
|
|
285
|
+
| `/api/spellcheck/dictionary` | `DELETE` | Remove word(s) from dictionary |
|
|
286
|
+
|
|
287
|
+
### POST `/api/spellcheck/validate`
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
// Check a document by ID
|
|
291
|
+
{ "id": "123", "collection": "pages" }
|
|
292
|
+
|
|
293
|
+
// Check raw text
|
|
294
|
+
{ "text": "Ceci est une test.", "language": "fr" }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Response:**
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"docId": "123",
|
|
302
|
+
"collection": "pages",
|
|
303
|
+
"score": 85,
|
|
304
|
+
"issueCount": 2,
|
|
305
|
+
"wordCount": 450,
|
|
306
|
+
"issues": [
|
|
307
|
+
{
|
|
308
|
+
"ruleId": "GRAMMAR",
|
|
309
|
+
"category": "GRAMMAR",
|
|
310
|
+
"message": "Le déterminant « une » ne correspond pas...",
|
|
311
|
+
"context": "Ceci est une test.",
|
|
312
|
+
"original": "une",
|
|
313
|
+
"replacements": ["un"],
|
|
314
|
+
"source": "languagetool"
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
"lastChecked": "2025-02-22T20:30:00.000Z"
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### POST `/api/spellcheck/fix`
|
|
322
|
+
|
|
323
|
+
```json
|
|
324
|
+
{
|
|
325
|
+
"id": "123",
|
|
326
|
+
"collection": "pages",
|
|
327
|
+
"original": "une test",
|
|
328
|
+
"replacement": "un test",
|
|
329
|
+
"offset": 42,
|
|
330
|
+
"length": 8
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
> `offset` and `length` enable precise offset-based targeting (v0.8.0+). Falls back to substring search if omitted.
|
|
335
|
+
|
|
336
|
+
### GET `/api/spellcheck/dictionary`
|
|
337
|
+
|
|
338
|
+
**Response:**
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{ "words": [{ "id": "1", "word": "typescript", "addedBy": { "email": "admin@example.com" }, "createdAt": "..." }], "count": 1 }
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### POST `/api/spellcheck/dictionary`
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
// Single word
|
|
348
|
+
{ "word": "TypeScript" }
|
|
349
|
+
|
|
350
|
+
// Multiple words
|
|
351
|
+
{ "words": ["TypeScript", "Next.js", "Payload"] }
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### DELETE `/api/spellcheck/dictionary`
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
// Single
|
|
358
|
+
{ "id": "abc123" }
|
|
359
|
+
|
|
360
|
+
// Multiple
|
|
361
|
+
{ "ids": ["abc123", "def456"] }
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### POST `/api/spellcheck/bulk`
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
// Scan all configured collections
|
|
368
|
+
{}
|
|
369
|
+
|
|
370
|
+
// Scan a specific collection
|
|
371
|
+
{ "collection": "posts" }
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
375
|
+
|
|
376
|
+
## Engine
|
|
377
|
+
|
|
378
|
+
### Text Extraction
|
|
379
|
+
|
|
380
|
+
The plugin extracts text from Payload documents by recursively traversing:
|
|
381
|
+
|
|
382
|
+
1. **Title** — Document title
|
|
383
|
+
2. **Hero** — `hero.richText` (Lexical JSON)
|
|
384
|
+
3. **Content** — Main content field (Lexical JSON)
|
|
385
|
+
4. **Layout blocks** — Each block's `richText` and `columns[].richText`
|
|
386
|
+
|
|
387
|
+
Code blocks are automatically skipped (not natural language).
|
|
388
|
+
|
|
389
|
+
### LanguageTool
|
|
390
|
+
|
|
391
|
+
- **API**: `POST https://api.languagetool.org/v2/check` (free, no auth)
|
|
392
|
+
- **Limit**: 18,000 characters per request (auto-truncated)
|
|
393
|
+
- **Rate**: 3-second delay between bulk requests
|
|
394
|
+
- **Timeout**: 30 seconds per request
|
|
395
|
+
|
|
396
|
+
### Filtering
|
|
397
|
+
|
|
398
|
+
Issues are filtered through multiple layers:
|
|
399
|
+
|
|
400
|
+
1. **Premium rules** — Skipped (free API only)
|
|
401
|
+
2. **Configured rules** — `skipRules` option
|
|
402
|
+
3. **Configured categories** — `skipCategories` option
|
|
403
|
+
4. **Custom dictionary** — Case-insensitive word matching
|
|
404
|
+
5. **Single-character** — Skipped (often punctuation false positives)
|
|
405
|
+
|
|
406
|
+
### Scoring
|
|
407
|
+
|
|
408
|
+
Score = `max(0, 100 - (issues / words * 1000))`
|
|
409
|
+
|
|
410
|
+
- **100** — No issues
|
|
411
|
+
- **90+** — Excellent (green)
|
|
412
|
+
- **80+** — Good (yellow)
|
|
413
|
+
- **<80** — Needs work (red)
|
|
414
|
+
|
|
415
|
+
### Claude AI (Optional)
|
|
416
|
+
|
|
417
|
+
When `enableAiFallback: true`, the plugin also sends text to Claude Haiku for:
|
|
418
|
+
|
|
419
|
+
- Inconsistent tone or register
|
|
420
|
+
- Incoherent statements or contradictions
|
|
421
|
+
- Awkward phrasing
|
|
422
|
+
- Missing words that change meaning
|
|
423
|
+
|
|
424
|
+
Claude issues are tagged with `source: 'claude'` and category `COHERENCE`, `TONE`, `PHRASING`, or `MISSING_WORD`.
|
|
425
|
+
|
|
426
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
427
|
+
|
|
428
|
+
## Collections
|
|
429
|
+
|
|
430
|
+
The plugin auto-creates two collections:
|
|
431
|
+
|
|
432
|
+
| Collection | Slug | Description |
|
|
433
|
+
|------------|------|-------------|
|
|
434
|
+
| SpellCheck Results | `spellcheck-results` | Stores check results per document |
|
|
435
|
+
| SpellCheck Dictionary | `spellcheck-dictionary` | Dynamic dictionary (one doc per word) |
|
|
436
|
+
|
|
437
|
+
**Results fields**: `docId`, `collection`, `title`, `slug`, `score`, `issueCount`, `wordCount`, `issues` (JSON), `lastChecked`
|
|
438
|
+
|
|
439
|
+
**Dictionary fields**: `word` (text, unique, indexed), `addedBy` (relationship to users)
|
|
440
|
+
|
|
441
|
+
Both collections are hidden from the admin nav. The dictionary is managed via the Dashboard's "Dictionnaire" tab or the REST API.
|
|
442
|
+
|
|
443
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
444
|
+
|
|
445
|
+
## Package Exports
|
|
446
|
+
|
|
447
|
+
### Main Entry (`@consilioweb/spellcheck`)
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Plugin
|
|
451
|
+
export { spellcheckPlugin } from './plugin'
|
|
452
|
+
|
|
453
|
+
// Types
|
|
454
|
+
export type { SpellCheckPluginConfig, SpellCheckIssue, SpellCheckResult } from './types'
|
|
455
|
+
|
|
456
|
+
// Engine (for programmatic use)
|
|
457
|
+
export { extractTextFromLexical, countWords } from './engine/lexicalParser'
|
|
458
|
+
export { checkWithLanguageTool } from './engine/languagetool'
|
|
459
|
+
export { checkWithClaude } from './engine/claude'
|
|
460
|
+
export { filterFalsePositives, calculateScore } from './engine/filters'
|
|
461
|
+
|
|
462
|
+
// Dictionary cache
|
|
463
|
+
export { loadDictionaryWords, invalidateDictionaryCache } from './endpoints/dictionary'
|
|
464
|
+
|
|
465
|
+
// i18n
|
|
466
|
+
export { getTranslations, getScoreLabel } from './i18n'
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Client Entry (`@consilioweb/spellcheck/client`)
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
export { SpellCheckField } from './components/SpellCheckField'
|
|
473
|
+
export { SpellCheckDashboard } from './components/SpellCheckDashboard'
|
|
474
|
+
export { IssueCard } from './components/IssueCard'
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Views Entry (`@consilioweb/spellcheck/views`)
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
export { SpellCheckView } from './views/SpellCheckView'
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
484
|
+
|
|
485
|
+
## Requirements
|
|
486
|
+
|
|
487
|
+
- **Node.js** >= 18
|
|
488
|
+
- **Payload CMS** 3.x
|
|
489
|
+
- **React** 18.x or 19.x
|
|
490
|
+
- **Any Payload DB adapter** (SQLite, PostgreSQL, MongoDB)
|
|
491
|
+
|
|
492
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
493
|
+
|
|
494
|
+
## Uninstall
|
|
495
|
+
|
|
496
|
+
### Automatic (recommended)
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
npx spellcheck-uninstall
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
This will:
|
|
503
|
+
1. Remove all `@consilioweb/spellcheck` imports and plugin calls from your source files
|
|
504
|
+
2. Drop the `spellcheck_results` table and indexes from your database
|
|
505
|
+
3. Remove the npm package
|
|
506
|
+
4. Regenerate the import map
|
|
507
|
+
|
|
508
|
+
> Use `--keep-data` to preserve the database table.
|
|
509
|
+
|
|
510
|
+
### Manual
|
|
511
|
+
|
|
512
|
+
1. Remove the plugin from your config
|
|
513
|
+
2. Run `npx payload generate:importmap`
|
|
514
|
+
3. (Optional) Drop the database table:
|
|
515
|
+
|
|
516
|
+
```sql
|
|
517
|
+
-- SQLite
|
|
518
|
+
DROP INDEX IF EXISTS `spellcheck_results_doc_id_idx`;
|
|
519
|
+
DROP INDEX IF EXISTS `spellcheck_results_collection_idx`;
|
|
520
|
+
DROP INDEX IF EXISTS `spellcheck_results_last_checked_idx`;
|
|
521
|
+
DROP TABLE IF EXISTS `spellcheck_results`;
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
525
|
+
|
|
526
|
+
## Changelog
|
|
527
|
+
|
|
528
|
+
### v0.8.1
|
|
529
|
+
|
|
530
|
+
- **Fix**: Corrections now remove the issue from the UI immediately (optimistic update)
|
|
531
|
+
- **Fix**: "Ignorer" persists in DB across page reloads (was local state only)
|
|
532
|
+
- **Fix**: Auto-fix missing DB columns on init (`push:true` compatibility for SQLite/Postgres)
|
|
533
|
+
|
|
534
|
+
### v0.8.0
|
|
535
|
+
|
|
536
|
+
- **New**: Dynamic dictionary — manage words from the admin dashboard (add/remove/import/export)
|
|
537
|
+
- **New**: `spellcheck-dictionary` collection (one document per word, merged with config dictionary)
|
|
538
|
+
- **New**: Dictionary REST API (GET/POST/DELETE at `/api/spellcheck/dictionary`)
|
|
539
|
+
- **New**: Offset-based corrections — precise fix targeting using LanguageTool offsets
|
|
540
|
+
- **New**: "Add to dictionary" button on issue cards (+ Dico)
|
|
541
|
+
- **New**: "Ignore" button to dismiss false positives
|
|
542
|
+
- **New**: Dictionary tab in the dashboard with search, bulk delete, import/export
|
|
543
|
+
- **New**: In-memory cache (5-min TTL) for dictionary DB queries
|
|
544
|
+
- **Changed**: `filterFalsePositives` is now async (merges config + DB dictionaries)
|
|
545
|
+
- **Changed**: Fix endpoint accepts `offset` and `length` parameters (falls back to substring search)
|
|
546
|
+
|
|
547
|
+
### v0.5.0 — v0.7.0
|
|
548
|
+
|
|
549
|
+
- Custom dictionary config, contextual multi-word filtering, background scan
|
|
550
|
+
- Lexical ghost space fix, extended French dictionary
|
|
551
|
+
- Contextual offset, manual edit input, repetition filter
|
|
552
|
+
|
|
553
|
+
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" alt="-----" />
|
|
554
|
+
|
|
555
|
+
## License
|
|
556
|
+
|
|
557
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
558
|
+
|
|
559
|
+
<p align="center">
|
|
560
|
+
<br />
|
|
561
|
+
Made with ❤️ by <a href="https://consilioweb.fr">ConsilioWEB</a>
|
|
562
|
+
<br />
|
|
563
|
+
<br />
|
|
564
|
+
<a href="https://www.linkedin.com/in/christophe-lopez-dev/"><img src="https://img.shields.io/badge/LinkedIn-0A66C2?logo=linkedin&logoColor=white" alt="LinkedIn" /></a>
|
|
565
|
+
<a href="https://github.com/pOwn3d"><img src="https://img.shields.io/badge/GitHub-181717?logo=github&logoColor=white" alt="GitHub" /></a>
|
|
566
|
+
<a href="https://consilioweb.fr"><img src="https://img.shields.io/badge/Web-consilioweb.fr-2563eb" alt="Website" /></a>
|
|
567
|
+
</p>
|