Pasting from WPS Office yields blank text (upstream CKEditor #11738) — root-caused, patch available for trilium-ckeditor5 fork
Environment
- TriliumNext desktop 0.95.0 and 0.105.0 (Windows 11), bug confirmed on both
- WPS Office 12.1.0.28488 (Windows); WPS for macOS behaves the same
- Bug is cross-platform: the faulty code is JS, and WPS on macOS writes the same clipboard HTML
What happens
Copy any paragraph/heading text in WPS → paste (Ctrl+V) into a text note → all body text becomes invisible whitespace. Tables paste fine. Markdown import works; only clipboard HTML paste is affected.
Root cause (verified at code level)
WPS clipboard HTML claims to be Word (<meta name=Generator content="Microsoft Word 14">) and wraps ordinary text runs in <span style="mso-spacerun:'yes';...">. Real Word only uses such spans for whitespace runs, so downstream code that "fixes up" spacerun spans wipes WPS body text. This hits two separate code paths:
Path 1 — CKEditor 5 PasteFromOffice (all versions)
normalizeSpacerunSpans() in paste-from-office/src/filters/space.ts rewrites the content of every span[style*=spacerun] to an equal-length NBSP sequence:
el.innerText = Array(len + 1).join('\u00A0 ').substr(0, len); // text runs become whitespaceUpstream: https://github.com/ckeditor/ckeditor5/issues/11738 (open since 2022; PRs #10471/#10533/#14039 all unmerged).
Path 2 — Univer doc-paste plugin (new in 0.105)
univer-doc-paste-plugin-word (checkPasteType: /word|mso|wps|kingsoft/i) has its own preprocessHtml():
preprocessHtml(e) {
return e.replace(/<span\b([^>]*)>([\s\S]*?)<\/span>/gi, (e, t, n) => {
let r = /mso-spacerun\s*:\s*yes/i.test(t), i = pYe(t);
if (!r && i == null) return e;
let a = mYe(n), o = i == null ? 1 : ..., s = Math.max(a, o);
return dYe.repeat(s) // <-- ENTIRE span, text included, replaced by U+E000 placeholders
})
}Since WPS text runs match mso-spacerun:yes, every text span is replaced by placeholder characters → blank paste, now independent of CKEditor.
Suggested fix (both paths)
Only treat a spacerun span as a whitespace run when its content actually contains no non-whitespace:
// path 1 (paste-from-office space.ts)
htmlDocument.querySelectorAll('span[style*=spacerun]').forEach(el => {
if (!/[^\s\u00A0]/.test(el.innerText)) { // <-- add this guard
el.innerText = Array(el.innerText.length + 1).join('\u00A0 ').substr(0, el.innerText.length);
}
});
// path 2 (univer plugin preprocessHtml)
let r = /mso-spacerun\s*:\s*yes/i.test(t) && /[^\s\u00A0]/.test(n.replace(/<[^>]*>/g, '')) === false;Word-pasted indentation runs (genuinely whitespace-only) keep working as before.
Context: how users have to patch this locally (why a proper fix matters)
I maintain a user-level workaround (https://github.com/akafel314/trilium-wps-paste-fix) that byte-patches app.asar with equal-length replacements making both matchers never match. On 0.105 this now additionally requires disabling the EnableEmbeddedAsarIntegrityValidation fuse in trilium.exe and recomputing the patched files' SHA256 inside the asar header — fragile and re-done on every app update. A one-line guard in each of the two spots above would remove the need for all of it.
Happy to provide sanitized WPS clipboard HTML samples for tests.
Source: TriliumNext/Trilium