ep_spellcheck 0.0.97 → 0.0.99
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/AGENTS.md +71 -0
- package/ep.json +7 -4
- package/package.json +4 -1
- package/spellcheck.js +23 -15
- package/static/js/spellcheck.js +28 -44
- package/templates/spellcheck_entry.ejs +0 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Agent Guide — ep_spellcheck
|
|
2
|
+
|
|
3
|
+
Add support to do 'Spell checking', with a toggle on/off option in Settings.
|
|
4
|
+
|
|
5
|
+
## Tech stack
|
|
6
|
+
|
|
7
|
+
* Etherpad plugin framework (hooks declared in `ep.json`)
|
|
8
|
+
* EJS templates rendered server-side via `eejsBlock_*` hooks
|
|
9
|
+
* html10n for i18n (`locales/<lang>.json`, `data-l10n-id` in templates)
|
|
10
|
+
|
|
11
|
+
## Project structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
ep_spellcheck/
|
|
15
|
+
├── AGENTS.md
|
|
16
|
+
├── CONTRIBUTING.md
|
|
17
|
+
├── ep.json
|
|
18
|
+
├── locales/
|
|
19
|
+
│ ├── ar.json
|
|
20
|
+
│ ├── be-tarask.json
|
|
21
|
+
│ ├── bn.json
|
|
22
|
+
│ ├── br.json
|
|
23
|
+
│ ├── ca.json
|
|
24
|
+
│ ├── cs.json
|
|
25
|
+
│ └── ...
|
|
26
|
+
├── package.json
|
|
27
|
+
├── spellcheck.js
|
|
28
|
+
├── static/
|
|
29
|
+
│ ├── js/
|
|
30
|
+
│ ├── tests/
|
|
31
|
+
├── templates/
|
|
32
|
+
│ ├── spellcheck_entry.ejs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Helpers used
|
|
36
|
+
|
|
37
|
+
_None — `ep_plugin_helpers` is not a dependency. Adoption is part of the helpers-adoption sweep (Phase 4)._
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Helpers NOT used
|
|
41
|
+
|
|
42
|
+
_To be audited in the helpers-adoption sweep (Phase 4)._
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Running tests locally
|
|
46
|
+
|
|
47
|
+
`ep_spellcheck` runs inside Etherpad's test harness. From an etherpad checkout that has installed this plugin via `pnpm run plugins i --path ../ep_spellcheck`:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Backend (Mocha) — harness boots its own server
|
|
51
|
+
pnpm --filter ep_etherpad-lite run test
|
|
52
|
+
|
|
53
|
+
# Playwright — needs `pnpm run dev` in a second terminal
|
|
54
|
+
pnpm --filter ep_etherpad-lite run test-ui
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Standing rules for agent edits
|
|
58
|
+
|
|
59
|
+
* PRs target `main`. Linear commits, no merge commits.
|
|
60
|
+
* Every bug fix includes a regression test in the same commit.
|
|
61
|
+
* All user-facing strings in `locales/`. No hardcoded English in templates.
|
|
62
|
+
* No hardcoded `aria-label` on icon-only controls — etherpad's html10n auto-populates `aria-label` from the localized string when (a) the element has a `data-l10n-id` and (b) no author-supplied `aria-label` is present. Adding a hardcoded English `aria-label` blocks that and leaves it untranslated. (See `etherpad-lite/src/static/js/vendors/html10n.ts:665-678`.)
|
|
63
|
+
* No nested interactive elements (no `<button>` inside `<a>`).
|
|
64
|
+
* LLM/Agent contributions are explicitly welcomed by maintainers.
|
|
65
|
+
|
|
66
|
+
## Quick reference: hooks declared in `ep.json`
|
|
67
|
+
|
|
68
|
+
* Server: `eejsBlock_mySettings`, `eejsBlock_dd_view`
|
|
69
|
+
* Client: `postAceInit`
|
|
70
|
+
|
|
71
|
+
When adding a hook, register it in both `ep.json` *and* the matching `exports.<hook> = ...` in the JS file.
|
package/ep.json
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
"parts": [
|
|
3
3
|
{
|
|
4
4
|
"name": "ep_spellcheck",
|
|
5
|
-
"client_hooks": {
|
|
6
|
-
"postAceInit": "ep_spellcheck/static/js/spellcheck:postAceInit"
|
|
7
|
-
},
|
|
8
5
|
"hooks": {
|
|
6
|
+
"loadSettings": "ep_spellcheck/spellcheck",
|
|
7
|
+
"clientVars": "ep_spellcheck/spellcheck",
|
|
9
8
|
"eejsBlock_mySettings": "ep_spellcheck/spellcheck",
|
|
10
|
-
"
|
|
9
|
+
"eejsBlock_padSettings": "ep_spellcheck/spellcheck"
|
|
10
|
+
},
|
|
11
|
+
"client_hooks": {
|
|
12
|
+
"postAceInit": "ep_spellcheck/static/js/spellcheck:postAceInit",
|
|
13
|
+
"handleClientMessage_CLIENT_MESSAGE": "ep_spellcheck/static/js/spellcheck:handleClientMessage_CLIENT_MESSAGE"
|
|
11
14
|
}
|
|
12
15
|
}
|
|
13
16
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ep_spellcheck",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.99",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"spell",
|
|
6
6
|
"check",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"type": "individual",
|
|
22
22
|
"url": "https://etherpad.org/"
|
|
23
23
|
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"ep_plugin_helpers": "^0.5.2"
|
|
26
|
+
},
|
|
24
27
|
"devDependencies": {
|
|
25
28
|
"eslint": "^8.57.1",
|
|
26
29
|
"eslint-config-etherpad": "^4.0.5",
|
package/spellcheck.js
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
|
3
|
+
const {padToggle} = require('ep_plugin_helpers/pad-toggle-server');
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
// Parallel User Settings + Pad Wide Settings checkboxes for "Spell Check".
|
|
6
|
+
// Helper owns markup, storage, broadcast, enforce, and i18n wiring.
|
|
7
|
+
const spellcheckToggle = padToggle({
|
|
8
|
+
pluginName: 'ep_spellcheck',
|
|
9
|
+
settingId: 'spellcheck',
|
|
10
|
+
l10nId: 'ep_spellcheck.spellcheck',
|
|
11
|
+
defaultLabel: 'Spell Check',
|
|
12
|
+
defaultEnabled: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Older settings.json used `ep_spellcheck.disabledByDefault: true` to flip
|
|
16
|
+
// the checkbox off. Translate to the helper's `defaultEnabled` so existing
|
|
17
|
+
// installs keep their current behavior after the conversion.
|
|
18
|
+
exports.loadSettings = async (hookName, args) => {
|
|
19
|
+
const ps = args && args.settings && args.settings.ep_spellcheck;
|
|
20
|
+
if (ps && typeof ps.defaultEnabled !== 'boolean' &&
|
|
21
|
+
typeof ps.disabledByDefault === 'boolean') {
|
|
22
|
+
ps.defaultEnabled = !ps.disabledByDefault;
|
|
12
23
|
}
|
|
13
|
-
|
|
14
|
-
args.content += eejs.require(ejsPath, {checked: checkedState});
|
|
15
|
-
return cb();
|
|
24
|
+
return spellcheckToggle.loadSettings(hookName, args);
|
|
16
25
|
};
|
|
17
26
|
|
|
18
|
-
exports.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
27
|
+
exports.clientVars = spellcheckToggle.clientVars;
|
|
28
|
+
exports.eejsBlock_mySettings = spellcheckToggle.eejsBlock_mySettings;
|
|
29
|
+
exports.eejsBlock_padSettings = spellcheckToggle.eejsBlock_padSettings;
|
package/static/js/spellcheck.js
CHANGED
|
@@ -1,51 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Sub-path import keeps the client bundle clean — the top-level
|
|
4
|
+
// `ep_plugin_helpers` index pulls in server-only modules (eejs, Settings).
|
|
5
|
+
const {padToggle} = require('ep_plugin_helpers/pad-toggle');
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
/* init */
|
|
20
|
-
if (padcookie.getPref('spellcheck') === false) {
|
|
21
|
-
$('#options-spellcheck').val();
|
|
22
|
-
$('#options-spellcheck').attr('checked', 'unchecked');
|
|
23
|
-
$('#options-spellcheck').attr('checked', false);
|
|
24
|
-
} else {
|
|
25
|
-
$('#options-spellcheck').attr('checked', 'checked');
|
|
26
|
-
}
|
|
7
|
+
// Same config as the server-side instance — must agree on pluginName,
|
|
8
|
+
// settingId, l10nId, and defaultLabel so checkbox ids and clientVars line up.
|
|
9
|
+
const spellcheckToggle = padToggle({
|
|
10
|
+
pluginName: 'ep_spellcheck',
|
|
11
|
+
settingId: 'spellcheck',
|
|
12
|
+
l10nId: 'ep_spellcheck.spellcheck',
|
|
13
|
+
defaultLabel: 'Spell Check',
|
|
14
|
+
defaultEnabled: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Re-export so the helper sees pad-wide broadcasts and refreshes our state
|
|
18
|
+
// when another user toggles the pad-wide checkbox.
|
|
19
|
+
exports.handleClientMessage_CLIENT_MESSAGE = spellcheckToggle.handleClientMessage_CLIENT_MESSAGE;
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
exports.postAceInit = () => {
|
|
22
|
+
// The `spellcheck` attribute is inherited from the nearest ancestor that
|
|
23
|
+
// sets it, so we only need to toggle it on #innerdocbody. Walking every
|
|
24
|
+
// <div>/<span> descendant (the previous behavior) is O(n) in line count
|
|
25
|
+
// and multiplied the browser's already-slow per-keystroke spellchecking
|
|
26
|
+
// work — observable as typing lag on pads with > a few hundred lines (#26).
|
|
27
|
+
const $inner = $('iframe[name="ace_outer"]').contents().find('iframe')
|
|
28
|
+
.contents().find('#innerdocbody');
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
spellcheck.enable();
|
|
39
|
-
} else {
|
|
40
|
-
padcookie.setPref('spellcheck', false);
|
|
41
|
-
spellcheck.disable();
|
|
42
|
-
}
|
|
43
|
-
// The previous code force-reloaded the page on Chrome via
|
|
44
|
-
// `window.browser.chrome`. That check was always a no-op on Chrome
|
|
45
|
-
// (Chrome has no `window.browser` object — it's the Firefox
|
|
46
|
-
// WebExtension API) and would have thrown a TypeError on Firefox.
|
|
47
|
-
// Toggling the spellcheck attribute takes effect in every current
|
|
48
|
-
// browser without a reload, so just drop the reload.
|
|
30
|
+
spellcheckToggle.init({
|
|
31
|
+
onChange: (enabled) => {
|
|
32
|
+
$inner.attr('spellcheck', enabled ? 'true' : 'false');
|
|
33
|
+
},
|
|
49
34
|
});
|
|
50
35
|
};
|
|
51
|
-
exports.postAceInit = postAceInit;
|