| name | ckeditor5-reviewing |
| description | Review or audit CKEditor 5 plugin code in the Trilium (TriliumNext Notes) monorepo, or a PR/diff touching packages/ckeditor5 (including its in-tree plugins under src/plugins/). Use when checking a Trilium CKEditor 5 plugin for correctness and idiom: schema / conversion / command / UI / widget code, CKEditor-specific defects (asymmetric upcast/downcast, unconsumed upcast elements, missing inline-widget position mapping, command refresh/isEnabled bugs, memory leaks, t() gaps, editing/UI split violations), and Trilium integration defects (plugin not registered in plugins.ts, button missing from toolbar.ts, import/file-extension lint failures, wrong augmentation module, wrong DOM assumptions in tests). Pairs with the ckeditor5-plugin-development and ckeditor5-testing skills and delegates their checklists. |
Reviewing CKEditor 5 plugins in Trilium
A workflow and defect catalog for reviewing CKEditor 5 plugin code in the Trilium monorepo — a
distinct task from writing it. This skill orchestrates the review and hunts both CKEditor-specific
and Trilium-integration bugs; it delegates the per-dimension "is this idiomatic?" checklists to
the companion skills rather than duplicating them:
ckeditor5-plugin-development — its references/review-checklist.md (architecture, schema,
conversion, commands, UI, a11y) and references/conventions.md (naming, imports, JSDoc, TS).
ckeditor5-testing — its review checklist and patterns for the test side (Vitest, browser-mode
vs. @vitest/browser-webdriverio browser mode, real ClassicEditor.create).
Use those for "does this follow the conventions"; use this skill for how to drive the review
and what subtle things tend to be wrong.
Scope & sources
This skill reviews CKEditor 5 plugin code in the Trilium monorepo (scope @triliumnext/). The
CKEditor 5 library itself is the external ckeditor5 dependency, 48 or later — its docs
(ckeditor.com/docs) and source
(github.com/ckeditor/ckeditor5) are external references,
not the code under review. Nearly all Trilium plugins live inside packages/ckeditor5, under
src/plugins/<name>/ — admonition, collapsible, footnotes, keyboard_marker, mermaid and the
in-tree plugins alongside them. There are no separate CKEditor plugin packages left.
The aggregate assembles them all and is wired into the editor UI from apps/client.
On versions: these skills name major versions only ("48 or later"). Trilium tracks CKEditor
5 closely, so an exact pin written here would be stale within weeks — read the current one from
packages/ckeditor5/package.json.
When to use
Reviewing a PR or diff that touches a Trilium CKEditor 5 plugin (packages/ckeditor5/src/plugins/
), the aggregate itself, or the toolbar config in apps/client;
auditing an existing
plugin; sanity-checking your own feature before opening a PR. For writing the feature, use
ckeditor5-plugin-development; for writing tests, use ckeditor5-testing.
Review workflow
- Scope the diff against Trilium's structure. What surface changed — editing
(schema/conversion/command), UI (componentFactory/buttons/dropdowns/balloons), a widget, config?
Which plugin (
packages/ckeditor5/src/plugins/<name>/)? Does it
also touch the aggregate's wiring (packages/ckeditor5/src/plugins.ts, src/index.ts) or the toolbar
(apps/client/src/widgets/type_widgets/text/toolbar.ts)? This tells you which checklists, defect
groups, and integration checks apply.
- Read model-first. The model is the source of truth; trace the feature in order
schema → conversion → command → UI. Confirm each layer is present and consistent (e.g. a
new model element has a schema registration and upcast and downcast and an insertion path).
- Check registration & wiring (Trilium-specific). A correct plugin that nobody loads is still
broken. Confirm:
- the plugin is exported and added to the right array in
packages/ckeditor5/src/plugins.ts
(CORE_PLUGINS / TRILIUM_PLUGINS / EXTERNAL_PLUGINS → builtinPlugins) so it actually
loads into the editor classes in packages/ckeditor5/src/index.ts (AttributeEditor[Balloon],
ClassicEditor[Decoupled], PopupEditor[Balloon]);
- any new toolbar component is added to
apps/client/src/widgets/type_widgets/text/toolbar.ts,
otherwise the button never appears even though the plugin loaded.
- Check conventions (Trilium-specific). Imports come from the
ckeditor5 aggregate (or a
relative path inside packages/ckeditor5) with explicit file extensions; augmentation uses
declare module 'ckeditor5' (never @ckeditor/ckeditor5-core), normally at the bottom of the
plugin's glue file; any license header matches the sibling files in that plugin folder and the
provenance recorded in its README.md. These are enforced by eslint-config-ckeditor5
(require-file-extensions-in-imports, allow-imports-only-from-main-package-entry-point,
no-legacy-imports) and stylelint-config-ckeditor5 — a diff that breaks them fails lint.
- Run the tests for the affected package. Use Vitest via
pnpm --filter @triliumnext/ckeditor5 test (or ); the two run sequentially because
each spins up headless Chrome. Both use (NOT
Playwright) and gate at . Coverage ≠ correctness: confirm
the is tested, not just that lines are hit. A bug fix with no new/changed test is
a red flag even when coverage stays green.
Triage / severity
- Blocker — data loss (content dropped on
getData()), crashes/console errors, undo
corruption, security (unsanitized HTML/XSS), schema corruption; plugin not registered in
plugins.ts (feature silently never loads).
- Major — incorrect behavior in real selections, accessibility gaps (no keyboard path, missing
labels/
addKeystrokeInfos), memory leaks, command enabled where the schema disallows it; button
missing from toolbar.ts; lint-failing imports (missing file extension / wrong package /
legacy @ckeditor/*) that block the build.
- Minor — convention/naming issues, missing
t(), redundant converters, non-idiomatic but
working code.
- Nit — style preferences with no behavioral impact; mark them as optional.
Lead with blockers/majors; don't bury them under nits.
Verify before reporting (avoid false positives)
A noisy review erodes trust. Confirm a finding is real before raising it. Common false
positives to not raise:
- "Missing downcast" when the code uses a two-way helper (
conversion.attributeToElement(...)
registers both directions) — count two-way helpers, not just for('dataDowncast').
- "
getAttribute('data-x') should be .dataset" inside a converter callback — that's a view
element, not DOM; .dataset would silently drop the value (see the plugin-dev conversion.md
view-element pitfall).
- "Boolean attribute should be set to
false" — the idiom is to remove it (yields undefined).
- "Button state not updated" when it's
bind()-ed to the command (reactive, not imperative).
- "Listener never removed" when added via
this.listenTo() (auto-cleaned in destroy()).
- "Plugin not registered" when it's pulled in transitively via another plugin's
static get requires() rather than listed directly in plugins.ts — check the dependency chain
before flagging.
- "Import should be from
@ckeditor/ckeditor5-*" — Trilium deliberately imports from the ckeditor5
aggregate (with file extensions); the deep @ckeditor/* path is the lint failure, not the fix.
When unsure, phrase it as a question ("Is <mark> intended to round-trip through getData()?
I don't see a dataDowncast.") rather than a false assertion.
Reference map
| File | Use it for |
|---|
references/defect-patterns.md | The bug catalog — CKEditor-general groups (conversion, schema, commands, UI, lifecycle, localization, undo, tests) plus a Trilium integration group. For each: the symptom, how to spot it in a diff, why it's wrong, and the fix. The core of a correctness review. |
Provenance & source references
The CKEditor-general defect patterns are distilled from the companion skills and the CKEditor 5
library at 48 or later; docs/… / packages/… paths in those patterns point into the
external CKEditor 5 repository (github.com/ckeditor/ckeditor5),
not the Trilium code under review. The Trilium-specific paths
(packages/ckeditor5/src/plugins/<name>/,
packages/ckeditor5/src/{plugins,index}.ts, apps/client/src/widgets/type_widgets/text/toolbar.ts)
point into the Trilium monorepo. To refresh, re-check those paths against the
current Trilium tree and bump the CKEditor version if the ckeditor5 pin changes.