The plan said "Hunspell pt-PT vendored like en-US". Measuring that first is
what saved it: nspell expands affixes eagerly on construction, and European
Portuguese's 1,340 rules over 44,257 stems want over a gigabyte of browser
heap — ~340 MB for the first 12,000 entries, and no return at all after three
minutes on the whole file. So the expansion runs once at build time instead:
1,039,058 forms, 2.66 MB gzipped, read by the same nspell in 842 ms.
The obvious npm package would also have shipped the wrong language. Both
dictionary-pt and dictionary-pt-br carry VERO, the Brazilian word list, so
vendoring by name puts pt-BR spellings behind a pt-PT label — the drift
SUGGESTIONS §3 warns about, arriving through the packaging where no reviewer
can see it. The source is Projecto Natura's, and the build script now asserts
the fault lines (receção in, recepção out) before writing anything.
Spellcheck consults both dictionaries and flags only what both reject, which
is the no-detector answer to a pair with no script boundary. The word card
does the same in the other direction: "data" is a word in both languages, so
Petal shows both readings rather than guessing which she meant.
Writing the tests caught the one real bug — extendedAlphabet was a snapshot
while correct/suggest read live, and her dictionary arrives after English, so
every lookup would have resolved "cora" while the underlines were already
right.
Not done, and not claimed: the pack has not been read by a pt-PT speaker, and
the Piper voice is deferred with the deploy.
Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
Vendored Hunspell en.aff/en.dic into web/public/dictionaries/en/ (served as a
static asset + embedded in the binary, kept out of the JS bundle). dictionary-en
moved to a devDep — only used to source the files.
- useSpellChecker (App-level, loads once/session): fetches the dict, builds an
nspell instance, replays a localStorage personal word list; addWord persists
and bumps a version so consumers re-decorate. Ambient types in
src/types/nspell.d.ts (the package ships none).
- SpellCheck Tiptap extension: misspellings as ProseMirror decorations (no
stored marks), recomputed on edit / caret move / checker swap. Latin-only
tokenizer so CJK is never flagged; skips short tokens + all-caps acronyms;
exempts the caret word to avoid mid-typing jitter. Reuses mapOffset (now
exported from SuggestionHighlight); wordAt resolves the exact span on click.
- MisspellCard: soft rose wavy underline, bilingual popover with up to 5 nspell
corrections (click to replace) + add-to-dictionary. Closes on outside-pointer,
edit, or doc switch.
Chinese spell check intentionally omitted — nspell is dictionary-based and
English-only; Chinese typos (homophone 别字) need an LLM, out of v1 scope.
tsc/vite/go build+vet clean; live server serves both dict files; nspell
behavior smoke-tested.
Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd