From 176a813fb9c793348b88135bfb4626d14f8da7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Fri, 10 Jul 2026 12:02:30 +0200 Subject: [PATCH] :bug: Fix selected text background color in light theme (#10614) --- .../app/main/ui/workspace/viewport_wasm.cljs | 11 +++++++ frontend/src/app/render_wasm/api.cljs | 1 + frontend/src/app/render_wasm/text_editor.cljs | 30 ++++++++++++++++++- frontend/src/app/util/color.cljs | 17 +++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 1c4384f1e6..4a0c4a80b2 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -575,6 +575,17 @@ (wasm.api/push-ruler-theme-colors!) (wasm.api/request-render "rulers-colors-theme"))))) + ;; Text-editor-wasm: push the theme colors (selection background, caret) + ;; into the WASM text editor so the selection follows the design tokens per + ;; theme (purple on light, teal on dark) instead of a hardcoded default. + (mf/with-effect [@canvas-init?] + (when @canvas-init? + (wasm.api/text-editor-apply-theme) + (theme/add-color-scheme-listener! + (fn [] + (wasm.api/text-editor-apply-theme) + (wasm.api/request-render "text-editor-colors-theme"))))) + ;; Ruler overlay updates below only change the UI surface, not the shapes. ;; They use `render-from-cache!` (cached tiles + UI, atomic) instead of a full ;; `request-render`, which would kick off a progressive tile-by-tile shape diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index cea8256980..e4be664616 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -273,6 +273,7 @@ (def draw-thumbnail-to-canvas webgl/draw-thumbnail-to-canvas) ;; Re-export public text editor functions +(def text-editor-apply-theme text-editor/text-editor-apply-theme) (def text-editor-focus text-editor/text-editor-focus) (def text-editor-blur text-editor/text-editor-blur) (def text-editor-set-cursor-from-offset text-editor/text-editor-set-cursor-from-offset) diff --git a/frontend/src/app/render_wasm/text_editor.cljs b/frontend/src/app/render_wasm/text_editor.cljs index 5dba3e675c..d630346159 100644 --- a/frontend/src/app/render_wasm/text_editor.cljs +++ b/frontend/src/app/render_wasm/text_editor.cljs @@ -15,7 +15,10 @@ [app.render-wasm.helpers :as h] [app.render-wasm.mem :as mem] [app.render-wasm.serializers :as sr] - [app.render-wasm.wasm :as wasm])) + [app.render-wasm.serializers.color :as sr-clr] + [app.render-wasm.wasm :as wasm] + [app.util.color :as uc] + [app.util.dom :as dom])) (def multiple-state-multiple (sr/translate-multiple-state :multiple)) @@ -124,6 +127,31 @@ nil))) +(def ^:private selection-color-css-var "--text-editor-selection-background-color") +(def ^:private caret-color-css-var "--text-editor-caret-color") + +(defn- resolve-theme-color + "Resolve a themed CSS color variable (read from the document body) into a + 32-bit argb value for the WASM text editor, preserving the variable's alpha + channel." + [css-var] + (when-let [{:keys [color opacity]} + (uc/parse-css-color-opacity + (dom/get-css-variable css-var js/document.body))] + (sr-clr/hex->u32argb color opacity))) + +(defn text-editor-apply-theme + "Push the current theme's selection and caret colors (read from the CSS + custom properties on the document body) into the WASM text editor. The + editor theme is a persistent singleton, so call once after init and again + on every color-scheme change." + [] + (when wasm/context-initialized? + (let [selection (resolve-theme-color selection-color-css-var) + caret (resolve-theme-color caret-color-css-var)] + (when (and selection caret) + (h/call wasm/internal-module "_text_editor_apply_theme" selection caret))))) + (defn text-editor-focus [id] (when wasm/context-initialized? diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index 58cd64beb7..d28c3ccfb6 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -104,3 +104,20 @@ (cc/valid-hex-color? s) (-> (subs s 1) cc/expand-hex cc/prepend-hash) :else (some-> (cc/parse-rgb s) cc/rgb->hex))))) + +(def ^:private rgba-color-re + #"rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*([0-9]*\.?[0-9]+)\s*)?\)") + +(defn parse-css-color-opacity + "Like `parse-css-color` but also extracts the alpha channel, returning a + `{:color \"#rrggbb\" :opacity <0..1>}` map (opacity defaults to 1 when the + source has none). Handles #rrggbb, #rgb, rgb() and rgba(). Returns nil when + the string cannot be parsed." + [raw] + (let [s (some-> raw str/trim)] + (when (and (string? s) (seq s)) + (if (cc/valid-hex-color? s) + {:color (-> (subs s 1) cc/expand-hex cc/prepend-hash) :opacity 1} + (when-let [[_ r g b a] (re-matches rgba-color-re s)] + {:color (cc/rgb->hex [(js/parseInt r 10) (js/parseInt g 10) (js/parseInt b 10)]) + :opacity (if a (js/parseFloat a) 1)})))))