From 895f649ef1c02eb1477d44acb0ffb7da096c4e37 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 14:01:37 +0100
Subject: [PATCH 1/7] :bug: Stop drag events when the user focus out the
 application

---
 .../app/main/data/workspace/drawing/box.cljs  |  8 +------
 .../main/data/workspace/drawing/curve.cljs    | 19 ++++++----------
 .../app/main/data/workspace/interactions.cljs |  8 ++-----
 .../app/main/data/workspace/path/drawing.cljs | 12 +++-------
 .../app/main/data/workspace/path/edition.cljs |  8 ++-----
 .../main/data/workspace/path/selection.cljs   | 13 +++--------
 .../app/main/data/workspace/path/streams.cljs |  4 +---
 .../app/main/data/workspace/selection.cljs    | 13 ++++-------
 .../app/main/data/workspace/transforms.cljs   | 19 ++++------------
 .../app/main/ui/workspace/viewport/hooks.cljs |  7 ++++--
 frontend/src/app/util/mouse.cljs              | 22 ++++++++++++++++++-
 11 files changed, 53 insertions(+), 80 deletions(-)

diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs
index 76a6c3ce8..a595a8c3f 100644
--- a/frontend/src/app/main/data/workspace/drawing/box.cljs
+++ b/frontend/src/app/main/data/workspace/drawing/box.cljs
@@ -78,13 +78,7 @@
   (ptk/reify ::handle-drawing
     ptk/WatchEvent
     (watch [_ state stream]
-      (let [stopper       (rx/merge
-                           (->> stream
-                                (rx/filter mse/mouse-event?)
-                                (rx/filter mse/mouse-up-event?))
-                           (->> stream
-                                (rx/filter #(= % :interrupt))))
-
+      (let [stopper      (mse/drag-stopper stream)
             layout       (get state :workspace-layout)
             zoom         (dm/get-in state [:workspace-local :zoom] 1)
 
diff --git a/frontend/src/app/main/data/workspace/drawing/curve.cljs b/frontend/src/app/main/data/workspace/drawing/curve.cljs
index 8a2194962..5c0d98898 100644
--- a/frontend/src/app/main/data/workspace/drawing/curve.cljs
+++ b/frontend/src/app/main/data/workspace/drawing/curve.cljs
@@ -28,11 +28,6 @@
 
 (def simplify-tolerance 0.3)
 
-(defn stopper-event?
-  [{:keys [type] :as event}]
-  (and (mse/mouse-event? event)
-       (= type :up)))
-
 (defn- insert-point
   [point]
   (ptk/reify ::insert-point
@@ -104,13 +99,13 @@
   (ptk/reify ::handle-drawing
     ptk/WatchEvent
     (watch [_ _ stream]
-      (let [stopper (rx/filter stopper-event? stream)
-            mouse  (rx/sample 10 ms/mouse-position)
-            shape  (cts/setup-shape {:type :path
-                                     :initialized? true
-                                     :frame-id uuid/zero
-                                     :parent-id uuid/zero
-                                     :segments []})]
+      (let [stopper (mse/drag-stopper stream)
+            mouse   (rx/sample 10 ms/mouse-position)
+            shape   (cts/setup-shape {:type :path
+                                      :initialized? true
+                                      :frame-id uuid/zero
+                                      :parent-id uuid/zero
+                                      :segments []})]
         (rx/concat
          (rx/of #(update % :workspace-drawing assoc :object shape))
          (->> mouse
diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs
index 5708ca3c4..1aad31f2f 100644
--- a/frontend/src/app/main/data/workspace/interactions.cljs
+++ b/frontend/src/app/main/data/workspace/interactions.cljs
@@ -193,9 +193,7 @@
     (watch [_ state stream]
       (let [initial-pos @ms/mouse-position
             selected (wsh/lookup-selected state)
-            stopper  (->> stream
-                          (rx/filter mse/mouse-event?)
-                          (rx/filter mse/mouse-up-event?))]
+            stopper  (mse/drag-stopper stream)]
         (when (= 1 (count selected))
           (rx/concat
            (->> ms/mouse-position
@@ -305,9 +303,7 @@
     (watch [_ state stream]
       (let [initial-pos @ms/mouse-position
             selected (wsh/lookup-selected state)
-            stopper  (->> stream
-                          (rx/filter mse/mouse-event?)
-                          (rx/filter mse/mouse-up-event?))]
+            stopper  (mse/drag-stopper stream)]
         (when (= 1 (count selected))
           (let [page-id     (:current-page-id state)
                 objects     (wsh/lookup-page-objects state page-id)
diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs
index af492bc0f..f536f3369 100644
--- a/frontend/src/app/main/data/workspace/path/drawing.cljs
+++ b/frontend/src/app/main/data/workspace/path/drawing.cljs
@@ -139,9 +139,7 @@
                  (rx/map #(drag-handler position idx prefix %))
                  (rx/take-until
                   (rx/merge
-                   (->> stream
-                        (rx/filter mse/mouse-event?)
-                        (rx/filter mse/mouse-up-event?))
+                   (mse/drag-stopper stream)
                    (->> stream
                         (rx/filter helpers/end-path-event?)))))]
 
@@ -166,9 +164,7 @@
     ptk/WatchEvent
     (watch [_ state stream]
       (let [stopper (rx/merge
-                     (->> stream
-                          (rx/filter mse/mouse-event?)
-                          (rx/filter mse/mouse-up-event?))
+                     (mse/drag-stopper stream)
                      (->> stream
                           (rx/filter helpers/end-path-event?)))
 
@@ -197,9 +193,7 @@
    (gpt/point? down-event))
 
   (let [stopper (rx/merge
-                 (->> stream
-                      (rx/filter mse/mouse-event?)
-                      (rx/filter mse/mouse-up-event?))
+                 (mse/drag-stopper stream)
                  (->> stream
                       (rx/filter helpers/end-path-event?)))
 
diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs
index 51d688707..164e37acb 100644
--- a/frontend/src/app/main/data/workspace/path/edition.cljs
+++ b/frontend/src/app/main/data/workspace/path/edition.cljs
@@ -150,9 +150,7 @@
   (ptk/reify ::drag-selected-points
     ptk/WatchEvent
     (watch [_ state stream]
-      (let [stopper (->> stream
-                         (rx/filter mse/mouse-event?)
-                         (rx/filter mse/mouse-up-event?))
+      (let [stopper (mse/drag-stopper stream)
 
             id (dm/get-in state [:workspace-local :edition])
 
@@ -279,9 +277,7 @@
                      (not alt?)))))
                (rx/take-until
                 (rx/merge
-                 (->> stream
-                      (rx/filter mse/mouse-event?)
-                      (rx/filter mse/mouse-up-event?))
+                 (mse/drag-stopper stream)
                  (->> stream
                       (rx/filter streams/finish-edition?)))))
 
diff --git a/frontend/src/app/main/data/workspace/path/selection.cljs b/frontend/src/app/main/data/workspace/path/selection.cljs
index 028a6ec3b..b2256b3c9 100644
--- a/frontend/src/app/main/data/workspace/path/selection.cljs
+++ b/frontend/src/app/main/data/workspace/path/selection.cljs
@@ -10,7 +10,6 @@
    [app.common.geom.point :as gpt]
    [app.common.geom.rect :as grc]
    [app.common.geom.shapes :as gsh]
-   [app.main.data.workspace.common :as dwc]
    [app.main.data.workspace.path.state :as st]
    [app.main.streams :as ms]
    [app.util.mouse :as mse]
@@ -119,15 +118,9 @@
     (ptk/reify ::handle-area-selection
       ptk/WatchEvent
       (watch [_ state stream]
-        (let [zoom   (get-in state [:workspace-local :zoom] 1)
-              stopper (rx/merge
-                       (->> stream
-                            (rx/filter mse/mouse-event?)
-                            (rx/filter mse/mouse-up-event?))
-                       (->> stream
-                            (rx/filter dwc/interrupt?)))
-
-              from-p @ms/mouse-position]
+        (let [zoom    (get-in state [:workspace-local :zoom] 1)
+              stopper (mse/drag-stopper stream)
+              from-p  @ms/mouse-position]
           (rx/concat
            (->> ms/mouse-position
                 (rx/map #(grc/points->rect [from-p %]))
diff --git a/frontend/src/app/main/data/workspace/path/streams.cljs b/frontend/src/app/main/data/workspace/path/streams.cljs
index 9f55e92a0..38d0efd50 100644
--- a/frontend/src/app/main/data/workspace/path/streams.cljs
+++ b/frontend/src/app/main/data/workspace/path/streams.cljs
@@ -53,9 +53,7 @@
          start (-> @ms/mouse-position to-pixel-snap)
 
          stopper (rx/merge
-                  (->> st/stream
-                       (rx/filter mse/mouse-event?)
-                       (rx/filter mse/mouse-up-event?))
+                  (mse/drag-stopper st/stream)
                   (->> st/stream
                        (rx/filter finish-edition?)))
 
diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs
index 3f22275b9..ca4200c11 100644
--- a/frontend/src/app/main/data/workspace/selection.cljs
+++ b/frontend/src/app/main/data/workspace/selection.cljs
@@ -27,6 +27,7 @@
    [app.main.data.modal :as md]
    [app.main.data.workspace.changes :as dch]
    [app.main.data.workspace.collapse :as dwc]
+   [app.main.data.workspace.edition :as dwe]
    [app.main.data.workspace.libraries-helpers :as dwlh]
    [app.main.data.workspace.specialized-panel :as-alias dwsp]
    [app.main.data.workspace.state-helpers :as wsh]
@@ -63,14 +64,8 @@
   (ptk/reify ::handle-area-selection
     ptk/WatchEvent
     (watch [_ state stream]
-      (let [zoom   (dm/get-in state [:workspace-local :zoom] 1)
-            stopper (rx/merge
-                     (->> stream
-                          (rx/filter mse/mouse-event?)
-                          (rx/filter mse/mouse-up-event?))
-                     (->> stream
-                          (rx/filter interrupt?)))
-
+      (let [zoom          (dm/get-in state [:workspace-local :zoom] 1)
+            stopper       (mse/drag-stopper stream)
             init-position @ms/mouse-position
 
             init-selrect  (grc/make-rect
@@ -155,7 +150,7 @@
              objects (wsh/lookup-page-objects state page-id)]
          (rx/of
           (dwc/expand-all-parents [id] objects)
-          :interrupt
+          (dwe/clear-edition-mode)
           ::dwsp/interrupt))))))
 
 (defn select-prev-shape
diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs
index fc8ee350d..c52cd2fbb 100644
--- a/frontend/src/app/main/data/workspace/transforms.cljs
+++ b/frontend/src/app/main/data/workspace/transforms.cljs
@@ -257,9 +257,7 @@
       (watch [_ state stream]
         (let [initial-position @ms/mouse-position
 
-              stopper (->> stream
-                           (rx/filter mse/mouse-event?)
-                           (rx/filter mse/mouse-up-event?))
+              stopper (mse/drag-stopper stream)
               layout  (:workspace-layout state)
               page-id (:current-page-id state)
               focus   (:workspace-focus-selected state)
@@ -370,10 +368,7 @@
 
     ptk/WatchEvent
     (watch [_ _ stream]
-      (let [stopper          (->> stream
-                                  (rx/filter mse/mouse-event?)
-                                  (rx/filter mse/mouse-up-event?))
-
+      (let [stopper         (mse/drag-stopper stream)
             group           (gsh/shapes->rect shapes)
             group-center    (grc/rect->center group)
             initial-angle   (gpt/angle @ms/mouse-position group-center)
@@ -436,10 +431,7 @@
      (watch [_ state stream]
        (let [initial  (deref ms/mouse-position)
 
-             stopper  (->> stream
-                           (rx/filter mse/mouse-event?)
-                           (rx/filter mse/mouse-up-event?))
-
+             stopper (mse/drag-stopper stream)
              zoom    (get-in state [:workspace-local :zoom] 1)
 
              ;; We toggle the selection so we don't have to wait for the event
@@ -518,10 +510,7 @@
 
              duplicate-move-started? (get-in state [:workspace-local :duplicate-move-started?] false)
 
-             stopper (->> stream
-                          (rx/filter mse/mouse-event?)
-                          (rx/filter mse/mouse-up-event?))
-
+             stopper (mse/drag-stopper stream)
              layout  (get state :workspace-layout)
              zoom    (get-in state [:workspace-local :zoom] 1)
              focus   (:workspace-focus-selected state)
diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
index 1a8c45567..c2d4ab55b 100644
--- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
@@ -32,6 +32,7 @@
    [app.util.dom :as dom]
    [app.util.globals :as globals]
    [app.util.keyboard :as kbd]
+   [app.util.mouse :as mse]
    [beicon.v2.core :as rx]
    [beicon.v2.operators :as rxo]
    [goog.events :as events]
@@ -42,7 +43,8 @@
   (let [on-key-down       (actions/on-key-down)
         on-key-up         (actions/on-key-up)
         on-mouse-wheel    (actions/on-mouse-wheel zoom)
-        on-paste          (actions/on-paste disable-paste in-viewport? workspace-read-only?)]
+        on-paste          (actions/on-paste disable-paste in-viewport? workspace-read-only?)
+        on-blur           (mf/use-fn #(st/emit! (mse/->BlurEvent)))]
 
     (mf/use-layout-effect
      (mf/deps on-key-down on-key-up on-mouse-wheel on-paste workspace-read-only?)
@@ -52,7 +54,8 @@
                    ;; bind with passive=false to allow the event to be cancelled
                    ;; https://stackoverflow.com/a/57582286/3219895
                    (events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false})
-                   (events/listen js/window EventType.PASTE on-paste)]]
+                   (events/listen js/window EventType.PASTE on-paste)
+                   (events/listen js/window EventType.BLUR on-blur)]]
          (fn []
            (doseq [key keys]
              (events/unlistenByKey key))))))))
diff --git a/frontend/src/app/util/mouse.cljs b/frontend/src/app/util/mouse.cljs
index b7771eba2..4576ed325 100644
--- a/frontend/src/app/util/mouse.cljs
+++ b/frontend/src/app/util/mouse.cljs
@@ -4,11 +4,14 @@
 ;;
 ;; Copyright (c) KALEIDOS INC
 
-(ns app.util.mouse)
+(ns app.util.mouse
+  (:require
+   [beicon.v2.core :as rx]))
 
 (defrecord MouseEvent [type ctrl shift alt meta])
 (defrecord PointerEvent [source pt ctrl shift alt meta])
 (defrecord ScrollEvent [point])
+(defrecord BlurEvent [])
 
 (defn mouse-event?
   [v]
@@ -22,6 +25,10 @@
   [v]
   (instance? ScrollEvent v))
 
+(defn blur-event?
+  [v]
+  (instance? BlurEvent v))
+
 (defn mouse-down-event?
   [^MouseEvent v]
   (= :down (.-type v)))
@@ -61,3 +68,16 @@
 (defn get-pointer-shift-mod
   [^PointerEvent ev]
   (.-shift ev))
+
+(defn drag-stopper
+  "Creates a stream to stop drag events. Takes into account the mouse and also
+  if the window loses focus or the esc key is pressed."
+  [stream]
+  (rx/merge
+   (->> stream
+        (rx/filter blur-event?))
+   (->> stream
+        (rx/filter mouse-event?)
+        (rx/filter mouse-up-event?))
+   (->> stream
+        (rx/filter #(= % :interrupt)))))

From b097f73b135e937ace9b4e7b24148cc5b6515f74 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 14:18:17 +0100
Subject: [PATCH 2/7] :bug: Fix problem with snap to frame guides

---
 frontend/src/app/worker/snaps.cljs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/frontend/src/app/worker/snaps.cljs b/frontend/src/app/worker/snaps.cljs
index ceecc5b89..77bf5d8f5 100644
--- a/frontend/src/app/worker/snaps.cljs
+++ b/frontend/src/app/worker/snaps.cljs
@@ -29,6 +29,7 @@
   (let [match-bounds?
         (fn [[_ data]]
           (some #(or (= :guide (:type %))
+                     (= :layout (:type %))
                      (grc/contains-point? bounds (:pt %))) data))]
     (->> (into []
                (comp (mapcat #(sd/query @state page-id frame-id axis %))

From 8850fd88948ce7aa622ee6bd707adbd740abfa10 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 14:52:38 +0100
Subject: [PATCH 3/7] :bug: Fix problem dragging in draft/projects screen

---
 frontend/src/app/main/ui/dashboard/grid.cljs | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs
index 6f5565611..276fa7ce4 100644
--- a/frontend/src/app/main/ui/dashboard/grid.cljs
+++ b/frontend/src/app/main/ui/dashboard/grid.cljs
@@ -419,8 +419,9 @@
         on-drag-enter
         (mf/use-fn
          (fn [e]
-           (when (or (dnd/has-type? e "Files")
-                     (dnd/has-type? e "application/x-moz-file"))
+           (when (and (not (dnd/has-type? e "penpot/files"))
+                      (or (dnd/has-type? e "Files")
+                          (dnd/has-type? e "application/x-moz-file")))
              (dom/prevent-default e)
              (reset! dragging? true))))
 
@@ -440,8 +441,9 @@
         on-drop
         (mf/use-fn
          (fn [e]
-           (when (or (dnd/has-type? e "Files")
-                     (dnd/has-type? e "application/x-moz-file"))
+           (when (and (not (dnd/has-type? e "penpot/files"))
+                      (or (dnd/has-type? e "Files")
+                          (dnd/has-type? e "application/x-moz-file")))
              (dom/prevent-default e)
              (reset! dragging? false)
              (import-files (.-files (.-dataTransfer e))))))]

From db7ed75a91cedf12009d9b634c905fa2421d4e3c Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 15:02:29 +0100
Subject: [PATCH 4/7] :bug: Add mime-type otf to color picker

---
 common/src/app/common/media.cljc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/src/app/common/media.cljc b/common/src/app/common/media.cljc
index 212d43f2a..a342a227f 100644
--- a/common/src/app/common/media.cljc
+++ b/common/src/app/common/media.cljc
@@ -10,7 +10,7 @@
    [cuerdas.core :as str]))
 
 ;; We have added ".ttf" as string to solve a problem with chrome input selector
-(def valid-font-types #{"font/ttf", ".ttf", "font/woff", "application/font-woff", "font/otf"})
+(def valid-font-types #{"font/ttf" ".ttf" "font/woff", "application/font-woff" "woff" "font/otf" ".otf" "font/opentype"})
 (def valid-image-types #{"image/jpeg", "image/png", "image/webp", "image/gif", "image/svg+xml"})
 (def str-image-types (str/join "," valid-image-types))
 (def str-font-types (str/join "," valid-font-types))

From dd69762b311b6c4d3e918966f6f7d491da7a68e1 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 15:13:57 +0100
Subject: [PATCH 5/7] :bug: Fix problem with dismiss fonts

---
 frontend/src/app/main/ui/dashboard/fonts.cljs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs
index 17aa4a8d4..be6cd908f 100644
--- a/frontend/src/app/main/ui/dashboard/fonts.cljs
+++ b/frontend/src/app/main/ui/dashboard/fonts.cljs
@@ -161,7 +161,7 @@
         (mf/use-fn
          (mf/deps fonts)
          (fn [_]
-           (run! on-delete (vals fonts))))]
+           (run! #(swap! fonts* dissoc (:id %)) (vals fonts))))]
 
     [:div {:class (stl/css :dashboard-fonts-upload)}
      [:div {:class (stl/css :dashboard-fonts-hero)}

From dc7d279e9dd803cd580270e66caa2ac298c59eb4 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 15:33:51 +0100
Subject: [PATCH 6/7] :bug: Fix problem with interactions over frames

---
 frontend/src/app/main/ui/viewer/shapes.cljs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs
index 6a739d320..5832f28ce 100644
--- a/frontend/src/app/main/ui/viewer/shapes.cljs
+++ b/frontend/src/app/main/ui/viewer/shapes.cljs
@@ -265,8 +265,7 @@
 
 (mf/defc interaction
   [{:keys [shape interactions interactions-show?]}]
-  (let [{:keys [x y width height]} (:selrect shape)
-        frame? (= :frame (:type shape))]
+  (let [{:keys [x y width height]} (:selrect shape)]
     (when-not (empty? interactions)
       [:rect {:x (- x 1)
               :y (- y 1)
@@ -276,7 +275,6 @@
               :stroke "var(--color-accent-tertiary)"
               :stroke-width (if interactions-show? 1 0)
               :fill-opacity (if interactions-show? 0.2 0)
-              :style {:pointer-events (when frame? "none")}
               :transform (gsh/transform-str shape)}])))
 
 

From 8e7471509cb4c267e23d15089bd70f8e074ede63 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Fri, 15 Mar 2024 15:54:40 +0100
Subject: [PATCH 7/7] :bug: Fix problem on modal transfer owner

---
 frontend/src/app/main/ui/dashboard/change_owner.cljs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs
index 0af37045d..b3a8e04d2 100644
--- a/frontend/src/app/main/ui/dashboard/change_owner.cljs
+++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs
@@ -29,10 +29,13 @@
         members-map (mf/deref refs/dashboard-team-members)
         members     (vals members-map)
 
-        options     (into [{:value ""
-                            :label (tr "modals.leave-and-reassign.select-member-to-promote")}]
-                          (filter #(not= (:label %) (:fullname profile))
-                                  (map #(hash-map :label (:name %) :value (str (:id %))) members)))
+        options
+        (into [{:value ""
+                :label (tr "modals.leave-and-reassign.select-member-to-promote")}]
+              (comp
+               (filter #(not= (:email %) (:email profile)))
+               (map #(hash-map :label (:name %) :value (str (:id %)))))
+              members)
 
         on-cancel   #(st/emit! (modal/hide))
         on-accept