diff --git a/CHANGES.md b/CHANGES.md
index f4fd816eba..0e75d2527f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,6 +17,7 @@
- Optimize sidebar performance for deeply nested shapes [Taiga #13017](https://tree.taiga.io/project/penpot/task/13017)
- Remove tokens path node and bulk remove tokens [Taiga #13007](https://tree.taiga.io/project/penpot/us/13007)
- Replace themes management modal radio buttons for switches [Taiga #9215](https://tree.taiga.io/project/penpot/us/9215)
+- [Access Tokens] Look & feel refinement [Taiga #13114](https://tree.taiga.io/project/penpot/us/13114)
### :bug: Bugs fixed
@@ -35,12 +36,18 @@
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
- Fix remove fill affects different element than selected [Taiga #13128](https://tree.taiga.io/project/penpot/issue/13128)
+## 2.13.3
+
+### :bug: Bugs fixed
+
+- Revert yetti (http server) update, because that caused a regression on multipart uploads
+
## 2.13.2
### :bug: Bugs fixed
- Fix modifying shapes by apply negative tokens to border radius [Taiga #13317](https://tree.taiga.io/project/penpot/issue/13317)
-- Fix security issue (Path Traversal Vulnerability) on fonts related RPC method
+- Fix arbitrary file read security issue on create-font-variant rpc method (https://github.com/penpot/penpot/security/advisories/GHSA-xp3f-g8rq-9px2)
## 2.13.1
diff --git a/backend/deps.edn b/backend/deps.edn
index af73aecbc8..27d052c510 100644
--- a/backend/deps.edn
+++ b/backend/deps.edn
@@ -28,8 +28,8 @@
com.google.guava/guava {:mvn/version "33.4.8-jre"}
funcool/yetti
- {:git/tag "v11.9"
- :git/sha "5fad7a9"
+ {:git/tag "v11.8"
+ :git/sha "1d1b33f"
:git/url "https://github.com/funcool/yetti.git"
:exclusions [org.slf4j/slf4j-api]}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
index ed6187e73f..655c00867d 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
@@ -867,15 +867,15 @@
:on-change on-change
:name (dm/str "flex-align-items-" type)
:options [{:id (dm/str "align-items-start-" type)
- :icon (get-layout-flex-icon :align-items :start is-column)
+ :icon (get-layout-grid-icon :align-items :start is-column)
:label "Align items start"
:value "start"}
{:id (dm/str "align-items-center-" type)
- :icon (get-layout-flex-icon :align-items :center is-column)
+ :icon (get-layout-grid-icon :align-items :center is-column)
:label "Align items center"
:value "center"}
{:id (dm/str "align-items-end-" type)
- :icon (get-layout-flex-icon :align-items :end is-column)
+ :icon (get-layout-grid-icon :align-items :end is-column)
:label "Align items end"
:value "end"}]}]))
diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs
index 3f8d655a5b..014736e4d8 100644
--- a/frontend/src/app/plugins/shape.cljs
+++ b/frontend/src/app/plugins/shape.cljs
@@ -175,6 +175,10 @@
(defn shape-proxy? [p]
(obj/type-of? p "ShapeProxy"))
+;; Cannot use token/token-proxy? here because of circular dependency in applyToShapes in token proxy
+(defn token-proxy? [t]
+ (obj/type-of? t "TokenProxy"))
+
(defn shape-proxy
([plugin-id id]
(shape-proxy plugin-id (:current-file-id @st/state) (:current-page-id @st/state) id))
@@ -1301,16 +1305,19 @@
tokens)))}
:applyToken
- (fn [token attrs]
- (let [token (u/locate-token file-id (obj/get token "$set-id") (obj/get token "$id"))
- kw-attrs (into #{} (map keyword attrs))]
- (if (some #(not (cto/token-attr? %)) kw-attrs)
- (u/display-not-valid :applyToken attrs)
- (st/emit!
- (dwta/toggle-token {:token token
- :attrs kw-attrs
- :shape-ids [id]
- :expand-with-children false})))))
+ {:schema [:tuple
+ [:fn token-proxy?]
+ [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
+ :fn (fn [token attrs]
+ (let [token (u/locate-token file-id (obj/get token "$set-id") (obj/get token "$id"))
+ kw-attrs (into #{} (map keyword attrs))]
+ (if (some #(not (cto/token-attr? %)) kw-attrs)
+ (u/display-not-valid :applyToken attrs)
+ (st/emit!
+ (dwta/toggle-token {:token token
+ :attrs kw-attrs
+ :shape-ids [id]
+ :expand-with-children false})))))}
:isVariantHead
(fn []
diff --git a/frontend/src/app/plugins/tokens.cljs b/frontend/src/app/plugins/tokens.cljs
index f46722884a..c11f8dc373 100644
--- a/frontend/src/app/plugins/tokens.cljs
+++ b/frontend/src/app/plugins/tokens.cljs
@@ -13,10 +13,10 @@
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid]
[app.main.data.style-dictionary :as sd]
+ [app.main.data.tokenscript :as ts]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.store :as st]
- ;; [app.plugins.shape :as shape]
[app.plugins.utils :as u]
[app.util.object :as obj]
[beicon.v2.core :as rx]
@@ -35,13 +35,25 @@
:shape-ids shape-ids
:expand-with-children false})))))
+(defn- get-resolved-value
+ [token tokens-tree]
+ (let [resolved-tokens (ts/resolve-tokens tokens-tree)
+ resolved-value (-> resolved-tokens
+ (dm/get-in [(:name token) :resolved-value])
+ (ts/tokenscript-symbols->penpot-unit))]
+ resolved-value))
+
(defn token-proxy? [p]
(obj/type-of? p "TokenProxy"))
+;; Cannot use shape/shape-proxy? here because of circular dependency in applyToken in shape proxy
+(defn shape-proxy? [s]
+ (obj/type-of? s "ShapeProxy"))
+
(defn token-proxy
[plugin-id file-id set-id id]
(obj/reify {:name "TokenProxy"
- :wrap u/wrap-errors}
+ :on-error u/handle-error}
:$plugin {:enumerable false :get (constantly plugin-id)}
:$file-id {:enumerable false :get (constantly file-id)}
:$set-id {:enumerable false :get (constantly set-id)}
@@ -82,6 +94,26 @@
(fn [_ value]
(st/emit! (dwtl/update-token set-id id {:value value})))}
+ :resolvedValue
+ {:this true
+ :enumerable false
+ :get
+ (fn [_]
+ (let [token (u/locate-token file-id set-id id)
+ tokens-lib (u/locate-tokens-lib file-id)
+ tokens-tree (ctob/get-tokens-in-active-sets tokens-lib)]
+ (get-resolved-value token tokens-tree)))}
+
+ :resolvedValueString
+ {:this true
+ :enumerable false
+ :get
+ (fn [_]
+ (let [token (u/locate-token file-id set-id id)
+ tokens-lib (u/locate-tokens-lib file-id)
+ tokens-tree (ctob/get-tokens-in-active-sets tokens-lib)]
+ (str (get-resolved-value token tokens-tree))))}
+
:description
{:this true
:get
@@ -113,14 +145,10 @@
:applyToShapes
{:schema [:tuple
- ;; FIXME: the schema decoder is interpreting the array of shape-proxys and converting
- ;; them to plain maps. For now we adapt the schema to accept it, but the decoder
- ;; should be fixed to keep the original proxy objects coming from the plugin.
- ;; [:vector [:fn shape/shape-proxy?]]
- [:vector [:map [:id ::sm/uuid]]]
- [:maybe [:set [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]]
+ [:vector [:fn shape-proxy?]]
+ [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
:fn (fn [shapes attrs]
- (apply-token-to-shapes file-id set-id id (map :id shapes) attrs))}
+ (apply-token-to-shapes file-id set-id id (map #(obj/get % "$id") shapes) attrs))}
:applyToSelected
{:schema [:tuple [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
@@ -136,7 +164,7 @@
(defn token-set-proxy
[plugin-id file-id id]
(obj/reify {:name "TokenSetProxy"
- :wrap u/wrap-errors}
+ :on-error u/handle-error}
:$plugin {:enumerable false :get (constantly plugin-id)}
:$file-id {:enumerable false :get (constantly file-id)}
:$id {:enumerable false :get (constantly id)}
@@ -257,7 +285,7 @@
(defn token-theme-proxy
[plugin-id file-id id]
(obj/reify {:name "TokenThemeProxy"
- :wrap u/wrap-errors}
+ :on-error u/handle-error}
:$plugin {:enumerable false :get (constantly plugin-id)}
:$file-id {:enumerable false :get (constantly file-id)}
:$id {:enumerable false :get (constantly id)}
@@ -352,7 +380,7 @@
(defn tokens-catalog
[plugin-id file-id]
(obj/reify {:name "TokensCatalog"
- :wrap u/wrap-errors}
+ :on-error u/handle-error}
:$plugin {:enumerable false :get (constantly plugin-id)}
:$id {:enumerable false :get (constantly file-id)}
diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs
index 8df47b2995..dfb8242a12 100644
--- a/frontend/src/app/plugins/utils.cljs
+++ b/frontend/src/app/plugins/utils.cljs
@@ -223,7 +223,9 @@
(defn display-not-valid
[code value]
- (.error js/console (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code))
+ (if (some? value)
+ (.error js/console (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code))
+ (.error js/console (dm/str "[PENPOT PLUGIN] Value not valid. Code: " code)))
nil)
(defn reject-not-valid
@@ -248,19 +250,12 @@
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))
-(defn wrap-errors
- "Function wrapper to be used in plugin proxies methods to handle errors.
- When an exception is thrown, a readable error message is output to the console
- and the exception is captured."
- [f]
- (fn []
- (let [args (js-arguments)]
- (try
- (.apply f nil args)
- (catch :default cause
- (display-not-valid (ex-message cause) (obj/stringify args))
- (if-let [explain (-> cause ex-data ::sm/explain)]
- (println (sm/humanize-explain explain))
- (js/console.log (ex-data cause)))
- (js/console.log (.-stack cause))
- nil)))))
\ No newline at end of file
+(defn handle-error
+ "Function to be used in plugin proxies methods to handle errors and print a readable
+ message to the console."
+ [cause]
+ (display-not-valid (ex-message cause) nil)
+ (if-let [explain (-> cause ex-data ::sm/explain)]
+ (println (sm/humanize-explain explain))
+ (js/console.log (ex-data cause)))
+ (js/console.log (.-stack cause)))
\ No newline at end of file
diff --git a/frontend/src/app/util/object.cljc b/frontend/src/app/util/object.cljc
index 723444b4d5..bb5ae6c5a4 100644
--- a/frontend/src/app/util/object.cljc
+++ b/frontend/src/app/util/object.cljc
@@ -10,6 +10,7 @@
(:refer-clojure :exclude [set! new get merge clone contains? array? into-array reify class])
#?(:cljs (:require-macros [app.util.object]))
(:require
+ [app.common.data :as d]
[app.common.json :as json]
[app.common.schema :as sm]
[clojure.core :as c]
@@ -156,6 +157,7 @@
this-sym (with-meta (gensym (str rsym "-this-")) {:tag 'js})
target-sym (with-meta (gensym (str rsym "-target-")) {:tag 'js})
+ cause-sym (gensym "cause-")
make-sym
(fn [pname prefix]
@@ -176,6 +178,7 @@
wrap (c/get params :wrap)
schema-1 (c/get params :schema-1)
this? (c/get params :this false)
+ on-error (c/get params :on-error)
decode-expr
(c/get params :decode/fn)
@@ -214,7 +217,16 @@
(with-meta {:tag 'function}))
val-sym
- (gensym (str "val-" (str/slug pname) "-"))]
+ (gensym (str "val-" (str/slug pname) "-"))
+
+ wrap-error-handling
+ (if on-error
+ (fn [expr]
+ `(try
+ ~expr
+ (catch :default ~cause-sym
+ (~on-error ~cause-sym))))
+ identity)]
(concat
(when wrap
@@ -226,8 +238,13 @@
`(fn []
(let [~this-sym (~'js* "this")
~fn-sym ~get-expr]
- (.call ~fn-sym ~this-sym ~this-sym)))
- get-expr)])
+ ~(wrap-error-handling
+ `(.call ~fn-sym ~this-sym ~this-sym))))
+ `(fn []
+ (let [~this-sym (~'js* "this")
+ ~fn-sym ~get-expr]
+ ~(wrap-error-handling
+ `(.call ~fn-sym ~this-sym)))))])
(when set-expr
[schema-sym schema-n
@@ -241,28 +258,35 @@
(make-sym pname "set-fn")
`(fn [~val-sym]
- (let [~this-sym (~'js* "this")
- ~fn-sym ~set-expr
+ ~(wrap-error-handling
+ `(let [~this-sym (~'js* "this")
+ ~fn-sym ~set-expr
- ;; We only emit schema and coercer bindings if
- ;; schema-n is provided
- ~@(if (some? schema-n)
- [schema-sym `(if (fn? ~schema-sym)
- (~schema-sym ~val-sym)
- ~schema-sym)
+ ;; We only emit schema and coercer bindings if
+ ;; schema-n is provided
+ ~@(if (some? schema-n)
+ [schema-sym
+ `(if (fn? ~schema-sym)
+ (~schema-sym ~val-sym)
+ ~schema-sym)
- coercer-sym `(if (nil? ~coercer-sym)
- (sm/coercer ~schema-sym)
- ~coercer-sym)
- val-sym (if (not= decode-expr 'app.common.json/->clj)
- `(~decode-sym ~val-sym)
- `(~decode-sym ~val-sym ~decode-options))
- val-sym `(~coercer-sym ~val-sym)]
- [])]
+ coercer-sym
+ `(if (nil? ~coercer-sym)
+ (sm/coercer ~schema-sym)
+ ~coercer-sym)
- ~(if this?
- `(.call ~fn-sym ~this-sym ~this-sym ~val-sym)
- `(.call ~fn-sym ~this-sym ~val-sym))))])
+ val-sym
+ (if (not= decode-expr 'app.common.json/->clj)
+ `(~decode-sym ~val-sym)
+ `(~decode-sym ~val-sym ~decode-options))
+
+ val-sym
+ `(~coercer-sym ~val-sym)]
+ [])]
+
+ ~(if this?
+ `(.call ~fn-sym ~this-sym ~this-sym ~val-sym)
+ `(.call ~fn-sym ~this-sym ~val-sym)))))])
(when fn-expr
[schema-sym (or schema-n schema-1)
@@ -275,7 +299,12 @@
(make-sym pname "get-fn")
`(fn []
(let [~this-sym (~'js* "this")
- ~fn-sym ~fn-expr
+ ~fn-sym ~(if (and (list? fn-expr)
+ (= 'fn (first fn-expr)))
+ (let [[sa sb & sother] fn-expr]
+ `(~sa ~sb ~(wrap-error-handling `(do ~@sother))))
+ fn-expr)
+
~fn-sym ~(if this?
`(.bind ~fn-sym ~this-sym ~this-sym)
`(.bind ~fn-sym ~this-sym))
@@ -284,25 +313,31 @@
;; schema-n or schema-1 is provided
~@(if (or schema-n schema-1)
[fn-sym `(fn* [~@(if schema-1 [val-sym] [])]
- (let [~@(if schema-n
- [val-sym `(into-array (cljs.core/js-arguments))]
- [])
- ~val-sym ~(if (not= decode-expr 'app.common.json/->clj)
- `(~decode-sym ~val-sym)
- `(~decode-sym ~val-sym ~decode-options))
+ ~(wrap-error-handling
+ `(let [~@(if schema-n
+ [val-sym `(into-array (cljs.core/js-arguments))]
+ [])
+ ~val-sym
+ ~(if (not= decode-expr 'app.common.json/->clj)
+ `(~decode-sym ~val-sym)
+ `(~decode-sym ~val-sym ~decode-options))
- ~schema-sym (if (fn? ~schema-sym)
- (~schema-sym ~val-sym)
- ~schema-sym)
+ ~schema-sym
+ (if (fn? ~schema-sym)
+ (~schema-sym ~val-sym)
+ ~schema-sym)
- ~coercer-sym (if (nil? ~coercer-sym)
- (sm/coercer ~schema-sym)
- ~coercer-sym)
+ ~coercer-sym
+ (if (nil? ~coercer-sym)
+ (sm/coercer ~schema-sym)
+ ~coercer-sym)
- ~val-sym (~coercer-sym ~val-sym)]
- ~(if schema-1
- `(~fn-sym ~val-sym)
- `(apply ~fn-sym ~val-sym))))]
+ ~val-sym
+ (~coercer-sym ~val-sym)]
+
+ ~(if schema-1
+ `(~fn-sym ~val-sym)
+ `(apply ~fn-sym ~val-sym)))))]
[])]
~(if wrap
`(~wrap-sym ~fn-sym)
@@ -375,12 +410,16 @@
(let [definition (first params)]
(if (some? definition)
(let [definition (if (map? definition)
- (c/merge {:wrap (:wrap tmeta)} definition)
+ (c/merge {:wrap (:wrap tmeta)
+ :on-error (:on-error tmeta)}
+ definition)
(-> {:enumerable false}
(c/merge (meta definition))
(assoc :wrap (:wrap tmeta))
+ (assoc :on-error (:on-error tmeta))
(assoc :fn definition)
- (dissoc :get :set)))
+ (dissoc :get :set :line :column)
+ (d/without-nils)))
definition (assoc definition :name (name ckey))]
(recur (rest params)
diff --git a/plugins/apps/poc-tokens-plugin/src/app/app.component.html b/plugins/apps/poc-tokens-plugin/src/app/app.component.html
index 5be709cb57..f5ef5ff940 100644
--- a/plugins/apps/poc-tokens-plugin/src/app/app.component.html
+++ b/plugins/apps/poc-tokens-plugin/src/app/app.component.html
@@ -113,7 +113,9 @@
class="body-m panel-item token-item"
(click)="applyToken(token.id)"
>
- {{ token.name }}
+
+ {{ token.name }}
+