66 lines
4.4 KiB
Markdown
66 lines
4.4 KiB
Markdown
# CHUNK #7 — Spell & Grammar
|
|
|
|
**Status:** 🟡 In progress — this is the current work order.
|
|
|
|
**Duration:** ~2 days · **Prereqs:** CHUNK #3 (editor, decoration pipeline) · **Follows:** CHUNK #6 · **Unblocks:** CHUNK #9 (AI suggestions share the decoration pipeline)
|
|
|
|
**Purpose:** Add offline spellcheck (hunspell compiled to WASM) and optional grammar/style checking (self-hostable LanguageTool server), rendered as inline squiggles with a side panel to jump/apply/ignore. Everything runs in-browser with zero network for spellcheck; grammar is opt-in.
|
|
|
|
---
|
|
|
|
## Steps
|
|
|
|
### 1. Spellcheck engine (`packages/editor/src/spell/`)
|
|
1. **hunspell-WASM** (`packages/editor/src/spell/hunspell.ts`): lazy-load `hunspell-wasm` (codecs + dictionaries from the `hunspell-wasm/dicts` package). Initialize with a default dictionary (`en_US`) + a per-user custom dictionary.
|
|
2. **Dictionary management** (`packages/editor/src/spell/dicts.ts`):
|
|
- Load dictionaries on demand (lazy, per active language).
|
|
- **Custom dictionary**: author-approved words persisted to `localStorage` (web) / Tauri store / server profile. Words added here are never re-underlined.
|
|
- Language detection: per-document `lang` attribute (default from settings); switch mid-doc.
|
|
3. **Checker** (`packages/editor/src/spell/checker.ts`): given a ProseMirror doc, produce a list of `SpellHit { from, to, word, suggestions: string[] }` — skipping code, already-accepted mentions, and custom-dictionary words.
|
|
|
|
### 2. Grammar engine (`packages/editor/src/grammar/`)
|
|
1. **LanguageTool adapter** (`packages/editor/src/grammar/languagetool.ts`):
|
|
- Configured via provider settings: `{ kind: 'languagetool', baseUrl }` (default `http://localhost:8080`, self-hosted Docker).
|
|
- `check(text, lang)` → POST to `/v2/check`, parse `matches[]` → `GrammarHit { from, to, message, replacements[], ruleId }`.
|
|
- Debounced (800ms) + cached per text hash to avoid spamming the server.
|
|
2. **Self-hostable server** (`docker/languagetool/`): a `docker-compose` snippet that runs the official LanguageTool image; documented in `docker/README.md`. API is OpenAI-compatible-ish (documented endpoints).
|
|
|
|
### 3. Decoration integration (`packages/editor/src/decorations/`)
|
|
1. Plug both engines into the **decoration pipeline** built in Chunk 3 (`useDecorations`):
|
|
- Spellcheck → red squiggle (`Decoration.inline` with `class="ps-spell"`).
|
|
- Grammar → blue squiggle (`class="ps-grammar"`).
|
|
2. Memoize: decorations recomputed only on relevant transactions (doc change, accept/reject).
|
|
|
|
### 4. Side panel (`apps/web/src/features/check/CheckPanel.tsx`)
|
|
1. Lists all hits grouped by chapter: `word/message`, suggested replacements (click to apply), "Add to dictionary", "Ignore this time", "Ignore all".
|
|
2. **Jump**: clicking a hit scrolls the editor to that range and highlights it.
|
|
3. Counts: X spelling / Y grammar issues remaining; per-chapter breakdown.
|
|
|
|
### 5. Settings
|
|
1. Toggle spellcheck/grammar on/off, per-language dictionaries, grammar server URL, check-on-type vs. check-on-save.
|
|
|
|
### 6. Tests
|
|
1. Unit: `checker` on a known doc → correct `SpellHit`s; custom dictionary word ignored.
|
|
2. Unit: grammar adapter parses a mock LanguageTool response correctly.
|
|
3. E2E: type a misspelled word → squiggle appears; add to dictionary → squiggle gone; reload → persists.
|
|
4. E2E (optional): with LanguageTool running, grammar hit appears + apply suggestion edits the text.
|
|
|
|
---
|
|
|
|
## Acceptance criteria (all must pass)
|
|
- [ ] Misspelled word → red squiggle; suggestions shown in panel.
|
|
- [ ] "Add to dictionary" → word never underlined again; persists across reload.
|
|
- [ ] Grammar (if LanguageTool configured) → blue squiggle; apply suggestion edits text.
|
|
- [ ] Panel jumps to a hit and highlights it in the editor.
|
|
- [ ] Code blocks and mentions are never spellchecked.
|
|
- [ ] Toggle spellcheck off → no squiggles; setting persists.
|
|
- [ ] Contract suite still green on both adapters.
|
|
|
|
## Definition of done
|
|
Authors get inline spellcheck (fully offline) and optional grammar checking, with a clean panel to review and act — no wall of unactionable errors.
|
|
|
|
## Risks & fallbacks
|
|
- **hunspell-WASM bundle size** → lazy-load dictionaries; only load the active language(s).
|
|
- **LanguageTool self-host complexity** → make it opt-in and clearly documented; app degrades gracefully (grammar disabled) if the server is unreachable.
|
|
- **Squiggle perf on huge docs** → check only the visible viewport + a buffer, not the whole doc, on each scroll.
|