Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-04-23 08:59:33 +02:00
commit 40fe6369cb
15 changed files with 174 additions and 77 deletions

View file

@ -40,6 +40,8 @@
- Fix several issues with internal srepl helpers - Fix several issues with internal srepl helpers
- Fix unexpected exception on template import from libraries - Fix unexpected exception on template import from libraries
- Fix incorrect uuid parsing from different parts of code - Fix incorrect uuid parsing from different parts of code
- Fix update layout on component restore [Taiga #10637](https://tree.taiga.io/project/penpot/issue/10637)
- Fix horizontal scroll in viewer [Github #6290](https://github.com/penpot/penpot/issues/6290)
## 2.6.1 ## 2.6.1

View file

@ -0,0 +1,63 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.binfile.cleaner
"A collection of helpers for perform cleaning of artifacts; mainly
for recently imported shapes."
(:require
[app.common.data :as d]
[app.common.uuid :as uuid]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRE DECODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn clean-shape-pre-decode
"Applies a pre-decode phase migration to the shape"
[shape]
(if (= "bool" (:type shape))
(if-let [content (get shape :bool-content)]
(-> shape
(assoc :content content)
(dissoc :bool-content))
shape)
shape))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; POST DECODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- fix-shape-shadow-color
"Some shapes can come with invalid `id` property on shadow colors
caused by incorrect uuid parsing bug that should be already fixed;
this function removes the invalid id from the data structure."
[shape]
(let [fix-color
(fn [{:keys [id] :as color}]
(if (uuid? id)
color
(if (and (string? id)
(re-matches uuid/regex id))
(assoc color :id (uuid/uuid id))
(dissoc color :id))))
fix-shadow
(fn [shadow]
(d/update-when shadow :color fix-color))
xform
(map fix-shadow)]
(d/update-when shape :shadow
(fn [shadows]
(into [] xform shadows)))))
(defn clean-shape-post-decode
"A shape procesor that expected to be executed after schema decoding
process but before validation."
[shape]
(-> shape
(fix-shape-shadow-color)))

View file

@ -8,6 +8,7 @@
"A ZIP based binary file exportation" "A ZIP based binary file exportation"
(:refer-clojure :exclude [read]) (:refer-clojure :exclude [read])
(:require (:require
[app.binfile.cleaner :as bfl]
[app.binfile.common :as bfc] [app.binfile.common :as bfc]
[app.binfile.migrations :as bfm] [app.binfile.migrations :as bfm]
[app.common.data :as d] [app.common.data :as d]
@ -592,6 +593,38 @@
{}) {})
(not-empty))) (not-empty)))
(defn- read-file-components
[{:keys [::bfc/input ::file-id ::entries]}]
(let [clean-component-post-decode
(fn [component]
(d/update-when component :objects
(fn [objects]
(reduce-kv (fn [objects id shape]
(assoc objects id (bfl/clean-shape-post-decode shape)))
objects
objects))))
clean-component-pre-decode
(fn [component]
(d/update-when component :objects
(fn [objects]
(reduce-kv (fn [objects id shape]
(assoc objects id (bfl/clean-shape-pre-decode shape)))
objects
objects))))]
(->> (keep (match-component-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry)
(clean-component-pre-decode)
(decode-component)
(clean-component-post-decode)
(validate-component))]
(if (= id (:id object))
(assoc result id object)
result)))
{})
(not-empty))))
(defn- read-file-typographies (defn- read-file-typographies
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::file-id ::entries]}]
(->> (keep (match-typography-entry-fn file-id) entries) (->> (keep (match-typography-entry-fn file-id) entries)
@ -612,49 +645,15 @@
(decode-tokens-lib) (decode-tokens-lib)
(validate-tokens-lib)))) (validate-tokens-lib))))
(defn- pre-decode-migrate-shape
"Applies a pre-decode phase migration to the shape"
[shape]
(if (= "bool" (:type shape))
(if-let [content (get shape :bool-content)]
(-> shape
(assoc :content content)
(dissoc :bool-content))
shape)
shape))
(defn- pre-decode-migrate-component
"Applies a pre-decode phase migration to component"
[component]
(d/update-when component :objects
(fn [objects]
(reduce-kv (fn [objects id shape]
(assoc objects id (pre-decode-migrate-shape shape)))
objects
objects))))
(defn- read-file-components
[{:keys [::bfc/input ::file-id ::entries]}]
(->> (keep (match-component-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry)
(pre-decode-migrate-component)
(decode-component)
(validate-component))]
(if (= id (:id object))
(assoc result id object)
result)))
{})
(not-empty)))
(defn- read-file-shapes (defn- read-file-shapes
[{:keys [::bfc/input ::file-id ::page-id ::entries] :as cfg}] [{:keys [::bfc/input ::file-id ::page-id ::entries] :as cfg}]
(->> (keep (match-shape-entry-fn file-id page-id) entries) (->> (keep (match-shape-entry-fn file-id page-id) entries)
(reduce (fn [result {:keys [id entry]}] (reduce (fn [result {:keys [id entry]}]
(let [object (-> (read-entry input entry) (let [object (->> (read-entry input entry)
(pre-decode-migrate-shape) (bfl/clean-shape-pre-decode)
(decode-shape) (decode-shape)
(validate-shape))] (bfl/clean-shape-post-decode)
(validate-shape))]
(if (= id (:id object)) (if (= id (:id object))
(assoc result id object) (assoc result id object)
result))) result)))

View file

@ -238,6 +238,7 @@
(db/update! conn :file (db/update! conn :file
{:data (blob/encode (:data file)) {:data (blob/encode (:data file))
:version (:version file) :version (:version file)
:modified-at (dt/now)
:features (db/create-array conn "text" (:features file))} :features (db/create-array conn "text" (:features file))}
{:id id}) {:id id})

View file

@ -180,8 +180,7 @@
(def ^:private (def ^:private
schema:get-file-data-for-thumbnail schema:get-file-data-for-thumbnail
[:map {:title "get-file-data-for-thumbnail"} [:map {:title "get-file-data-for-thumbnail"}
[:file-id ::sm/uuid] [:file-id ::sm/uuid]])
[:features {:optional true} ::cfeat/features]])
(def ^:private (def ^:private
schema:partial-file schema:partial-file
@ -211,7 +210,6 @@
(fmg/migrate-file)))] (fmg/migrate-file)))]
(-> (cfeat/get-team-enabled-features cf/flags team) (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file))) (cfeat/check-file-features! (:features file)))
{:file-id file-id {:file-id file-id

View file

@ -28,6 +28,7 @@
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.shadow :as ctss] [app.common.types.shape.shadow :as ctss]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[clojure.set :as set] [clojure.set :as set]
@ -847,9 +848,6 @@
(update :pages-index update-vals update-container) (update :pages-index update-vals update-container)
(update :components update-vals update-container)))) (update :components update-vals update-container))))
(def ^:private valid-shadow?
(sm/lazy-validator ::ctss/shadow))
(defmethod migrate-data "legacy-44" (defmethod migrate-data "legacy-44"
[data _] [data _]
(letfn [(fix-shadow [shadow] (letfn [(fix-shadow [shadow]
@ -861,7 +859,7 @@
(update-object [object] (update-object [object]
(let [xform (comp (map fix-shadow) (let [xform (comp (map fix-shadow)
(filter valid-shadow?))] (filter ctss/valid-shadow?))]
(d/update-when object :shadow #(into [] xform %)))) (d/update-when object :shadow #(into [] xform %))))
(update-container [container] (update-container [container]
@ -1036,7 +1034,7 @@
(update-shape [shape] (update-shape [shape]
(let [xform (comp (map fix-shadow) (let [xform (comp (map fix-shadow)
(filter valid-shadow?))] (filter ctss/valid-shadow?))]
(d/update-when shape :shadow #(into [] xform %)))) (d/update-when shape :shadow #(into [] xform %))))
(update-container [container] (update-container [container]
@ -1207,11 +1205,11 @@
object)) object))
(update-container [container] (update-container [container]
(d/update-when container :objects update-vals update-object))] (d/update-when container :objects d/update-vals update-object))]
(-> data (-> data
(update :pages-index update-vals update-container) (update :pages-index d/update-vals update-container)
(update :components update-vals update-container)))) (update :components d/update-vals update-container))))
(defmethod migrate-data "legacy-67" (defmethod migrate-data "legacy-67"
[data _] [data _]
@ -1222,8 +1220,8 @@
(d/update-when container :objects update-vals update-object))] (d/update-when container :objects update-vals update-object))]
(-> data (-> data
(update :pages-index update-vals update-container) (update :pages-index d/update-vals update-container)
(update :components update-vals update-container)))) (update :components d/update-vals update-container))))
(defmethod migrate-data "0001-remove-tokens-from-groups" (defmethod migrate-data "0001-remove-tokens-from-groups"
[data _] [data _]
@ -1237,8 +1235,33 @@
(dissoc :applied-tokens))) (dissoc :applied-tokens)))
(update-page [page] (update-page [page]
(d/update-when page :objects update-vals update-object))] (d/update-when page :objects d/update-vals update-object))]
(update data :pages-index update-vals update-page)))
(update data :pages-index d/update-vals update-page)))
(defmethod migrate-data "0002-clean-shape-interactions"
[data _]
(let [decode-fn (sm/decoder ctsi/schema:interaction sm/json-transformer)
validate-fn (sm/validator ctsi/schema:interaction)
xform
(comp
(map decode-fn)
(filter validate-fn))
update-object
(fn [object]
(d/update-when object :interactions
(fn [interactions]
(into [] xform interactions))))
update-container
(fn [container]
(d/update-when container :objects d/update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
(update :components d/update-vals update-container))))
(defmethod migrate-data "0002-normalize-bool-content" (defmethod migrate-data "0002-normalize-bool-content"
[data _] [data _]
@ -1315,4 +1338,5 @@
"legacy-66" "legacy-66"
"legacy-67" "legacy-67"
"0001-remove-tokens-from-groups" "0001-remove-tokens-from-groups"
"0002-normalize-bool-content"])) "0002-normalize-bool-content"
"0002-clean-shape-interactions"]))

