From 215de966ae6d93f9505b2f966ea8a89edb824876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 1 Jul 2026 12:40:25 +0200 Subject: [PATCH] :bug: Fix extra linebreak on MacOS IME (#10498) * :bug: Fix extra linebreak in Japense IME (MacOS) * :recycle: Remove composing? from the state and query event instead --- .../ui/workspace/shapes/text/v3_editor.cljs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) 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 450db8f839..03cb433153 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 @@ -39,6 +39,24 @@ (if (>= (count lang) 3) (str/capital lang) (str/upper lang))) "Noto Color Emoji")) +(defn- composing-event? + "True when a key/input event is part of an in-flight IME composition. + + Read from the browser event itself so it stays correct regardless of render + timing or the relative ordering of compositionend. + Note that , and that compositionstart + dispatches after the first composing keydown event. + + We are checkign both isComposing and the keyCode (229, which is what is reported + when using an IME), beause compositionstart dispatches after the first composing + event. Note that also, on MacOS, the key that commits a composition (e.g. Enter + in Japanese IME) dispatches its keydown while composition is still active, so we + can't rely on a stale state and need to query the event itself." + [^js event] + (let [native (.-nativeEvent event)] + (or (.-isComposing native) + (= 229 (.-keyCode event))))) + (mf/defc text-editor* "Contenteditable element positioned over the text shape to capture input events." [{:keys [shape]}] @@ -47,7 +65,6 @@ clip-id (dm/str "text-edition-clip" shape-id) contenteditable-ref (mf/use-ref nil) - composing? (mf/use-state false) fallback-fonts (wasm.api/fonts-from-text-content (:content shape) false) fallback-families (map (fn [font] @@ -72,15 +89,11 @@ on-composition-start (mf/use-fn (fn [_event] - (reset! composing? true) (text-editor/text-editor-composition-start))) on-composition-update (mf/use-fn (fn [event] - (when-not composing? - (reset! composing? true)) - ;; IME cancel (e.g. Escape on Linux ibus-mozc) fires compositionupdate ;; with an empty string; that must reach WASM to clear the preview text. (let [data (.-data event)] @@ -94,7 +107,6 @@ on-composition-end (mf/use-fn (fn [^js event] - (reset! composing? false) (let [data (or (.-data event) "")] (text-editor/text-editor-composition-end data) (sync-wasm-text-editor-content!) @@ -143,11 +155,10 @@ (mf/use-fn (fn [^js event] (when (and (text-editor/text-editor-has-focus?) - (not @composing?)) + (not (composing-event? event))) (let [key (.-key event) ctrl? (or (.-ctrlKey event) (.-metaKey event)) shift? (.-shiftKey event)] - (cond ;; Escape: finalize and stop (= key "Escape") @@ -241,7 +252,7 @@ input-type (.-inputType native-event) data (.-data native-event)] ;; Skip composition-related input events - composition-end handles those - (when (and (not @composing?) + (when (and (not (composing-event? event)) (not= input-type "insertCompositionText")) (when (and data (seq data)) (text-editor/text-editor-insert-text data)