diff --git a/CHANGES.md b/CHANGES.md
index 6f89fe950..7a01888b8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -111,6 +111,8 @@
- Fix 400 error when user changes password [Taiga #5643](https://tree.taiga.io/project/penpot/issue/5643)
- Fix cannot undo layer styles [Taiga #5676](https://tree.taiga.io/project/penpot/issue/5676)
- Fix unexpected exception on boolean shapes [Taiga #5685](https://tree.taiga.io/project/penpot/issue/5685)
+- Fix ctrl+z on select not working [Taiga #5677](https://tree.taiga.io/project/penpot/issue/5677)
+- Fix thubmnail rendering flashing [Taiga #5675](https://tree.taiga.io/project/penpot/issue/5675)
### :arrow_up: Deps updates
diff --git a/backend/resources/app/onboarding.edn b/backend/resources/app/onboarding.edn
index e9f6f063f..0438d25ba 100644
--- a/backend/resources/app/onboarding.edn
+++ b/backend/resources/app/onboarding.edn
@@ -1,36 +1,30 @@
[{:id "material-design-3"
:name "Material Design 3"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-md3.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/main/Material%20Design%203.penpot"}
{:id "tutorial-for-beginners"
:name "Tutorial for beginners"
- :thumbnail-uri "https://penpot.app/images/libraries/tutorial-for-beginners.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/tutorial-for-beginners.penpot"}
{:id "penpot-design-system"
:name "Penpot Design System"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-ds-penpot.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Penpot-Design-system.penpot"}
+ {:id "flex-layout-playground"
+ :name "Flex Layout Playground"
+ :file-uri "https://github.com/penpot/penpot-files/raw/main/Flex%20Layout%20Playground.penpot"}
{:id "wireframing-kit"
:name "Wireframing Kit"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-wireframes.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/wireframing-kit.penpot"}
{:id "ant-design"
:name "Ant Design UI Kit (lite)"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-ant-design.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Ant-Design-UI-Kit-Lite.penpot"}
{:id "cocomaterial"
:name "Cocomaterial"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-cocomaterial.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Cocomaterial.penpot"}
{:id "circum-icons"
:name "Circum Icons pack"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-circum.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/CircumIcons.penpot"}
{:id "coreui"
:name "CoreUI"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-coreui.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/main/CoreUI%20DesignSystem%20(DEMO).penpot"}
{:id "whiteboarding-kit"
:name "Whiteboarding Kit"
- :thumbnail-uri "https://penpot.app/images/libraries/cover-whiteboards.jpg"
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Whiteboarding-mapping-kit.penpot"}]
diff --git a/backend/src/app/rpc/commands/management.clj b/backend/src/app/rpc/commands/management.clj
index 3de69a9dd..ead66317b 100644
--- a/backend/src/app/rpc/commands/management.clj
+++ b/backend/src/app/rpc/commands/management.clj
@@ -28,7 +28,8 @@
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.spec.alpha :as s]
- [clojure.walk :as walk]))
+ [clojure.walk :as walk]
+ [promesa.exec :as px]))
;; --- COMMAND: Duplicate File
@@ -322,6 +323,18 @@
;; delete possible broken relations on moved files
(db/exec-one! conn [sql:delete-broken-relations pids])
+ ;; Update the modification date of the all affected projects
+ ;; ensuring that the destination project is the most recent one.
+ (doseq [project-id (into (list project-id) source)]
+
+ ;; NOTE: as this is executed on virtual thread, sleeping does
+ ;; not causes major issues, and allows an easy way to set a
+ ;; trully different modification date to each file.
+ (px/sleep 10)
+ (db/update! conn :project
+ {:modified-at (dt/now)}
+ {:id project-id}))
+
nil))
(s/def ::ids (s/every ::us/uuid :kind set?))
@@ -423,9 +436,9 @@
{::doc/added "1.10"
::doc/deprecated "1.19"}
[cfg _params]
- (mapv #(select-keys % [:id :name :thumbnail-uri]) (::setup/templates cfg)))
+ (mapv #(select-keys % [:id :name]) (::setup/templates cfg)))
(sv/defmethod ::get-builtin-templates
{::doc/added "1.19"}
[cfg _params]
- (mapv #(select-keys % [:id :name :thumbnail-uri]) (::setup/templates cfg)))
+ (mapv #(select-keys % [:id :name]) (::setup/templates cfg)))
diff --git a/backend/src/app/setup/templates.clj b/backend/src/app/setup/templates.clj
index 98afd340c..c51bc98c8 100644
--- a/backend/src/app/setup/templates.clj
+++ b/backend/src/app/setup/templates.clj
@@ -23,7 +23,6 @@
[:map {:title "Template"}
[:id ::sm/word-string]
[:name ::sm/word-string]
- [:thumbnail-uri ::sm/word-string]
[:file-uri ::sm/word-string]])
(def ^:private schema:templates
diff --git a/frontend/resources/images/thumbnails/template-flex-layout-playground.jpg b/frontend/resources/images/thumbnails/template-flex-layout-playground.jpg
new file mode 100644
index 000000000..0457dbfce
Binary files /dev/null and b/frontend/resources/images/thumbnails/template-flex-layout-playground.jpg differ
diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs
index 879810ff1..e377b1ab3 100644
--- a/frontend/src/app/main/data/dashboard.cljs
+++ b/frontend/src/app/main/data/dashboard.cljs
@@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
- [app.common.time :as dt]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -23,6 +22,7 @@
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
+ [app.util.time :as dt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.core :as rx]
@@ -863,6 +863,7 @@
[{:keys [ids project-id] :as params}]
(dm/assert! (sm/set-of-uuid? ids))
(dm/assert! (uuid? project-id))
+
(ptk/reify ::move-files
IDeref
(-deref [_]
@@ -872,13 +873,13 @@
ptk/UpdateEvent
(update [_ state]
(let [origin-project (get-in state [:dashboard-files (first ids) :project-id])
- update-project (fn [project]
+ update-project (fn [project delta]
(-> project
(update :count #(+ % (count ids)))
- (assoc :modified-at (dt/now))))]
+ (assoc :modified-at (dt/plus (dt/now) {:milliseconds delta}))))]
(-> state
- (d/update-in-when [:dashboard-projects origin-project] update-project)
- (d/update-in-when [:dashboard-projects project-id] update-project))))
+ (d/update-in-when [:dashboard-projects origin-project] update-project 0)
+ (d/update-in-when [:dashboard-projects project-id] update-project 10))))
ptk/WatchEvent
(watch [_ _ _]
diff --git a/frontend/src/app/main/data/shortcuts_impl.js b/frontend/src/app/main/data/shortcuts_impl.js
index d72137ed0..9be5beb05 100644
--- a/frontend/src/app/main/data/shortcuts_impl.js
+++ b/frontend/src/app/main/data/shortcuts_impl.js
@@ -17,17 +17,20 @@ if (Mousetrap.addKeycodes) {
const target = Mousetrap.prototype || Mousetrap;
target.stopCallback = function(e, element, combo) {
- // if the element has the class "mousetrap" then no need to stop
- if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
- return false;
+ // if the element has the data attribute "mousetrap-dont-stop" then no need
+ // to stop. It should be used like
...
+ // or :div {:data-mousetrap-dont-stop true}
+ if ('mousetrapDontStop' in element.dataset) {
+ return false
}
// stop for input, select, textarea and button
- return element.tagName == 'INPUT' ||
- element.tagName == 'SELECT' ||
- element.tagName == 'TEXTAREA' ||
- (element.tagName == 'BUTTON' && combo.includes("tab")) ||
- (element.contentEditable && element.contentEditable == 'true');
+ const shouldStop = element.tagName == "INPUT" ||
+ element.tagName == "SELECT" ||
+ element.tagName == "TEXTAREA" ||
+ (element.tagName == "BUTTON" && combo.includes("tab")) ||
+ (element.contentEditable && element.contentEditable == "true");
+ return shouldStop;
}
export default Mousetrap;
diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs
index d0f5f3f99..3c58700ae 100644
--- a/frontend/src/app/main/ui/dashboard/projects.cljs
+++ b/frontend/src/app/main/ui/dashboard/projects.cljs
@@ -296,10 +296,9 @@
:on-import on-import}]
[:span.info (str (tr "labels.num-of-files" (i18n/c file-count)))]
- (when (> file-count 0)
- (let [time (-> (:modified-at project)
- (dt/timeago {:locale locale}))]
- [:span.recent-files-row-title-info (str ", " time)]))
+ (let [time (-> (:modified-at project)
+ (dt/timeago {:locale locale}))]
+ [:span.recent-files-row-title-info (str ", " time)])
[:div.project-actions
(when-not (:is-default project)
[:button.pin-icon.tooltip.tooltip-bottom
diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs
index 9357faa8d..18bb1453f 100644
--- a/frontend/src/app/main/ui/dashboard/templates.cljs
+++ b/frontend/src/app/main/ui/dashboard/templates.cljs
@@ -150,8 +150,9 @@
(mf/defc templates-section
{::mf/wrap-props false}
[{:keys [default-project-id profile project-id team-id content-width]}]
- (let [templates (->> (mf/deref builtin-templates)
- (filter #(not= (:id %) "tutorial-for-beginners")))
+ (let [templates (mf/deref builtin-templates)
+ templates (mf/with-memo [templates]
+ (filterv #(not= (:id %) "tutorial-for-beginners") templates))
route (mf/deref refs/route)
route-name (get-in route [:data :name])
diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs
index d5f76a4a3..ba6a8b28e 100644
--- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs
+++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs
@@ -72,7 +72,9 @@
(reset! current-colors (into [] (filter check-valid-color?) colors)))))
[:div.libraries
- [:select {:on-change on-library-change :value (name @selected)}
+ [:select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :on-change on-library-change
+ :value (name @selected)}
[:option {:value "recent"} (tr "workspace.libraries.colors.recent-colors")]
[:option {:value "file"} (tr "workspace.libraries.colors.file-library")]
diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs
index a7c019bf8..5c7d535f3 100644
--- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs
@@ -103,7 +103,6 @@
render-frame* (mf/use-state (not thumbnail-uri))
debug? (debug? :thumbnails)
-
on-bitmap-load
(mf/use-fn
(fn []
@@ -171,6 +170,8 @@
(when (not= "false" (dom/get-data image-node "ready"))
(dom/set-data! image-node "ready" "false")))
(when-not ^boolean @disable*
+ (reset! svg-uri* nil)
+ (reset! bitmap-uri* nil)
(reset! render-frame* true)
(reset! regenerate* true))))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
index b4536f350..2a0970000 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
@@ -164,7 +164,7 @@
(if ^boolean new-css-system
[:div {:class (css :assets-bar)}
[:div {:class (css :assets-header)}
- (when-not read-only?
+ (when-not ^boolean read-only?
[:button {:class (css :libraries-button)
:on-click #(modal/show! :libraries-dialog {})}
[:span {:class (css :libraries-icon)}
@@ -229,6 +229,7 @@
i/close])]
[:select.input-select {:value (:section filters)
+ :data-mousetrap-dont-stop true
:on-change on-section-filter-change}
[:option {:value "all"} (tr "workspace.assets.box-filter-all")]
[:option {:value "components"} (tr "workspace.assets.components")]
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs
index 9fd49134f..781f1b3ad 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs
@@ -189,7 +189,7 @@
;; NOTE: Neither get-parent-at nor get-parent-with-selector
;; work if the component template changes, so we need to
;; seek for an alternate solution. Maybe use-context?
- scroll-node (dom/get-parent-with-selector node ".tool-window-content")
+ scroll-node (dom/get-parent-with-data node "scrollContainer")
parent-node (dom/get-parent-at node 2)
subid
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
index 1fd8cef7d..e6ec213ff 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
@@ -538,6 +538,7 @@
(if (some? filtered-objects)
[:*
[:div {:class (stl/css new-css-system :tool-window-content)
+ :data-scroll-container true
:ref on-render-container}
[:& filters-tree {:objects filtered-objects
:key (dm/str (:id page))
@@ -546,6 +547,7 @@
:style {:min-height 16}}]]
[:div {:on-scroll on-scroll
:class (stl/css new-css-system :tool-window-content)
+ :data-scroll-container true
:style {:display (when (some? filtered-objects) "none")}}
[:& layers-tree {:objects filtered-objects
:key (dm/str (:id page))
@@ -554,6 +556,7 @@
[:div {:on-scroll on-scroll
:class (stl/css new-css-system :tool-window-content)
+ :data-scroll-container true
:style {:display (when (some? filtered-objects) "none")}}
[:& layers-tree {:objects objects
:key (dm/str (:id page))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs
index a65f6b125..94ebe5c78 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs
@@ -147,7 +147,8 @@
[:div.constraints-form
[:div.row-flex
[:span.left-right i/full-screen]
- [:select.input-select {:on-change (on-constraint-select-changed :constraints-h)
+ [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :on-change (on-constraint-select-changed :constraints-h)
:value (d/name constraints-h "scale")}
(when (= constraints-h :multiple)
[:option {:value ""} (tr "settings.multiple")])
@@ -158,7 +159,8 @@
[:option {:value "scale"} (tr "workspace.options.constraints.scale")]]]
[:div.row-flex
[:span.top-bottom i/full-screen]
- [:select.input-select {:on-change (on-constraint-select-changed :constraints-v)
+ [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :on-change (on-constraint-select-changed :constraints-v)
:value (d/name constraints-v "scale")}
(when (= constraints-v :multiple)
[:option {:value ""} (tr "settings.multiple")])
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs
index 879c49173..9fe6cad42 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs
@@ -169,7 +169,8 @@
[:div.element-set-options-group
{:key index}
(when (scale-enabled? export)
- [:select.input-select {:on-change (partial on-scale-change index)
+ [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :on-change (partial on-scale-change index)
:value (:scale export)}
[:option {:value "0.5"} "0.5x"]
[:option {:value "0.75"} "0.75x"]
@@ -182,7 +183,8 @@
:placeholder (tr "workspace.options.export.suffix")
:on-change (partial on-suffix-change index)
:on-key-down manage-key-down}]
- [:select.input-select {:value (d/name (:type export))
+ [:select.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :value (d/name (:type export))
:on-change (partial on-type-change index)}
[:option {:value "png"} "PNG"]
[:option {:value "jpeg"} "JPEG"]
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
index 5980469cd..f1f2ec21e 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
@@ -139,7 +139,8 @@
i/actions]
[:select.input-select
- {:default-value shadow-style
+ {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :default-value shadow-style
:on-change (fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))}
@@ -165,7 +166,8 @@
{:on-click on-toggle-open-shadow}
i/actions]
[:select.input-select
- {:default-value shadow-style
+ {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :default-value shadow-style
:on-change (fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs
index b1c4ae748..58c6d4d8c 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs
@@ -425,6 +425,7 @@
[:select.input-select.variant-option
{:disabled (= font-id :multiple)
+ :data-mousetrap-dont-stop true
:value (attr->string font-variant-id)
:on-change on-font-variant-change
:on-blur on-blur}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs
index d834d4a6b..f0aca9d25 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs
@@ -20,7 +20,8 @@
(case type
:select
- [:& select {:default-value value
+ [:& select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :default-value value
:class "input-option"
:options options
:on-change on-change}]
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
index 964f626b1..60bf0afb6 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
@@ -106,7 +106,8 @@
:select-on-focus select-on-focus
:on-blur on-blur}]]
- [:select#style.input-select {:value (enum->string (:stroke-alignment stroke))
+ [:select#style.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :value (enum->string (:stroke-alignment stroke))
:on-change (on-stroke-alignment-change index)}
(when (= (:stroke-alignment stroke) :multiple)
[:option {:value ""} "--"])
@@ -115,7 +116,8 @@
[:option {:value ":outer"} (tr "workspace.options.stroke.outer")]]
(when-not disable-stroke-style
- [:select#style.input-select {:value (enum->string (:stroke-style stroke))
+ [:select#style.input-select {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
+ :value (enum->string (:stroke-style stroke))
:on-change (on-stroke-style-change index)}
(when (= (:stroke-style stroke) :multiple)
[:option {:value ""} "--"])
diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs
index dfe8769f6..a4db28c8b 100644
--- a/frontend/src/app/util/dom.cljs
+++ b/frontend/src/app/util/dom.cljs
@@ -166,6 +166,13 @@
current
(recur (.-parentElement current) (dec current-count))))))
+(defn get-parent-with-data
+ [^js node name]
+ (loop [current node]
+ (if (or (nil? current) (obj/in? (.-dataset current) name))
+ current
+ (recur (.-parentElement current)))))
+
(defn get-parent-with-selector
[^js node selector]
(loop [current node]