View file

@ -864,7 +864,7 @@
choices))] choices))]
{:pred pred {:pred pred
:type-properties :type-properties
{:title "contains" {:title "contains any"
:description "contains predicate"}}))}) :description "contains predicate"}}))})
(register! (register!

View file

@ -212,7 +212,7 @@
[:interactions {:optional true} [:interactions {:optional true}
[:vector {:gen/max 2} ::ctsi/interaction]] [:vector {:gen/max 2} ::ctsi/interaction]]
[:shadow {:optional true} [:shadow {:optional true}
[:vector {:gen/max 1} ::ctss/shadow]] [:vector {:gen/max 1} ctss/schema:shadow]]
[:blur {:optional true} ::ctsb/blur] [:blur {:optional true} ::ctsb/blur]
[:grow-type {:optional true} [:grow-type {:optional true}
[::sm/one-of grow-types]] [::sm/one-of grow-types]]

View file

@ -26,7 +26,9 @@
[:hidden :boolean] [:hidden :boolean]
[:color ::ctc/color]]) [:color ::ctc/color]])
(sm/register! ::shadow schema:shadow)
(def check-shadow (def check-shadow
(sm/check-fn schema:shadow)) (sm/check-fn schema:shadow))
(def valid-shadow?
(sm/validator schema:shadow))

