diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index fabf3e3fed..4d365f0bee 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -1067,7 +1067,8 @@ (watch [_ state _] (let [[text content] (if (v3-html-paste? state) - (when-let [fragment (text-clipboard/html->fragment html)] + (when-let [fragment (some-> (text-clipboard/html->fragment html) + (text-clipboard/without-overrides))] [(text-paste/fragment->text fragment) (text-paste/fragment->content fragment (txt/get-default-text-attrs))]) (let [style (deref refs/workspace-clipboard-style) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs index 8ed1cb50fe..f3138a5238 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs @@ -107,11 +107,13 @@ (def ^:private max-paste-html-length 1000000) (defn- clipboard->fragment - "The paste fragment for `data`: its HTML when allowed and it has text, else its plain text." + "The paste fragment for `data`: its HTML when allowed and it has text, else its + plain text. Either way it is unstyled, so it adopts the caret style." [^js data html-paste?] (let [html (when html-paste? (.getData data "text/html"))] (or (when (< 0 (count html) max-paste-html-length) - (text-clipboard/html->fragment html)) + (some-> (text-clipboard/html->fragment html) + (text-clipboard/without-overrides))) (text-clipboard/text->fragment (.getData data "text/plain"))))) (defn- selection-start @@ -125,18 +127,32 @@ {:para focus-para :offset focus-offset}))) (defn- paste-fragment - "Insert the text of `fragment`, then restyle it with the fragment overrides." + "Insert the text of `fragment`, then restyle it with the fragment overrides or, + when it has none, with the pending caret style." [fragment] (let [shape-id (text-editor/text-editor-get-active-shape-id) - start (selection-start)] + start (selection-start) + styled? (text-paste/styled? fragment) + pending? (some? (text-editor/get-pending-caret-styles shape-id))] + (when styled? + (text-editor/clear-pending-caret-styles!)) (text-editor/text-editor-insert-text (text-paste/fragment->text fragment)) - (if (and (some? start) (text-paste/styled? fragment)) + (cond + (nil? start) + (sync-wasm-text-editor-content!) + + styled? (do ;; Sync first so the cached content stays index-aligned with WASM. (text-editor/text-editor-sync-content) (if-let [{:keys [content]} (wasm.api/apply-paste-styles shape-id fragment start)] (commit-restyled-content shape-id content) (sync-wasm-text-editor-content!))) + + pending? + (sync-with-pending-caret-styles! shape-id start) + + :else (sync-wasm-text-editor-content!)))) (defn- reset-input-node @@ -315,8 +331,6 @@ (mf/use-fn (fn [^js event] (dom/prevent-default event) - ;; Pasted text keeps the surrounding style; drop any pending caret style. - (text-editor/clear-pending-caret-styles!) (when-let [fragment (some-> (.-clipboardData event) (clipboard->fragment (features/active-feature? @st/state "text-editor-wasm/v1-html-paste")))] diff --git a/frontend/src/app/util/text/clipboard.cljs b/frontend/src/app/util/text/clipboard.cljs index 963f49b688..4c806fd1c4 100644 --- a/frontend/src/app/util/text/clipboard.cljs +++ b/frontend/src/app/util/text/clipboard.cljs @@ -244,6 +244,17 @@ (.parseFromString html "text/html") (document->fragment))) +(defn without-overrides + "`fragment` as one plain run per paragraph, so it takes the style it is pasted + into. External sources keep only their text and paragraphs." + [fragment] + (mapv (fn [{:keys [children] :as paragraph}] + (assoc paragraph :children + (if (seq children) + [{:text (str/join (map :text children)) :attrs {}}] + []))) + fragment)) + (defn text->fragment "The paste fragment for plain text, one paragraph per line, or nil when empty." [text] diff --git a/frontend/test/frontend_tests/util_text_clipboard_test.cljs b/frontend/test/frontend_tests/util_text_clipboard_test.cljs index a8f65d6e4f..d2d757fbb0 100644 --- a/frontend/test/frontend_tests/util_text_clipboard_test.cljs +++ b/frontend/test/frontend_tests/util_text_clipboard_test.cljs @@ -163,3 +163,14 @@ (t/testing "empty text gives no fragment" (t/is (nil? (clipboard/text->fragment ""))))) + +(t/deftest without-overrides + (t/testing "external HTML keeps its text and paragraphs but no emphasis" + (t/is (= [(paragraph (plain-run "Hello bold and italic")) + (paragraph) + (paragraph (plain-run "Second"))] + (clipboard/without-overrides (html->fragment google-docs-html))))) + + (t/testing "empty paragraphs stay empty" + (t/is (= [(paragraph)] + (clipboard/without-overrides [(paragraph)])))))