diff --git a/CHANGES.md b/CHANGES.md index abefc839d6..6057916df2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -52,6 +52,13 @@ - Fix blur input field when click on viewport [Taiga #2164](https://tree.taiga.io/project/penpot/issue/2164) - Fix default page id in workspace [Taiga #2205](https://tree.taiga.io/project/penpot/issue/2205) - Fix problem when importing a file with grids [Taiga #2314](https://tree.taiga.io/project/penpot/issue/2314) +- Fix problem with imported svgs with filters [Taiga #2478](https://tree.taiga.io/project/penpot/issue/2478) +- Fix issues when updating selrect in paths [Taiga #2366](https://tree.taiga.io/project/penpot/issue/2366) +- Fix scroll jumps in handoff mode [Taiga #2383](https://tree.taiga.io/project/penpot/issue/2383) +- Fix handoff text with opacity [Taiga #2384](https://tree.taiga.io/project/penpot/issue/2384) +- Restored rules color [Taiga #2460](https://tree.taiga.io/project/penpot/issue/2460) +- Fix thumbnail not taking frame blending mode [Taiga #2301](https://tree.taiga.io/project/penpot/issue/2301) +- Fix import/export with SVG edge cases [Taiga #2389](https://tree.taiga.io/project/penpot/issue/2389) ### :arrow_up: Deps updates diff --git a/common/src/app/common/colors.cljc b/common/src/app/common/colors.cljc index fe194da319..f8acee0d3f 100644 --- a/common/src/app/common/colors.cljc +++ b/common/src/app/common/colors.cljc @@ -11,6 +11,8 @@ (def canvas "#E8E9EA") (def default-layout "#DE4762") (def gray-20 "#B1B2B5") +(def gray-30 "#7B7D85") (def info "#59B9E2") (def test "#fabada") (def white "#FFFFFF") + diff --git a/frontend/src/app/main/data/workspace/path/changes.cljs b/frontend/src/app/main/data/workspace/path/changes.cljs index ba0700bf3f..c31745babd 100644 --- a/frontend/src/app/main/data/workspace/path/changes.cljs +++ b/frontend/src/app/main/data/workspace/path/changes.cljs @@ -29,7 +29,12 @@ [old-points old-selrect] (helpers/content->points+selrect shape old-content) [new-points new-selrect] (helpers/content->points+selrect shape new-content) - rch (if (empty? new-content) + rch (cond + ;; https://tree.taiga.io/project/penpot/issue/2366 + (nil? shape-id) + [] + + (empty? new-content) [{:type :del-obj :id shape-id :page-id page-id} @@ -37,6 +42,7 @@ :page-id page-id :shapes [shape-id]}] + :else [{:type :mod-obj :id shape-id :page-id page-id @@ -47,7 +53,12 @@ :page-id page-id :shapes [shape-id]}]) - uch (if (empty? new-content) + uch (cond + ;; https://tree.taiga.io/project/penpot/issue/2366 + (nil? shape-id) + [] + + (empty? new-content) [{:type :add-obj :id shape-id :obj shape @@ -58,6 +69,8 @@ {:type :reg-objects :page-id page-id :shapes [shape-id]}] + + :else [{:type :mod-obj :id shape-id :page-id page-id diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 318643a03d..a702d522d9 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -32,7 +32,6 @@ [app.util.strings :as ust] [app.util.timers :as ts] [cuerdas.core :as str] - [debug :refer [debug?]] [rumext.alpha :as mf])) (def ^:const viewbox-decimal-precision 3) @@ -195,14 +194,9 @@ (let [frame? (= (:type item) :frame)] (cond (and frame? thumbnails? (some? (:thumbnail item))) - [:image {:xlinkHref (:thumbnail item) - :x (:x item) - :y (:y item) - :width (:width item) - :height (:height item) - ;; DEBUG - :style {:filter (when (debug? :thumbnails) "sepia(1)")} - }] + [:> shape-container {:shape item} + [:& frame/frame-thumbnail {:shape item}]] + frame? [:& frame-wrapper {:shape item :key (:id item)}] diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 8c72f72943..ae5967d1ba 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -179,25 +179,28 @@ [attrs styles])) (defn add-style-attrs - [props shape] - (let [render-id (mf/use-ctx muc/render-ctx) - svg-defs (:svg-defs shape {}) - svg-attrs (:svg-attrs shape {}) + ([props shape] + (let [render-id (mf/use-ctx muc/render-ctx)] + (add-style-attrs props shape render-id))) - [svg-attrs svg-styles] (mf/use-memo - (mf/deps render-id svg-defs svg-attrs) - #(extract-svg-attrs render-id svg-defs svg-attrs)) + ([props shape render-id] + (let [svg-defs (:svg-defs shape {}) + svg-attrs (:svg-attrs shape {}) - styles (-> (obj/get props "style" (obj/new)) - (obj/merge! svg-styles) - (add-fill shape render-id) - (add-stroke shape render-id) - (add-layer-props shape))] + [svg-attrs svg-styles] (mf/use-memo + (mf/deps render-id svg-defs svg-attrs) + #(extract-svg-attrs render-id svg-defs svg-attrs)) - (-> props - (obj/merge! svg-attrs) - (add-border-radius shape) - (obj/set! "style" styles)))) + styles (-> (obj/get props "style" (obj/new)) + (obj/merge! svg-styles) + (add-fill shape render-id) + (add-stroke shape render-id) + (add-layer-props shape))] + + (-> props + (obj/merge! svg-attrs) + (add-border-radius shape) + (obj/set! "style" styles))))) (defn extract-style-attrs [shape] diff --git a/frontend/src/app/main/ui/shapes/export.cljs b/frontend/src/app/main/ui/shapes/export.cljs index 9f26a0712a..f794f687d1 100644 --- a/frontend/src/app/main/ui/shapes/export.cljs +++ b/frontend/src/app/main/ui/shapes/export.cljs @@ -195,6 +195,12 @@ :penpot:suffix suffix :penpot:scale (str scale)}])) +(defn style->str + [style] + (->> style + (map (fn [[key val]] (str (d/name key) ":" val))) + (str/join "; "))) + (mf/defc export-svg-data [shape] [:* (when (contains? shape :svg-attrs) @@ -203,6 +209,10 @@ svg-defs (->> shape :svg-defs keys (mapv d/name) (str/join ","))] [:> "penpot:svg-import" #js {:penpot:svg-attrs (when-not (empty? svg-attrs) svg-attrs) + ;; Style and filter are special properties so we need to save it otherwise will be indistingishible from + ;; standard properties + :penpot:svg-style (when (contains? (:svg-attrs shape) :style) (style->str (get-in shape [:svg-attrs :style]))) + :penpot:svg-filter (when (contains? (:svg-attrs shape) :filter) (get-in shape [:svg-attrs :filter])) :penpot:svg-defs (when-not (empty? svg-defs) svg-defs) :penpot:svg-transform (when svg-transform (str svg-transform)) :penpot:svg-viewbox-x (get-in shape [:svg-viewbox :x]) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index bf49ca432d..cdb4aea31c 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -8,6 +8,7 @@ (:require [app.main.ui.shapes.attrs :as attrs] [app.util.object :as obj] + [debug :refer [debug?]] [rumext.alpha :as mf])) (defn frame-clip-id @@ -26,6 +27,21 @@ [:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"} [:rect {:x x :y y :width width :height height}]]))) +(mf/defc frame-thumbnail + {::mf/wrap-props false} + [props] + (let [shape (obj/get props "shape")] + (when (:thumbnail shape) + [:image.frame-thumbnail + {:id (str "thumbnail-" (:id shape)) + :xlinkHref (:thumbnail shape) + :x (:x shape) + :y (:y shape) + :width (:width shape) + :height (:height shape) + ;; DEBUG + :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) + (defn frame-shape [shape-wrapper] (mf/fnc frame-shape diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index 961a30e294..cfb73aad9d 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -53,7 +53,7 @@ (obj/set! "clipPath" (frame/frame-clip-url shape render-id)) (= :group type) - (attrs/add-style-attrs shape))] + (attrs/add-style-attrs shape render-id))] [:& (mf/provider muc/render-ctx) {:value render-id} [:> :g wrapper-props diff --git a/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs index 48aa1a5aae..6dd87c2a76 100644 --- a/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs @@ -59,7 +59,7 @@ (mf/deps selected) (fn [] (when (and (= (count selected) 1) selected?) - (.scrollIntoView (mf/ref-val item-ref) false)))) + (dom/scroll-into-view-if-needed! (mf/ref-val item-ref) true)))) [:li {:ref item-ref :class (dom/classnames diff --git a/frontend/src/app/main/ui/workspace/rules.cljs b/frontend/src/app/main/ui/workspace/rules.cljs index 45a8cd0d99..a9d5b34b2e 100644 --- a/frontend/src/app/main/ui/workspace/rules.cljs +++ b/frontend/src/app/main/ui/workspace/rules.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.rules (:require + [app.common.colors :as colors] [app.common.math :as mth] [app.util.object :as obj] [rumext.alpha :as mf])) @@ -47,8 +48,8 @@ (.translate dctx 0 txfm)) (obj/set! dctx "font" "12px worksans") - (obj/set! dctx "fillStyle" "var(--color-gray-30)") - (obj/set! dctx "strokeStyle" "var(--color-gray-30)") + (obj/set! dctx "fillStyle" colors/gray-30) + (obj/set! dctx "strokeStyle" colors/gray-30) (obj/set! dctx "textAlign" "center") (loop [i minv] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 827462f0c8..3651012c06 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -15,7 +15,6 @@ [app.util.object :as obj] [app.util.timers :as ts] [beicon.core :as rx] - [debug :refer [debug?]] [rumext.alpha :as mf])) (defn check-frame-props @@ -36,27 +35,12 @@ (= new-thumbnail? old-thumbnail?) (= new-children old-children)))) -(mf/defc thumbnail - {::mf/wrap-props false} - [props] - (let [shape (obj/get props "shape")] - (when (:thumbnail shape) - [:image.frame-thumbnail - {:id (str "thumbnail-" (:id shape)) - :xlinkHref (:thumbnail shape) - :x (:x shape) - :y (:y shape) - :width (:width shape) - :height (:height shape) - ;; DEBUG - :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) - (mf/defc frame-placeholder {::mf/wrap-props false} [props] (let [{:keys [x y width height fill-color] :as shape} (obj/get props "shape")] (if (some? (:thumbnail shape)) - [:& thumbnail {:shape shape}] + [:& frame/frame-thumbnail {:shape shape}] [:rect {:x x :y y :width width :height height :style {:fill (or fill-color "var(--color-white)")}}]))) (defn custom-deferred @@ -128,7 +112,7 @@ [:> shape-container {:shape shape} [:& ff/fontfaces-style {:shapes all-children}] (if show-thumbnail? - [:& thumbnail {:shape shape}] + [:& frame/frame-thumbnail {:shape shape}] [:& deferred-frame-shape {:shape shape :childs children}])]]))))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0da6e0fa07..e331f149be 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -33,6 +33,7 @@ [app.main.ui.workspace.viewport.utils :as utils] [app.main.ui.workspace.viewport.widgets :as widgets] [beicon.core :as rx] + [debug :refer [debug?]] [rumext.alpha :as mf])) ;; --- Viewport @@ -192,7 +193,7 @@ [:& use/export-page {:options options}] - [:& (mf/provider use/include-metadata-ctx) {:value false} + [:& (mf/provider use/include-metadata-ctx) {:value (debug? :show-export-metadata)} [:& (mf/provider embed/context) {:value true} ;; Render root shape [:& shapes/root-shape {:key page-id diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs index a4069406c3..19978d5a89 100644 --- a/frontend/src/app/util/code_gen.cljs +++ b/frontend/src/app/util/code_gen.cljs @@ -180,8 +180,9 @@ shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge)) shape-format (->> text-shape-style vals (map :format) (reduce merge)) - - text-values (->> (search-text-attrs (:content shape) (conj (:props style-text) :fill-color-gradient)) + text-values (->> (search-text-attrs + (:content shape) + (conj (:props style-text) :fill-color-gradient :fill-opacity)) (d/merge txt/default-text-attrs))] (str/join "\n" diff --git a/frontend/src/app/util/import/parser.cljs b/frontend/src/app/util/import/parser.cljs index 8a51ff8710..f17b7f9158 100644 --- a/frontend/src/app/util/import/parser.cljs +++ b/frontend/src/app/util/import/parser.cljs @@ -22,6 +22,9 @@ (def uuid-regex #"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}") +(def uuid-regex-prefix + #"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}-") + (defn valid? [root] (contains? (:attrs root) :xmlns:penpot)) @@ -554,20 +557,31 @@ (defn remove-prefix [s] (cond-> s (string? s) - (str/replace (re-pattern (str uuid-regex "-")) ""))) + (str/replace uuid-regex-prefix ""))) (defn get-svg-attrs - [svg-data svg-attrs] - (let [assoc-key + [svg-import svg-data svg-attrs] + (let [process-attr (fn [acc prop] - (let [key (keyword prop)] - (if-let [v (or (get svg-data key) - (get-in svg-data [:attrs key]))] - (assoc acc key (remove-prefix v)) - acc)))] + (cond + (and (= prop "style") + (contains? (:attrs svg-import) :penpot:svg-style)) + (let [style (get-in svg-import [:attrs :penpot:svg-style])] + (assoc acc :style (parse-style style))) + (and (= prop "filter") + (contains? (:attrs svg-import) :penpot:svg-filter)) + (let [style (get-in svg-import [:attrs :penpot:svg-filter])] + (assoc acc :filter (parse-style style))) + + :else + (let [key (keyword prop)] + (if-let [v (or (get svg-data key) + (get-in svg-data [:attrs key]))] + (assoc acc key (remove-prefix v)) + acc))))] (->> (str/split svg-attrs ",") - (reduce assoc-key {})))) + (reduce process-attr {})))) (defn get-svg-defs [node] @@ -595,7 +609,7 @@ (cond-> props :true - (assoc :svg-attrs (get-svg-attrs svg-data svg-attrs)) + (assoc :svg-attrs (get-svg-attrs svg-import svg-data svg-attrs)) (some? viewbox-x) (assoc :svg-viewbox {:x (d/parse-double viewbox-x) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index c70bce1ed4..d46e0b35c4 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -46,6 +46,9 @@ ;; When active the thumbnails will be displayed with a sepia filter :thumbnails + + ;; When active we can check in the browser the export values + :show-export-metadata }) ;; These events are excluded when we activate the :events flag diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 18ac987f73..6410109940 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -265,10 +265,10 @@ msgid "dashboard.export-frames" msgstr "Export artboards to PDF..." msgid "dashboard.export-multi" -msgstr "Export %s files" +msgstr "Export Penpot %s files" msgid "dashboard.export-single" -msgstr "Export file" +msgstr "Export Penpot file" msgid "dashboard.export.detail" msgstr "* Might include components, graphics, colors and/or typographies." @@ -343,7 +343,7 @@ msgstr "" "[font licensing](https://www.typography.com/faq)." msgid "dashboard.import" -msgstr "Import files" +msgstr "Import Penpot files" msgid "dashboard.import.analyze-error" msgstr "Oops! We couldn't import this file" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 2059f19a0b..c7643a39f6 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -269,10 +269,10 @@ msgid "dashboard.export-frames" msgstr "Exportar tableros a PDF..." msgid "dashboard.export-multi" -msgstr "Exportar %s archivos" +msgstr "Exportar %s archivos Penpot" msgid "dashboard.export-single" -msgstr "Exportar archivo" +msgstr "Exportar archivo Penpot" msgid "dashboard.export.detail" msgstr "* Pueden incluir components, gráficos, colores y/o tipografias." @@ -349,7 +349,7 @@ msgstr "" "licensing](https://www.typography.com/faq)." msgid "dashboard.import" -msgstr "Importar archivos" +msgstr "Importar archivos Penpot" msgid "dashboard.import.analyze-error" msgstr "¡Vaya! No hemos podido importar el fichero"