View file

@ -579,8 +579,13 @@
ldata (dsh/lookup-file-data state library-id) ldata (dsh/lookup-file-data state library-id)
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
(cll/generate-restore-component ldata component-id library-id page objects))] (cll/generate-restore-component ldata component-id library-id page objects))
(rx/of (dch/commit-changes changes))))))
frames
(->> changes :redo-changes (keep :frame-id))]
(rx/of (dch/commit-changes changes)
(ptk/data-event :layout/update {:ids frames}))))))
(defn restore-components (defn restore-components

View file

@ -89,14 +89,16 @@
(mf/with-effect [file-id revn visible? thumbnail-id] (mf/with-effect [file-id revn visible? thumbnail-id]
(when (and visible? (not thumbnail-id)) (when (and visible? (not thumbnail-id))
(->> (ask-for-thumbnail file-id revn) (let [subscription
(rx/subs! (fn [thumbnail-id] (->> (ask-for-thumbnail file-id revn)
(st/emit! (dd/set-file-thumbnail file-id thumbnail-id))) (rx/subs! (fn [thumbnail-id]
(fn [cause] (st/emit! (dd/set-file-thumbnail file-id thumbnail-id)))
(log/error :hint "unable to render thumbnail" (fn [cause]
:file-if file-id (log/error :hint "unable to render thumbnail"
:revn revn :file-if file-id
:message (ex-message cause))))))) :revn revn
:message (ex-message cause)))))]
(partial rx/dispose! subscription))))
[:div {:class (stl/css :grid-item-th) [:div {:class (stl/css :grid-item-th)
:style {:background-color bg-color} :style {:background-color bg-color}

View file

@ -117,6 +117,7 @@
padding-right: 0 $s-8 $s-40 $s-8; padding-right: 0 $s-8 $s-40 $s-8;
transition: transform 400ms ease 300ms; transition: transform 400ms ease 300ms;
z-index: $z-index-2; z-index: $z-index-2;
pointer-events: none;
} }
.reset-button { .reset-button {

View file

@ -432,7 +432,7 @@
(let [id (obj/get self "$id") (let [id (obj/get self "$id")
value (mapv #(shadow-defaults (parser/parse-shadow %)) value)] value (mapv #(shadow-defaults (parser/parse-shadow %)) value)]
(cond (cond
(not (sm/validate [:vector ::ctss/shadow] value)) (not (sm/validate [:vector ctss/schema:shadow] value))
(u/display-not-valid :shadows value) (u/display-not-valid :shadows value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))

View file

@ -50,7 +50,8 @@
[headers] [headers]
(into {} (map vec) (seq (.entries ^js headers)))) (into {} (map vec) (seq (.entries ^js headers))))
(def default-headers (defn default-headers
[]
{"x-frontend-version" (:full cfg/version)}) {"x-frontend-version" (:full cfg/version)})
(defn fetch (defn fetch
@ -74,7 +75,7 @@
headers (cond-> headers headers (cond-> headers
(not omit-default-headers) (not omit-default-headers)
(d/merge default-headers)) (merge (default-headers)))
headers (-update-headers body headers) headers (-update-headers body headers)

View file

@ -42,12 +42,11 @@
:http-body body}))) :http-body body})))
(defn- request-data-for-thumbnail (defn- request-data-for-thumbnail
[file-id revn features] [file-id revn]
(let [path "api/rpc/command/get-file-data-for-thumbnail" (let [path "api/rpc/command/get-file-data-for-thumbnail"
params {:file-id file-id params {:file-id file-id
:revn revn :revn revn
:strip-frames-with-thumbnails true :strip-frames-with-thumbnails true}
:features features}
request {:method :get request {:method :get
:uri (u/join cf/public-uri path) :uri (u/join cf/public-uri path)
:credentials "include" :credentials "include"
@ -86,6 +85,6 @@
nil))) nil)))
(defmethod impl/handler :thumbnails/generate-for-file (defmethod impl/handler :thumbnails/generate-for-file
[{:keys [file-id revn features] :as message} _] [{:keys [file-id revn] :as message} _]
(->> (request-data-for-thumbnail file-id revn features) (->> (request-data-for-thumbnail file-id revn)
(rx/map render-thumbnail))) (rx/map render-thumbnail)))