#11295·Trilium

Pasting from WPS Office yields blank text (upstream CKEditor #11738) — root-caused, patch available for trilium-ckeditor5 fork

Author: akafel314Created Sep 1, 2026Updated Sep 15, 2026

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:

javascript
el.innerText = Array(len + 1).join('\u00A0 ').substr(0, len); // text runs become whitespace

Upstream: 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():

javascript
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:

javascript
// 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.