The plugin used to call dictionaryapi.dev for every lookup, which was
slow, rate-limited, and broke offline. It now ships a local SQLite
file (~23MB, ~167K English entries from Wiktionary via the
MattDodsonEnglish/english-dictionary project) and queries it locally
via sql.js.
Plumbing:
- DictionaryStore loads the SQLite from
<vault>/.obsidian/plugins/lexophile/dictionary.sqlite, downloads
on demand from a configurable URL (default: a release asset on this
repo), and exposes a synchronous lookup() that returns a WordEntry
or null.
- lookupWord is now an async wrapper around store.lookup that throws
WordNotFoundError on null. DictionaryNotReadyError surfaces the
pre-download state so modals can fail fast with a helpful message.
- sql.js initialization moved to its own module (src/sqlite.ts) and
reused by both the Kobo import and the new dictionary store.
- AddWordModal / MassImportModal / KoboImportModal each take the
store via constructor and guard against the not-ready state before
starting work.
- Settings tab gains a "Local dictionary" section with a status pill
(Ready / Downloaded-not-loaded / Not downloaded), an entry count and
size summary, a Download/Re-download button, and a configurable URL.
- Removed the inter-request rate limit (was 350ms for API throttling) —
local lookups are sub-millisecond, so a 0ms yield is enough to let
the progress line repaint.
Bundled dictionary data is hosted as a release asset on
bryanmanio/obsidian-lexophile (tag: dictionary-v1). The plugin does
not call dictionaryapi.dev at any point during normal use. The Chrome
extension still uses the API for its own lookups — out of scope here.
Build artifacts:
- scripts/build-dictionary.py converts the upstream parquet (167,585
entries) to the compact SQLite schema we ship; documented for
reproducibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>