Plugin that adds left/center/right/justify alignment buttons to the Etherpad editor toolbar, plus HTML export support for the four alignments.
- Etherpad plugin framework (hooks declared in
ep.json) - EJS templates rendered server-side via
eejsBlock_*hooks - html10n for i18n (
locales/<lang>.json,data-l10n-idin templates) ep_plugin_helpersfor shared boilerplate
ep_align/
├── ep.json # hook declarations
├── index.js # server hooks
├── static/
│ ├── js/
│ │ ├── index.js # client hooks
│ │ └── shared.js # collectContent (shared client+server)
│ └── tests/
│ ├── backend/specs/exportHTML.ts # Mocha export test
│ └── frontend-new/specs/align.spec.ts # Playwright UI test
├── templates/editbarButtons.ejs # toolbar HTML
├── locales/<lang>.json # i18n strings (en.json is canonical)
├── package.json
├── CONTRIBUTING.md
└── AGENTS.md # this file
templatefromep_plugin_helpers— renderseejsBlock_editbarMenuLeft. Passes the hookargsto itsskipcallback so the read-only and toolbar-conflict checks both run there.
These are documented gaps, not omissions:
lineAttribute— would replaceaceAttribsToClasses,aceDomLineProcessLineAttributes,aceRegisterBlockElements,aceRegisterLineAttributes, and thecollectContentPre/Postpair. The helper produces an unstyled<{tag}>wrapper;ep_aligncurrently emits<{tag} style="...;text-align:{tag}">. Adopting the helper requires moving styling into a CSS file injected viaaceEditorCSSand updating the Playwright assertion. Tracked as a follow-up.lineAttributeExport— produces<{tag}>...</{tag}>. The backend export spec enforces<p style='text-align:{tag}'>...</p>for back-compat with consumers of the HTML export API. Adopting requires either extending the helper with awrapInP-style option or breaking export compatibility.
ep_align runs inside Etherpad's test harness. From an etherpad checkout that has installed this plugin via pnpm run plugins i --path ../ep_align:
# Backend (Mocha) — harness boots its own server
pnpm --filter ep_etherpad-lite run test
# Playwright — needs `pnpm run dev` in a second terminal
pnpm --filter ep_etherpad-lite run test-ui- PRs target
main. Linear commits, no merge commits. - Every bug fix includes a regression test in the same commit.
- All user-facing strings in
locales/. No hardcoded English in templates. - No hardcoded
aria-labelon icon-only controls — etherpad's html10n auto-populatesaria-labelfrom the localized string when (a) the element has adata-l10n-idand (b) no author-suppliedaria-labelis present. Adding a hardcoded Englisharia-labelblocks that and leaves it untranslated. (Seeetherpad-lite/src/static/js/vendors/html10n.ts:665-678.) - No nested interactive elements (no
<button>inside<a>). - Never break the export shape (
<p style='text-align:X'>...</p>) without updatingstatic/tests/backend/specs/exportHTML.tsto match the new contract. - LLM/Agent contributions are explicitly welcomed by maintainers.
- Server:
eejsBlock_editbarMenuLeft,padInitToolbar,getLineHTMLForExport,collectContentPre,collectContentPost - Client:
aceEditEvent,postToolbarInit,aceDomLineProcessLineAttributes,postAceInit,aceInitialized,aceAttribsToClasses,aceRegisterBlockElements,aceRegisterLineAttributes,collectContentPre(shared.js)
When adding a hook, register it in both ep.json and the matching exports.<hook> = ... in the JS file.