🐛 Fix WASM text selection copy to Windows apps (#11305)

Write text/html alongside text/plain on copy/cut so Windows apps that
prefer CF_HTML do not paste the empty contenteditable newline.
This commit is contained in:
Alejandro Alonso 2026-08-21 15:15:40 +02:00 committed by GitHub
parent 8aefa2ddfd
commit dd6b521bc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 5 deletions

View File

@ -19,6 +19,7 @@
[app.main.ui.css-cursors :as cur]
[app.render-wasm.api :as wasm.api]
[app.render-wasm.text-editor :as text-editor]
[app.util.clipboard :as clipboard]
[app.util.dom :as dom]
[app.util.keyboard :as kbd]
[cuerdas.core :as str]
@ -267,8 +268,13 @@
(when (text-editor/text-editor-has-focus?)
(dom/prevent-default event)
(when (text-editor/text-editor-has-selection?)
(let [text (text-editor/text-editor-export-selection)]
(.setData (.-clipboardData event) "text/plain" text))))))
(let [text (or (text-editor/text-editor-export-selection) "")
html (clipboard/plain-text->html text)
data (.-clipboardData event)]
;; text/html matters on Windows: many apps prefer CF_HTML, and
;; without it they can pick up the empty contenteditable `<br>`.
(.setData data "text/plain" text)
(.setData data "text/html" html))))))
on-cut
(mf/use-fn
@ -276,9 +282,12 @@
(when (text-editor/text-editor-has-focus?)
(dom/prevent-default event)
(when (text-editor/text-editor-has-selection?)
(let [text (text-editor/text-editor-export-selection)]
(.setData (.-clipboardData event) "text/plain" (or text ""))
(when (and text (seq text))
(let [text (or (text-editor/text-editor-export-selection) "")
html (clipboard/plain-text->html text)
data (.-clipboardData event)]
(.setData data "text/plain" text)
(.setData data "text/html" html)
(when (seq text)
(text-editor/text-editor-delete-backward)
(sync-wasm-text-editor-content!)
(wasm.api/request-render-preserving-target "text-cut"))))

View File

@ -22,6 +22,18 @@
#js {:decodeTransit t/decode-str
:allowHTMLPaste false})
(defn plain-text->html
"Build a minimal text/html clipboard payload from plain text.
Windows apps often prefer CF_HTML over CF_UNICODETEXT; writing only
text/plain from a contenteditable copy handler can leave them with the
editor surface's empty/`<br>` fallback (a lone newline)."
[text]
(let [escaped (-> (or text "")
(dom/escape-html)
(str/replace "\n" "<br>"))]
(str "<meta charset=\"utf-8\">" escaped)))
(defn- from-data-transfer
"Get clipboard stream from DataTransfer instance"
([data-transfer]