▍ humdrum codex / glint v1.0.2
license AGPL-3.0

← issues

TASK-020 Spellcheck with undercurl underlines

🏁 Done feature priority: medium release-1

created 2026-06-29 18:11 · updated 2026-06-30 01:45

Description

Lightweight inline spellcheck: red wavy (undercurl) underline under misspelled words, with one-key suggest-and-replace. Catch obvious typos only — not an intense/morphological checker. Must stay zero-cgo so the Homebrew/go-install build keeps working, and must not bloat the binary much.

RENDERING (undercurl)

DICTIONARY (lightweight, pure-Go)

SUGGEST & REPLACE

WHAT TO SKIP (no false positives)

PERSONAL DICTIONARY (easy add)

TOGGLE / DEFAULTS

PERF

Acceptance Criteria

Implementation Plan

Decisions: dict=common ~60k (Norvig-freq ∩ curated words_alpha, frequency-ranked, rejects common typos); key=single Alt+; popup hub (suggest/toggle/add/ignore); mouse click on flagged word also opens popup.

Slices (each TDD): A. internal/spell: embed words.txt.gz (60k, freq order) → Known(word) case-insensitive + possessive/plural leniency. B. internal/spell: BK-tree from dict → Suggest(word,max) ranked by edit-distance then frequency rank. C. internal/spell: personal dict ~/.config/glint/dict.txt load+Add (append+in-memory), Known unions it. D. editor Span.Wavy + theme.Spell red color; renderSpans/renderSpansCursor emit undercurl SGR (4:3 + 58:2 color), preserve markup-visible invariant; degrade gracefully. E. editor spellcheck pass: mark misspelled prose words Wavy; skip code fences/inline code/URLs/wikilinks/link targets/frontmatter values/headings markup + whole-doc when codeFile!=''; viewport-only + word→ok cache invalidated on add; session toggle. F. app popup mode (Alt+; + mouse click on flagged word): 5 suggestions + Toggle + Add + Ignore; arrows/number+Enter apply→replace word, dirty, clear underline; Esc dismiss. G. config spellcheck=auto|on|off; default ON md/txt/unnamed, OFF code (share TASK-018 ext map).

Implementation Notes

Slice A/B/C done (commit 2c0a77e9): internal/spell — 60k embedded dict (freq∩curated, rejects common typos), Known() w/ possessive leniency, BK-tree Suggest() OSA-reranked + freq tie-break, personal dict load/Add at ~/.config/glint/dict.txt. 11 tests green.

Slice D/E done (commits 92f9461e, +theme.Spell): undercurl Span rendering (raw SGR 4:3 + 58:2 color, invariant-preserving, graceful degrade), scanner Prose tagging, spellPass with skip rules (code/inline-code/URL/email/wikilink/link-target/frontmatter/acronym/camelcase/<3-char), word->ok cache, codeFile + toggle gates, AddToDictionary clears cache live. 13 editor tests green.

Slice F/G done (commits 1221e42d, 1d757ec1): config spellcheck=auto|on|off + DictPath, dict loaded at app startup (embedded + personal), Alt+; + mouse-click-on-flagged-word open the popup (suggestions 1-9 / a Add / i Ignore / t Toggle, Esc), toggle-only popup when nothing flagged, glint -c/-h/help-overlay/README/CHANGELOG documented incl. tmux undercurl passthrough. Smoke test confirms App.View emits 4:3+58:2 SGR. All 7 ACs met. Full suite + vet + build green; binary +~250KB (embedded gz).