dsh-spell-check 0.0.0-stage → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adithya N Raj
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 CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="assets/banner-dark.png">
3
+ <img alt="dsh-spell-check" src="assets/banner-light.png" width="1012">
4
+ </picture>
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ <br>
7
+
8
+ # dsh-spell-check
9
+
10
+ English | [中文](README.zh-CN.md)
11
+
12
+ ![language](https://img.shields.io/badge/language-javascript-f7dc6f) ![license](https://img.shields.io/badge/license-MIT-blue) ![node](https://img.shields.io/badge/node-%E2%89%A5%2022-brightgreen) ![dsh](https://img.shields.io/badge/dsh-web%20profile-orange) ![runtime](https://img.shields.io/badge/runtime-offline_%C2%B7_100%25_local-brightgreen) ![tests](https://img.shields.io/badge/tests-128%20checks-brightgreen)
13
+
14
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that spell-checks the composer the way Chrome does: the word you are typing is checked as you type it, and fixing it never takes your hands off the keyboard.
15
+
16
+ - **A strip above the caret** while you type, offering the correction. **Tab** accepts it. Escape dismisses, the arrow keys move between alternatives, `1`–`9` jump straight to one, and clicking an entry works too.
17
+ - **Right-click any word** for a menu with every ranked alternative plus *Add to dictionary* — the same list, on the word you pointed at rather than the one behind the caret.
18
+ - **Nothing leaves your machine.** No network calls, no telemetry, no model. The dictionary ships with the plugin.
19
+
20
+ Everything is local: 20,000 frequency-ranked English words plus ~730 curated misspellings, matched with Damerau-Levenshtein so `atuo → auto`, `theh → the` and `recieve → receive` all count as one edit rather than two.
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ dsh plugin --profile web add dsh-spell-check
26
+ ```
27
+
28
+ Restart dsh. The checker is on from then on — there is no chip, no toggle, and nothing added to the composer bar.
29
+
30
+ ### In the desktop app
31
+
32
+ Open **Plugins** in the sidebar, click **Add plugin**, paste the repository address and install:
33
+
34
+ ```
35
+ https://github.com/adithyanraj03/dsh-spell-check
36
+ ```
37
+
38
+ ## What you get
39
+
40
+ ### Type, then press Tab
41
+
42
+ The word under the caret is checked as you type. When it is wrong, a strip appears directly above it:
43
+
44
+ ![Typing, the suggestion strip appearing, and Tab accepting it](assets/strip.gif)
45
+
46
+ **Tab** replaces the word and leaves the caret after it, so a second Tab corrects the *next* word rather than repeating the last one. The strip stays put while your pointer is over it, so it can be clicked as well as tabbed to.
47
+
48
+ Misspelled words in the draft carry a blue wavy underline — the plugin's own marking, with Chromium's red squiggles switched off, so there is exactly one spellchecker in the box:
49
+
50
+ ![Blue wavy underlines under the misspelled words](assets/strip.png)
51
+
52
+ ### Right-click a word
53
+
54
+ For any word in the draft, right-click opens the full list — including words that are further away than the one you would tab to:
55
+
56
+ ![The right-click menu with ranked suggestions and Add to dictionary](assets/menu.png)
57
+
58
+ Picking a word replaces it in place. The menu appears **only** for a misspelled word that has suggestions; everywhere else the normal menu still opens, so Cut/Copy/Paste are never taken away.
59
+
60
+ ### What it deliberately ignores
61
+
62
+ - **Real words.** `hoe` is not corrected to `how`, `form` is not corrected to `from`. A word in the dictionary is left alone — that rule is what separates this from the autocorrect people switch off.
63
+ - **Acronyms**, `ALL_CAPS` tokens, and anything containing digits.
64
+ - **Code.** Inside a fenced ```` ``` ```` block in the draft, the checker stays silent.
65
+ - **`@mentions` and `/commands`**, because a token has to start with a letter to be a word.
66
+
67
+ ### Learned words
68
+
69
+ *Add to dictionary* teaches the local engine immediately. The word is also handed to the host half, which persists it under `~/.dsh/storages/spell-check.json` so it survives a restart.
70
+
71
+ ## Configuration
72
+
73
+ Nothing to configure. There is no row to edit and no flag that turns off a feature you might want.
74
+
75
+ ## Requirements
76
+
77
+ | dsh | status |
78
+ |---|---|
79
+ | `0.2.1-rc.3` | tested — embedded dictionary, strip, Tab, right-click menu and learned words |
80
+ | `0.2.0` | expected to work; the client half only needs `slots`, `conversation` and `sessions` |
81
+
82
+ - Node 22+
83
+
84
+ ## Changing the dictionary
85
+
86
+ The word list is data, not code. Drop a plain text file at `assets/words-extra.txt` — one word per line — and run `npm run build`. Common `SCOWL`, `Hunspell` (`en_US.dic`) and `wordfreq` exports all reduce to that shape, so any of them can be plugged in without touching the source. Lines with an affix marker (`walk/ed`) are reduced to the headword.
87
+
88
+ Options, and what each costs:
89
+
90
+ | Source | Licence | Size | Trade-off |
91
+ |---|---|---|---|
92
+ | current list (nltk corpora, frequency-ranked) | same as this repo | 20,000 | fast, tuned for typos, catches `wether → whether` |
93
+ | [SCOWL](http://wordlist.aspell.net/) size 60 | permissive, mixed | ~95,000 | far better coverage of real words, but rare-valid words stop being corrected (`wether` is a real word: a castrated ram) |
94
+ | [Hunspell](https://github.com/wooorm/nspell) `en_US` + `nspell` | LGPL/MPL (dictionary), MIT (nspell) | ~50,000 stems + affixes | closest to what Chrome actually uses; needs an affix engine, so it is a dependency rather than a word list |
95
+ | [wordfreq](https://github.com/rspeer/wordfreq) top-N | Apache-2.0 | any | best frequency ordering, which is what decides which suggestion is offered first |
96
+ | [google-10000-english](https://github.com/first20hours/google-10000-english) | public domain derivative | 10,000 | tiny and frequency-ordered; what people usually mean by "Google's list" |
97
+
98
+ A bigger dictionary is not automatically better here. Every valid word you add is a typo the checker will stop offering, so the useful expansion is usually **more misspellings** (the curated map in [src/engine.js](src/engine.js)), not more words.
99
+
100
+ ## How it works
101
+
102
+ The browser half holds the whole engine. `scripts/build-client.mjs` strips the `export` keywords from [src/engine.js](src/engine.js) and splices it, together with the word list, into [src/client.js](src/client.js) — so there is one copy of the matching rules and no round trip per keystroke. The host half imports that same file and exists only to persist learned words.
103
+
104
+ Two things about this composer are worth knowing if you extend it:
105
+
106
+ - **It is Lexical, and typing does not emit an `input` event.** Lexical calls `preventDefault()` on `beforeinput`, so the plugin listens to `selectionchange`, `beforeinput` and `keyup` instead. A listener on `input` alone is silently deaf.
107
+ - **`execCommand` cannot be trusted to replace text.** Lexical applies edits against its own selection, not the DOM range the page hands it, so a `insertText` aimed at a word lands at the start of the draft. Replacements go through the composer's own API — `conversation.input.for(scope).setDraft(text)` — which sets the model directly.
108
+
109
+ ## Tests
110
+
111
+ ```sh
112
+ npm test
113
+ ```
114
+
115
+ 129 checks, no network and no browser required:
116
+
117
+ | suite | covers |
118
+ |---|---|
119
+ | `scripts/check-styles.mjs` | the stylesheet is composed of well-formed rules — an orphaned declaration silently kills every rule after it |
120
+ | `scripts/test-engine.mjs` | matching, ranking, case handling, acronym and code exclusion, no duplicate suggestions |
121
+ | `scripts/test-host.mjs` | the shipped dictionary loads, the service answers, learned words persist |
122
+ | `scripts/test-client.mjs` | the bundle loads through dsh's module loader, registers one seat, attaches and detaches its listeners, and renders nothing in the composer bar |
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,110 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="assets/banner-dark.png">
3
+ <img alt="dsh-spell-check" src="assets/banner-light.png" width="1012">
4
+ </picture>
5
+
6
+ <br>
7
+
8
+ # dsh-spell-check
9
+
10
+ [English](README.md) | 中文
11
+
12
+ ![language](https://img.shields.io/badge/language-javascript-f7dc6f) ![license](https://img.shields.io/badge/license-MIT-blue) ![node](https://img.shields.io/badge/node-%E2%89%A5%2022-brightgreen) ![dsh](https://img.shields.io/badge/dsh-web%20profile-orange) ![runtime](https://img.shields.io/badge/runtime-offline_%C2%B7_100%25_local-brightgreen) ![tests](https://img.shields.io/badge/tests-129%20checks-brightgreen)
13
+
14
+ 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件,像 Chrome 那样给输入框做拼写检查:正在输入的词会边打边查,改错时手不用离开键盘。
15
+
16
+ - **光标上方出现建议条**,**Tab** 接受它。Esc 关闭,方向键在候选中移动,`1`–`9` 直接选中,也可以鼠标点击。
17
+ - **右键任意单词**,弹出按相关度排序的全部候选,外加「加入词典」——同一份列表,作用在你指的那个词上,而不是光标后面的词。
18
+ - **一切都在本机完成。** 没有网络请求,没有遥测,没有模型。词典随插件一起发布。
19
+
20
+ 词典是 20,000 个按词频排序的英文词,加上约 730 条手工整理的常见拼写错误;匹配用 Damerau-Levenshtein 距离,所以 `atuo → auto`、`theh → the`、`recieve → receive` 都只算一次编辑,而不是两次。
21
+
22
+ ## 安装
23
+
24
+ ```sh
25
+ dsh plugin --profile web add dsh-spell-check
26
+ ```
27
+
28
+ 重启 dsh 即生效。没有开关,没有状态芯片,输入栏不会被加入任何东西。
29
+
30
+ ### 桌面版
31
+
32
+ 侧栏打开 **Plugins** → **Add plugin**,粘贴仓库地址后安装:
33
+
34
+ ```
35
+ https://github.com/adithyanraj03/dsh-spell-check
36
+ ```
37
+
38
+ ## 用法
39
+
40
+ ### 边打边查,Tab 接受
41
+
42
+ 光标所在的词会随输入被检查。判断为错误时,它上方会出现一条建议:
43
+
44
+ ![输入、建议条出现、Tab 接受](assets/strip.gif)
45
+
46
+ **Tab** 会替换该词,并把光标留在替换后的位置,所以紧接着再按一次 Tab 修的是**下一个**词,而不是重复上一个。鼠标移到建议条上时它不会消失,因此点击同样可用。
47
+
48
+ 草稿中的错词带有蓝色波浪下划线——这是插件自己的标记,Chromium 的红色波浪线已关闭,输入框里只有一套拼写检查:
49
+
50
+ ![错词下的蓝色波浪线](assets/strip.png)
51
+
52
+ ### 右键菜单
53
+
54
+ 对草稿中任意单词点右键,会列出完整的候选——包括比 Tab 那条更远的词:
55
+
56
+ ![右键菜单:排序后的候选与「加入词典」](assets/menu.png)
57
+
58
+ 只有当这个词拼错且存在候选时,菜单才会接管右键;其余情况仍是系统菜单,剪切/复制/粘贴不会被拿走。
59
+
60
+ ### 刻意不管的东西
61
+
62
+ - **正确的词。** `hoe` 不会被改成 `how`,`form` 不会被改成 `from`。词典里有的词一律不动——这条规则正是它和那种让人关掉的自动更正的区别。
63
+ - **缩写词**、全大写词,以及含数字的内容。
64
+ - **代码。** 草稿里围栏代码块(```` ``` ````)内部保持沉默。
65
+ - **`@提及` 和 `/命令`**,因为一个词必须以字母开头才算词。
66
+
67
+ ### 学过的词
68
+
69
+ 「加入词典」会立刻在本机引擎生效,同时交给宿主半边持久化到 `~/.dsh/storages/spell-check.json`,重启后仍然有效。
70
+
71
+ ## 换词典
72
+
73
+ 词表是数据,不是代码。把纯文本文件放到 `assets/words-extra.txt`(每行一个词)然后 `npm run build` 即可。`SCOWL`、`Hunspell`(`en_US.dic`)和 `wordfreq` 的导出格式都能直接放,带词缀标记的行(`walk/ed`)会取词干。
74
+
75
+ | 来源 | 许可 | 规模 | 代价 |
76
+ |---|---|---|---|
77
+ | 当前词表(nltk 语料,按词频排序) | 与本仓库相同 | 20,000 | 针对错字调过,能抓到 `wether → whether` |
78
+ | [SCOWL](http://wordlist.aspell.net/) size 60 | 宽松,混合 | 约 95,000 | 覆盖更多真词,但 `wether` **本身**是个真词(去势的公羊),于是不再被纠正 |
79
+ | Hunspell `en_US` + `nspell` | 词典 MPL/LGPL,nspell MIT | 约 50,000 词干 + 词缀 | 最接近 Chrome 实际使用的方案;需要词缀引擎,属于依赖而非词表 |
80
+ | [wordfreq](https://github.com/rspeer/wordfreq) 取前 N | Apache-2.0 | 任意 | 词频排序最好,而排序决定了推荐哪个候选 |
81
+
82
+ 词典更大不一定更好:每增加一个「合法的词」,就少纠正一类错字。真正值得扩充的通常是**错拼表**([src/engine.js](src/engine.js) 里手工维护的那份),而不是词表本身。
83
+
84
+ ## 实现要点
85
+
86
+ 浏览器半边自带整个引擎。`scripts/build-client.mjs` 去掉 [src/engine.js](src/engine.js) 里的 `export` 关键字,连同词表一起拼进 [src/client.js](src/client.js)——所以匹配规则只有一份,每次按键也不需要往返一次宿主。宿主半边 import 同一个文件,只负责持久化学过的词。
87
+
88
+ 这个输入框有两个坑,改这块代码前值得知道:
89
+
90
+ - **它是 Lexical,打字不会触发 `input` 事件。** Lexical 对 `beforeinput` 调了 `preventDefault()`,所以插件监听的是 `selectionchange`、`beforeinput` 和 `keyup`。只听 `input` 会安静地完全失效。
91
+ - **`execCommand` 不能用来替换文字。** Lexical 按它自己的选区应用编辑,不认页面给它的 DOM Range,于是针对某个词的 `insertText` 会插到草稿开头。替换走输入框自己的接口 `conversation.input.for(scope).setDraft(text)`,直接改模型。
92
+
93
+ ## 测试
94
+
95
+ ```sh
96
+ npm test
97
+ ```
98
+
99
+ 129 项检查,不需要网络也不需要浏览器:
100
+
101
+ | 套件 | 覆盖 |
102
+ |---|---|
103
+ | `scripts/check-styles.mjs` | 样式表由完整规则组成——一条落单的声明会让它之后的全部规则失效 |
104
+ | `scripts/test-engine.mjs` | 匹配、排序、大小写、缩写与代码排除、候选不重复 |
105
+ | `scripts/test-host.mjs` | 随包词典能加载、服务能回答、学过的词会持久化 |
106
+ | `scripts/test-client.mjs` | 通过 dsh 的模块加载器装载、只注册一个座位、监听器能挂上也能摘掉、输入栏里不渲染任何东西 |
107
+
108
+ ## 许可
109
+
110
+ MIT
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file