From f29a94058af9b6b66309d430e7c822b1e1996052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Thu, 20 Aug 2026 14:45:43 +0200 Subject: [PATCH] :bug: Fix not quitting v3 editor with Esc + Undo transactions (#11293) * :bug: Fix Esc key not quitting editor v3 * :bug: Fix undo transactions being split in editor v3 --- .../ui/workspace/shapes/text/v3_editor.cljs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 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 86cce0327d..58377e9c1c 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 @@ -11,13 +11,16 @@ [app.common.data.macros :as dm] [app.common.types.text :as txt] [app.main.data.helpers :as dsh] + [app.main.data.workspace :as dw] [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.css-cursors :as cur] [app.render-wasm.api :as wasm.api] [app.render-wasm.text-editor :as text-editor] [app.util.dom :as dom] + [app.util.keyboard :as kbd] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -294,12 +297,7 @@ (and ctrl? (= (str/lower key) "a"))) (text-editor/clear-pending-caret-styles!)) (cond - ;; Escape: finalize and stop - (= key "Escape") - (do - (dom/prevent-default event) - (when-let [node (mf/ref-val contenteditable-ref)] - (.blur node))) + ;; NOTE: Escape is handled in a document key-up listener (see effect below). ;; Ctrl+A: select all (key is "a" or "A" depending on platform) (and ctrl? (= (str/lower key) "a")) @@ -516,6 +514,18 @@ "--editor-container-height" (dm/str height "px") "--fallback-families" (if (seq fallback-families) (dm/str (str/join ", " fallback-families)) "sourcesanspro")}] + ;; Exit on Escape via a document key-up listener (like v2). On key-down the trailing + ;; key-up is read as a non-editing Escape and deselects the shape. + (mf/use-effect + (mf/deps) + (fn [] + (let [on-key-up (fn [event] + (when (kbd/esc? event) + (dom/stop-propagation event) + (st/emit! (dw/clear-edition-mode))))] + (.addEventListener js/document "keyup" on-key-up) + #(.removeEventListener js/document "keyup" on-key-up)))) + ;; Register the native `beforeinput` listener. React's synthetic ;; `onBeforeInput` does not expose `getTargetRanges()`, even with ;; nativeEvent (it's fully synthetic, composed of other two events). @@ -533,6 +543,9 @@ (mf/use-effect (mf/deps contenteditable-ref) (fn [] + ;; Group the whole editing session (edits, reflow resizes, finalize) into a single + ;; undo entry. Nested transactions (e.g. style shortcuts) are ref-counted and fold in. + (st/emit! (dwu/start-undo-transaction shape-id :timeout nil)) (when-let [node (mf/ref-val contenteditable-ref)] ;; Focus and select all text on mount (this will trigger on-focus) (.focus node) @@ -543,6 +556,7 @@ ;; it was not being reliable (timing issues, Firefox issues…) (fn [] (on-blur) + (st/emit! (dwu/commit-undo-transaction shape-id)) (text-editor/text-editor-dispose) (wasm.api/request-render-preserving-target "text-editor-dispose"))))