name: ckeditor5-reviewing description: >- Review or audit CKEditor 5 plugin code in the Trilium (TriliumNext Notes) monorepo, or a PR/diff touching packages/ckeditor5-* or the packages/ckeditor5 build. 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, .po localization gaps, wrong test environment). 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— itsreferences/review-checklist.md(architecture, schema, conversion, commands, UI, a11y) andreferences/conventions.md(naming, imports, JSDoc, TS).ckeditor5-testing— its review checklist and patterns for the test side (Vitest, happy-dom vs.@vitest/browser-webdriveriobrowser mode, realClassicEditor.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 pinned at 48.2.0 — its docs
(ckeditor.com/docs) and source
(github.com/ckeditor/ckeditor5) are external references,
not the code under review. The Trilium plugins live in packages/ckeditor5-{admonition,collapsible, footnotes,keyboard-marker,math,mermaid}; they are assembled by the build/aggregator package
packages/ckeditor5 and wired into the editor UI from apps/client.
When to use
Reviewing a PR or diff that touches a Trilium CKEditor 5 plugin (packages/ckeditor5-*), the
aggregator (packages/ckeditor5), 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 Trilium plugin package (
packages/ckeditor5-<x>)? Does it also touch the aggregator (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 inpackages/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.
- the plugin is exported and added to the right array in
- Check conventions (Trilium-specific). Imports come from the
ckeditor5aggregate (or cross-plugin@triliumnext/ckeditor5-<x>) with explicit file extensions; augmentation usesdeclare module 'ckeditor5'(never@ckeditor/ckeditor5-core); any license header matches the package's existing convention (it's not uniform across packages); new UI strings are inlang/en.po+lang/contexts.json. These are enforced byeslint-config-ckeditor5(require-file-extensions-in-imports,allow-imports-only-from-main-package-entry-point,no-legacy-imports) andstylelint-config-ckeditor5— a diff that breaks them fails lint. - Run the tests for the affected package. Use Vitest via
pnpm --filter @triliumnext/ckeditor5-<f> test(math + mermaid run sequentially). Note the test environment: admonition/collapsible use happy-dom; math/mermaid/footnotes/keyboard-marker use@vitest/browser-webdriveriobrowser mode (NOT Playwright). Coverage ≠ correctness: confirm the change itself 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. - Observe behavior. Attach the CKEditor Inspector (model / view / schema / commands), then:
round-trip
editor.getData()→setData()(does content survive a save?); exercise selection edge cases (collapsed vs. ranged, inside objects/limits, at attribute boundaries); toggle read-only. - Apply the dimension checklists (delegate to the two companion skills).
- Hunt defects with
references/defect-patterns.md— the high-value, easy-to-miss failure modes (CKEditor-general + Trilium integration). - Verify each finding before reporting it (see below). Reproduce it, or cite the exact line and the rule it breaks. Discard the ones that don't hold up.
- Report findings: severity-tagged,
file:line, with the concrete fix or question.
Triage / severity
- Blocker — data loss (content dropped on
getData()), crashes/console errors, undo corruption, security (unsanitized HTML/XSS), schema corruption; plugin not registered inplugins.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 fromtoolbar.ts; lint-failing imports (missing file extension / wrong package / legacy@ckeditor/*) that block the build. - Minor — convention/naming issues, missing
t(), missinglang/en.poentries, 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 justfor('dataDowncast'). - "
getAttribute('data-x')should be.dataset" inside a converter callback — that's a view element, not DOM;.datasetwould silently drop the value (see the plugin-devconversion.mdview-element pitfall). - "Boolean attribute should be set to
false" — the idiom is to remove it (yieldsundefined). - "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 indestroy()). - "Plugin not registered" when it's pulled in transitively via another plugin's
static get requires()rather than listed directly inplugins.ts— check the dependency chain before flagging. - "Import should be from
@ckeditor/ckeditor5-*" — Trilium deliberately imports from theckeditor5aggregate (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 the pinned 48.2.0; 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-*,
packages/ckeditor5/src/{plugins,index}.ts, apps/client/src/widgets/type_widgets/text/toolbar.ts,
lang/en.po) 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.