diff --git a/CHANGES.md b/CHANGES.md index 200c09be70..affe7d1318 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,22 @@ # CHANGELOG -## 2.7.0 (Unreleased) + +## 2.7.2 (Unreleased) + +### :bug: Bugs fixed + +- Update plugins runtime [Github #6604](https://github.com/penpot/penpot/pull/6604) + + +## 2.7.1 + +### :bug: Bugs fixed + +- Fix incorrect handling of strokes with images on importing files +- Fix tokens disappearing after manual additions [Taiga #11063](https://tree.taiga.io/project/penpot/issue/11063) + + +## 2.7.0 ### :rocket: Epics and highlights @@ -21,6 +37,7 @@ ### :bug: Bugs fixed +- Fix "at" icon to match all icons on app [Taiga #11136](https://tree.taiga.io/project/penpot/issue/11136) - Fix problem in viewer with the back button [Taiga #10907](https://tree.taiga.io/project/penpot/issue/10907) - Fix resize bar background on tokens panel [Taiga #10811](https://tree.taiga.io/project/penpot/issue/10811) - Fix shortcut for history version panel [Taiga #11006](https://tree.taiga.io/project/penpot/issue/11006) @@ -47,6 +64,13 @@ - Fix cannot rename Design Token Sets when group of same name exists [Taiga Issue #10773](https://tree.taiga.io/project/penpot/issue/10773) - Fix problem when duplicating grid layout [Github #6391](https://github.com/penpot/penpot/issues/6391) - Fix issue that makes workspace shortcuts stop working [Taiga #11062](https://tree.taiga.io/project/penpot/issue/11062) +- Fix problem while syncing library colors and typographies [Taiga #11068](https://tree.taiga.io/project/penpot/issue/11068) +- Fix problem with path edition of shapes [Taiga #9496](https://tree.taiga.io/project/penpot/issue/9496) +- Fix exception on paste invalid html [Taiga #11047](https://tree.taiga.io/project/penpot/issue/11047) +- Fix share button being displayed with no permissions [Taiga #11086](https://tree.taiga.io/project/penpot/issue/11086) +- Fix inline styles in code tab [Taiga Issue #7583](https://tree.taiga.io/project/penpot/issue/7583) +- Fix exception on returning openapi.json +- Fix json encoding of TokensLib [Taiga #10994](https://tree.taiga.io/project/penpot/issue/10994) ## 2.6.2 diff --git a/backend/src/app/http/sse.clj b/backend/src/app/http/sse.clj index fdeda67368..7459d9224b 100644 --- a/backend/src/app/http/sse.clj +++ b/backend/src/app/http/sse.clj @@ -9,7 +9,6 @@ (:refer-clojure :exclude [tap]) (:require [app.common.data :as d] - [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.transit :as t] [app.http.errors :as errors] @@ -54,18 +53,20 @@ ::yres/status 200 ::yres/body (yres/stream-body (fn [_ output] - (binding [events/*channel* (sp/chan :buf buf :xf (keep encode))] - (let [listener (events/start-listener - (partial write! output) - (partial pu/close! output))] - (try + (let [channel (sp/chan :buf buf :xf (keep encode)) + listener (events/start-listener + channel + (partial write! output) + (partial pu/close! output))] + (try + (binding [events/*channel* channel] (let [result (handler)] - (events/tap :end result)) - (catch Throwable cause - (events/tap :error (errors/handle' cause request)) - (when-not (ex/instance? java.io.EOFException cause) - (binding [l/*context* (errors/request->context request)] - (l/err :hint "unexpected error on processing sse response" :cause cause)))) - (finally - (sp/close! events/*channel*) - (px/await! listener)))))))})) + (events/tap :end result))) + + (catch Throwable cause + (let [result (errors/handle' cause request)] + (events/tap channel :error result))) + + (finally + (sp/close! channel) + (px/await! listener))))))})) diff --git a/backend/src/app/rpc/commands/audit.clj b/backend/src/app/rpc/commands/audit.clj index f43195dd72..a9bed1db44 100644 --- a/backend/src/app/rpc/commands/audit.clj +++ b/backend/src/app/rpc/commands/audit.clj @@ -92,9 +92,9 @@ [:string {:max 250}] [::sm/one-of {:format "string"} valid-event-types]]] [:props - [:map-of :keyword :any]] + [:map-of :keyword ::sm/any]] [:context {:optional true} - [:map-of :keyword :any]]]) + [:map-of :keyword ::sm/any]]]) (def schema:push-audit-events [:map {:title "push-audit-events"} diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index 04f550676c..60b524ee38 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -115,7 +115,8 @@ (db/update! pool :project {:modified-at (dt/now)} - {:id project-id}) + {:id project-id} + {::db/return-keys false}) result)) diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index bdb2fcbc59..aff510d4cf 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -189,7 +189,7 @@ [:is-shared ::sm/boolean] [:project-id ::sm/uuid] [:created-at ::dt/instant] - [:data {:optional true} :any]]) + [:data {:optional true} ::sm/any]]) (def schema:permissions-mixin [:map {:title "PermissionsMixin"} diff --git a/backend/src/app/rpc/commands/fonts.clj b/backend/src/app/rpc/commands/fonts.clj index 43b90305e1..4c4e243669 100644 --- a/backend/src/app/rpc/commands/fonts.clj +++ b/backend/src/app/rpc/commands/fonts.clj @@ -80,9 +80,9 @@ (def ^:private schema:create-font-variant [:map {:title "create-font-variant"} [:team-id ::sm/uuid] - [:data [:map-of :string :any]] + [:data [:map-of ::sm/text ::sm/any]] [:font-id ::sm/uuid] - [:font-family :string] + [:font-family ::sm/text] [:font-weight [::sm/one-of {:format "number"} valid-weight]] [:font-style [::sm/one-of {:format "string"} valid-style]]]) diff --git a/backend/src/app/rpc/doc.clj b/backend/src/app/rpc/doc.clj index efd23ed44a..cba16e0ab5 100644 --- a/backend/src/app/rpc/doc.clj +++ b/backend/src/app/rpc/doc.clj @@ -9,6 +9,7 @@ (:require [app.common.data :as d] [app.common.exceptions :as ex] + [app.common.json :as json] [app.common.pprint :as pp] [app.common.schema :as sm] [app.common.schema.desc-js-like :as smdj] @@ -19,7 +20,6 @@ [app.http.sse :as-alias sse] [app.loggers.webhooks :as-alias webhooks] [app.rpc :as-alias rpc] - [app.util.json :as json] [app.util.services :as sv] [app.util.template :as tmpl] [clojure.java.io :as io] @@ -86,7 +86,7 @@ (fn [request] (let [params (:query-params request) pstyle (:type params "js") - context (assoc context :param-style pstyle)] + context (assoc @context :param-style pstyle)] {::yres/status 200 ::yres/body (-> (io/resource "app/templates/api-doc.tmpl") @@ -178,8 +178,7 @@ (fn [_] {::yres/status 200 ::yres/headers {"content-type" "application/json; charset=utf-8"} - ::yres/body (json/encode context)}) - + ::yres/body (json/encode @context)}) (fn [_] {::yres/status 404}))) @@ -209,7 +208,7 @@ (defmethod ig/init-key ::routes [_ {:keys [::rpc/methods] :as cfg}] - [(let [context (prepare-doc-context methods)] + [(let [context (delay (prepare-doc-context methods))] [["/_doc" {:handler (doc-handler context) :allowed-methods #{:get}}] @@ -217,7 +216,7 @@ {:handler (doc-handler context) :allowed-methods #{:get}}]]) - (let [context (prepare-openapi-context methods)] + (let [context (delay (prepare-openapi-context methods))] [["/openapi" {:handler (openapi-handler) :allowed-methods #{:get}}] diff --git a/backend/src/app/util/events.clj b/backend/src/app/util/events.clj index a41843c6b1..b26810cb04 100644 --- a/backend/src/app/util/events.clj +++ b/backend/src/app/util/events.clj @@ -10,7 +10,6 @@ to them. Mainly used in http.sse for progress reporting." (:refer-clojure :exclude [tap run!]) (:require - [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.logging :as l] [promesa.exec :as px] @@ -18,33 +17,30 @@ (def ^:dynamic *channel* nil) -(defn channel - [] - (sp/chan :buf 32)) - (defn tap - [type data] - (when-let [channel *channel*] - (sp/put! channel [type data]) - nil)) + ([type data] + (when-let [channel *channel*] + (sp/put! channel [type data]) + nil)) + ([channel type data] + (when channel + (sp/put! channel [type data]) + nil))) (defn start-listener - [on-event on-close] - - (dm/assert! - "expected active events channel" - (sp/chan? *channel*)) + [channel on-event on-close] + (assert (sp/chan? channel) "expected active events channel") (px/thread {:virtual true} (try (loop [] - (when-let [event (sp/take! *channel*)] + (when-let [event (sp/take! channel)] (let [result (ex/try! (on-event event))] (if (ex/exception? result) (do (l/wrn :hint "unexpected exception" :cause result) - (sp/close! *channel*)) + (sp/close! channel)) (recur))))) (finally (on-close))))) @@ -55,7 +51,7 @@ [f on-event] (binding [*channel* (sp/chan :buf 32)] - (let [listener (start-listener on-event (constantly nil))] + (let [listener (start-listener *channel* on-event (constantly nil))] (try (f) (finally diff --git a/common/deps.edn b/common/deps.edn index ae1a8868dc..713c54d004 100644 --- a/common/deps.edn +++ b/common/deps.edn @@ -28,7 +28,7 @@ integrant/integrant {:mvn/version "0.13.1"} funcool/tubax {:mvn/version "2021.05.20-0"} - funcool/cuerdas {:mvn/version "2023.11.09-407"} + funcool/cuerdas {:mvn/version "2025.05.26-411"} funcool/promesa {:git/sha "0c5ed6ad033515a2df4b55addea044f60e9653d0" :git/url "https://github.com/funcool/promesa"} diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 023491a9ca..d3ee9f2180 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -47,14 +47,14 @@ [:type [:= :assign]] ;; NOTE: the full decoding is happening on the handler because it ;; needs a proper context of the current shape and its type - [:value [:map-of :keyword :any]] + [:value [:map-of :keyword ::sm/any]] [:ignore-touched {:optional true} :boolean] [:ignore-geometry {:optional true} :boolean]]] [:set [:map {:title "SetOperation"} [:type [:= :set]] [:attr :keyword] - [:val :any] + [:val ::sm/any] [:ignore-touched {:optional true} :boolean] [:ignore-geometry {:optional true} :boolean]]] [:set-touched @@ -238,9 +238,9 @@ [:component-id {:optional true} ::sm/uuid] [:ignore-touched {:optional true} :boolean] [:parent-id ::sm/uuid] - [:shapes :any] + [:shapes ::sm/any] [:index {:optional true} [:maybe :int]] - [:after-shape {:optional true} :any] + [:after-shape {:optional true} ::sm/any] [:component-swap {:optional true} :boolean]]] [:reorder-children @@ -250,14 +250,14 @@ [:component-id {:optional true} ::sm/uuid] [:ignore-touched {:optional true} :boolean] [:parent-id ::sm/uuid] - [:shapes :any]]] + [:shapes ::sm/any]]] [:add-page [:map {:title "AddPageChange"} [:type [:= :add-page]] [:id {:optional true} ::sm/uuid] [:name {:optional true} :string] - [:page {:optional true} :any]]] + [:page {:optional true} ::sm/any]]] [:mod-page [:map {:title "ModPageChange"} @@ -327,14 +327,14 @@ [:type [:= :add-component]] [:id ::sm/uuid] [:name :string] - [:shapes {:optional true} [:vector {:gen/max 3} :any]] + [:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]] [:path {:optional true} :string]]] [:mod-component [:map {:title "ModCompoenentChange"} [:type [:= :mod-component]] [:id ::sm/uuid] - [:shapes {:optional true} [:vector {:gen/max 3} :any]] + [:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]] [:name {:optional true} :string] [:variant-id {:optional true} ::sm/uuid] [:variant-properties {:optional true} [:vector ::ctv/variant-property]]]] @@ -411,7 +411,7 @@ [:set-tokens-lib [:map {:title "SetTokensLib"} [:type [:= :set-tokens-lib]] - [:tokens-lib :any]]] + [:tokens-lib ::sm/any]]] [:set-token-set [:map {:title "SetTokenSetChange"} diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc index 692bde840c..8429503f63 100644 --- a/common/src/app/common/files/changes_builder.cljc +++ b/common/src/app/common/files/changes_builder.cljc @@ -25,18 +25,19 @@ ;; Auxiliary functions to help create a set of changes (undo + redo) -(sm/register! - ^{::sm/type ::changes} - [:map {:title "changes"} - [:redo-changes vector?] - [:undo-changes seq?] - [:origin {:optional true} any?] - [:save-undo? {:optional true} boolean?] - [:stack-undo? {:optional true} boolean?] - [:undo-group {:optional true} any?]]) +(def schema:changes + (sm/register! + ^{::sm/type ::changes} + [:map {:title "changes"} + [:redo-changes vector?] + [:undo-changes seq?] + [:origin {:optional true} ::sm/any] + [:save-undo? {:optional true} boolean?] + [:stack-undo? {:optional true} boolean?] + [:undo-group {:optional true} ::sm/any]])) (def check-changes! - (sm/check-fn ::changes)) + (sm/check-fn schema:changes)) (defn empty-changes ([origin page-id] diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index bb0fb744f7..5ac14ac501 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -626,6 +626,9 @@ (map? (:fill-image form)) (update-in [:fill-image :id] lookup-index) + (map? (:stroke-image form)) + (update-in [:stroke-image :id] lookup-index) + ;; This covers old shapes and the new :fills. (uuid? (:fill-color-ref-file form)) (update :fill-color-ref-file lookup-index) diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 631db173a1..cf3267ba64 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -584,7 +584,7 @@ (generate-sync-shape-direct changes file libraries container shape-id false))) (defmethod generate-sync-shape :colors - [_ changes library-id _ shape _ libraries _] + [_ changes library-id _ shape libraries _] (shape-log :debug (:id shape) nil :msg "Sync colors of shape" :shape (:name shape)) ;; Synchronize a shape that uses some colors of the library. The value of the @@ -595,7 +595,7 @@ #(ctc/sync-shape-colors % library-id library-colors)))) (defmethod generate-sync-shape :typographies - [_ changes library-id container shape _ libraries _] + [_ changes library-id container shape libraries _] (shape-log :debug (:id shape) nil :msg "Sync typographies of shape" :shape (:name shape)) ;; Synchronize a shape that uses some typographies of the library. The attributes diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index 8f68ea051a..27a9ca2a21 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -214,8 +214,7 @@ (defn lazy-validator [s] - (let [s (schema s) - vfn (delay (validator s))] + (let [vfn (delay (validator s))] (fn [v] (@vfn v)))) (defn lazy-explainer @@ -318,11 +317,14 @@ ([params] (cond (map? params) - (let [type (get params :type)] + (let [mdata (meta params) + type (or (get mdata ::id) + (get mdata ::type) + (get params :type))] (assert (qualified-keyword? type) "expected qualified keyword for `type`") (let [s (m/-simple-schema params)] (swap! sr/registry assoc type s) - nil)) + s)) (vector? params) (let [mdata (meta params) @@ -330,11 +332,12 @@ (get mdata ::type))] (assert (qualified-keyword? type) "expected qualified keyword to be on metadata") (swap! sr/registry assoc type params) - nil) + params) (m/into-schema? params) (let [type (m/-type params)] - (swap! sr/registry assoc type params)) + (swap! sr/registry assoc type params) + params) :else (throw (ex-info "Invalid Arguments" {})))) @@ -1042,6 +1045,8 @@ {:title "agent" :description "instance of clojure agent"}})) +(register! ::any (mu/update-properties :any assoc :gen/gen sg/any)) + ;; ---- PREDICATES (def valid-safe-number? diff --git a/common/src/app/common/schema/desc_js_like.cljc b/common/src/app/common/schema/desc_js_like.cljc index e60c329e89..f61279378c 100644 --- a/common/src/app/common/schema/desc_js_like.cljc +++ b/common/src/app/common/schema/desc_js_like.cljc @@ -7,6 +7,7 @@ (ns app.common.schema.desc-js-like (:require [app.common.data :as d] + [app.common.schema :as-alias sm] [cuerdas.core :as str] [malli.core :as m] [malli.util :as mu])) @@ -90,7 +91,7 @@ (defmethod visit :int [_ schema _ _] (str "integer" (-titled schema) (-min-max-suffix-number schema))) (defmethod visit :double [_ schema _ _] (str "double" (-titled schema) (-min-max-suffix-number schema))) (defmethod visit :select-keys [_ schema _ options] (describe* (m/deref schema) options)) -(defmethod visit :and [_ s children _] (str (str/join ", and " children) (-titled s))) +(defmethod visit :and [_ s children _] (str (str/join " && " children) (-titled s))) (defmethod visit :enum [_ s children _options] (str "enum" (-titled s) " of " (str/join ", " children))) (defmethod visit :maybe [_ _ children _] (str (first children) " nullable")) (defmethod visit :tuple [_ _ children _] (str "(" (str/join ", " children) ")")) @@ -106,7 +107,8 @@ (defmethod visit :qualified-symbol [_ _ _ _] "qualified symbol") (defmethod visit :uuid [_ _ _ _] "uuid") (defmethod visit :boolean [_ _ _ _] "boolean") -(defmethod visit :keyword [_ _ _ _] "keyword") +(defmethod visit :keyword [_ _ _ _] "string") +(defmethod visit :fn [_ _ _ _] "FN") (defmethod visit :vector [_ _ children _] (str "[" (last children) "]")) @@ -123,10 +125,12 @@ (defmethod visit :repeat [_ schema children _] (str "repeat " (-diamond (first children)) (-repeat-suffix schema))) - (defmethod visit :set [_ schema children _] (str "set[" (first children) "]" (minmax-suffix schema))) +(defmethod visit ::sm/set [_ schema children _] + (str "set[" (first children) "]" (minmax-suffix schema))) + (defmethod visit ::m/val [_ schema children _] (let [suffix (minmax-suffix schema)] (cond-> (first children) @@ -152,7 +156,6 @@ (or (:title props) "*"))) - (defmethod visit :map [_ schema children {:keys [::level ::max-level] :as options}] (let [props (m/properties schema) @@ -172,13 +175,11 @@ ": " s))) (str/join ",\n")) - header (cond-> (if (zero? level) - (str "type " title) - (str title)) + header (cond-> (str "type " title) closed? (str "!") (some? title) (str " "))] - (str header "{\n" entries "\n" (pad "}" level)))))) + (str (pad header level) "{\n" entries "\n" (pad "}\n" level)))))) (defmethod visit :multi [_ s children {:keys [::level ::max-level] :as options}] @@ -205,18 +206,18 @@ (defmethod visit :merge [_ schema children _] - (let [entries (str/join " , " children) + (let [entries (str/join ",\n" children) props (m/properties schema) title (or (some-> (:title props) str/camel str/capital) "")] - (str "merge object " title " { " entries " }"))) + (str "merge type " title " { \n" entries "\n}\n"))) -(defmethod visit :app.common.schema/one-of - [_ _ children _] +(defmethod visit ::sm/one-of + [_ _ children _] (let [elems (last children)] - (str "OneOf[" (->> elems - (map d/name) - (str/join ",")) "]"))) + (str "string oneOf (" (->> elems + (map d/name) + (str/join "|")) ")"))) (defmethod visit :schema [_ schema children options] (visit ::m/schema schema children options)) diff --git a/common/src/app/common/schema/generators.cljc b/common/src/app/common/schema/generators.cljc index 57bc3703f6..2f90ab2260 100644 --- a/common/src/app/common/schema/generators.cljc +++ b/common/src/app/common/schema/generators.cljc @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.common.schema.generators - (:refer-clojure :exclude [set subseq uuid filter map let boolean]) + (:refer-clojure :exclude [set subseq uuid filter map let boolean vector keyword int double]) #?(:cljs (:require-macros [app.common.schema.generators])) (:require [app.common.schema.registry :as sr] @@ -38,10 +38,6 @@ ([s opts] (mg/generator s (assoc opts :registry sr/default-registry)))) -(defn filter - [pred gen] - (tg/such-that pred gen 100)) - (defn small-double [& {:keys [min max] :or {min -100 max 100}}] (tg/double* {:min min, :max max, :infinite? false, :NaN? false})) @@ -61,7 +57,7 @@ (defn word-keyword [] (->> (word-string) - (tg/fmap keyword))) + (tg/fmap c/keyword))) (defn email [] @@ -100,12 +96,11 @@ (c/map second)) (c/map list bools elements))))))) -(def any tg/any) -(def boolean tg/boolean) - -(defn set - [g] - (tg/set g)) +(defn map-of + ([kg vg] + (tg/map kg vg {:min-elements 1 :max-elements 3})) + ([kg vg opts] + (tg/map kg vg opts))) (defn elements [s] @@ -119,6 +114,10 @@ [f g] (tg/fmap f g)) +(defn filter + [pred gen] + (tg/such-that pred gen 100)) + (defn mcat [f g] (tg/bind g f)) @@ -126,3 +125,22 @@ (defn tuple [& opts] (apply tg/tuple opts)) + +(defn vector + [& opts] + (apply tg/vector opts)) + +(defn set + [g] + (tg/set g)) + +;; Static Generators + +(def boolean tg/boolean) +(def text (word-string)) +(def double (small-double)) +(def int (small-int)) +(def keyword (word-keyword)) + +(def any + (tg/one-of [text boolean double int keyword])) diff --git a/common/src/app/common/schema/openapi.cljc b/common/src/app/common/schema/openapi.cljc index f571408269..967e267043 100644 --- a/common/src/app/common/schema/openapi.cljc +++ b/common/src/app/common/schema/openapi.cljc @@ -97,7 +97,8 @@ (defmethod visit :enum [_ _ children options] (merge (some-> (m/-infer children) (transform* options)) {:enum children})) (defmethod visit :maybe [_ _ children _] {:oneOf (conj children {:type "null"})}) (defmethod visit :tuple [_ _ children _] {:type "array", :items children, :additionalItems false}) -(defmethod visit :re [_ schema _ options] {:type "string", :pattern (first (m/children schema options))}) +(defmethod visit :re [_ schema _ options] + {:type "string", :pattern (str (first (m/children schema options)))}) (defmethod visit :nil [_ _ _ _] {:type "null"}) (defmethod visit :string [_ schema _ _] diff --git a/common/src/app/common/types/color.cljc b/common/src/app/common/types/color.cljc index f93f756174..29ea0b08c1 100644 --- a/common/src/app/common/types/color.cljc +++ b/common/src/app/common/types/color.cljc @@ -35,7 +35,7 @@ (.. r (toString 16) (padStart 2 "0")) (.. g (toString 16) (padStart 2 "0")) (.. b (toString 16) (padStart 2 "0")))))) - sg/any)) + sg/int)) (defn rgb-color-string? [o] diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index bfb6d09e2e..bab2105f59 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -41,7 +41,7 @@ [:map-of {:gen/max 10} ::sm/uuid :map]] [:plugin-data {:optional true} ::ctpg/plugin-data]]) -(def check-container! +(def check-container (sm/check-fn ::container)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -74,13 +74,9 @@ (defn get-shape [container shape-id] - (dm/assert! - "expected valid container" - (check-container! container)) - - (dm/assert! - "expected valid uuid for `shape-id`" - (uuid? shape-id)) + (assert (check-container container)) + (assert (uuid? shape-id) + "expected valid uuid for `shape-id`") (-> container (get :objects) diff --git a/common/src/app/common/types/shape/text.cljc b/common/src/app/common/types/shape/text.cljc index 1042e6f692..9b0e6908c9 100644 --- a/common/src/app/common/types/shape/text.cljc +++ b/common/src/app/common/types/shape/text.cljc @@ -16,54 +16,56 @@ (def node-types #{"root" "paragraph-set" "paragraph"}) -(sm/register! - ^{::sm/type ::content} - [:map - [:type [:= "root"]] - [:key {:optional true} :string] - [:children - {:optional true} - [:maybe - [:vector {:min 1 :gen/max 2 :gen/min 1} - [:map - [:type [:= "paragraph-set"]] - [:key {:optional true} :string] - [:children - [:vector {:min 1 :gen/max 2 :gen/min 1} - [:map - [:type [:= "paragraph"]] - [:key {:optional true} :string] - [:fills {:optional true} - [:maybe - [:vector {:gen/max 2} ::shape/fill]]] - [:font-family {:optional true} :string] - [:font-size {:optional true} :string] - [:font-style {:optional true} :string] - [:font-weight {:optional true} :string] - [:direction {:optional true} :string] - [:text-decoration {:optional true} :string] - [:text-transform {:optional true} :string] - [:typography-ref-id {:optional true} [:maybe ::sm/uuid]] - [:typography-ref-file {:optional true} [:maybe ::sm/uuid]] - [:children - [:vector {:min 1 :gen/max 2 :gen/min 1} - [:map - [:text :string] - [:key {:optional true} :string] - [:fills {:optional true} - [:maybe - [:vector {:gen/max 2} ::shape/fill]]] - [:font-family {:optional true} :string] - [:font-size {:optional true} :string] - [:font-style {:optional true} :string] - [:font-weight {:optional true} :string] - [:direction {:optional true} :string] - [:text-decoration {:optional true} :string] - [:text-transform {:optional true} :string] - [:typography-ref-id {:optional true} [:maybe ::sm/uuid]] - [:typography-ref-file {:optional true} [:maybe ::sm/uuid]]]]]]]]]]]]]) +(def schema:content + [:map + [:type [:= "root"]] + [:key {:optional true} :string] + [:children + {:optional true} + [:maybe + [:vector {:min 1 :gen/max 2 :gen/min 1} + [:map + [:type [:= "paragraph-set"]] + [:key {:optional true} :string] + [:children + [:vector {:min 1 :gen/max 2 :gen/min 1} + [:map + [:type [:= "paragraph"]] + [:key {:optional true} :string] + [:fills {:optional true} + [:maybe + [:vector {:gen/max 2} ::shape/fill]]] + [:font-family {:optional true} :string] + [:font-size {:optional true} :string] + [:font-style {:optional true} :string] + [:font-weight {:optional true} :string] + [:direction {:optional true} :string] + [:text-decoration {:optional true} :string] + [:text-transform {:optional true} :string] + [:typography-ref-id {:optional true} [:maybe ::sm/uuid]] + [:typography-ref-file {:optional true} [:maybe ::sm/uuid]] + [:children + [:vector {:min 1 :gen/max 2 :gen/min 1} + [:map + [:text :string] + [:key {:optional true} :string] + [:fills {:optional true} + [:maybe + [:vector {:gen/max 2} ::shape/fill]]] + [:font-family {:optional true} :string] + [:font-size {:optional true} :string] + [:font-style {:optional true} :string] + [:font-weight {:optional true} :string] + [:direction {:optional true} :string] + [:text-decoration {:optional true} :string] + [:text-transform {:optional true} :string] + [:typography-ref-id {:optional true} [:maybe ::sm/uuid]] + [:typography-ref-file {:optional true} [:maybe ::sm/uuid]]]]]]]]]]]]]) +(sm/register! ::content schema:content) +(def valid-content? + (sm/lazy-validator schema:content)) (sm/register! ^{::sm/type ::position-data} diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index ffed9c3949..f96d1b0296 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -7,6 +7,7 @@ (ns app.common.types.tokens-lib (:require #?(:clj [app.common.fressian :as fres]) + #?(:clj [clojure.data.json :as json]) [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] @@ -118,7 +119,7 @@ [:map {:title "Token"} [:name cto/token-name-ref] [:type [::sm/one-of cto/token-types]] - [:value :any] + [:value ::sm/any] [:description {:optional true} :string] [:modified-at {:optional true} ::sm/inst]]) @@ -389,7 +390,8 @@ [:description {:optional true} :string] [:modified-at {:optional true} ::sm/inst] [:tokens {:optional true - :gen/gen (->> (sg/generator [:map-of ::sm/text schema:token]) + :gen/gen (->> (sg/map-of (sg/generator ::sm/text) + (sg/generator schema:token)) (sg/fmap #(into (d/ordered-map) %)))} [:and [:map-of {:gen/max 5 @@ -910,6 +912,12 @@ Will return a value that matches this schema: "themes" (clj->js themes) "active-themes" (clj->js active-themes)))]) + + #?@(:clj + [json/JSONWriter + (-write [this writter options] (json/-write (encode-dtcg this) writter options))]) + + ITokenSets (add-set [_ token-set] (let [path (get-token-set-prefixed-path token-set) diff --git a/frontend/resources/images/icons/at.svg b/frontend/resources/images/icons/at.svg index 72e5ff01db..954c6be2ad 100644 --- a/frontend/resources/images/icons/at.svg +++ b/frontend/resources/images/icons/at.svg @@ -1 +1,3 @@ - + + + \ No newline at end of file diff --git a/frontend/resources/plugins-runtime/index.js b/frontend/resources/plugins-runtime/index.js index c51bb9dbfc..07b7a342ca 100644 --- a/frontend/resources/plugins-runtime/index.js +++ b/frontend/resources/plugins-runtime/index.js @@ -5086,7 +5086,8 @@ class zc extends HTMLElement { "allow-modals", "allow-popups", "allow-popups-to-escape-sandbox", - "allow-storage-access-by-user-activation" + "allow-storage-access-by-user-activation", + "allow-same-origin" ), o && l.sandbox.add("allow-downloads"), l.addEventListener("load", () => { var d; (d = this.shadowRoot) == null || d.dispatchEvent( diff --git a/frontend/scripts/build b/frontend/scripts/build index b298015456..46b5985faa 100755 --- a/frontend/scripts/build +++ b/frontend/scripts/build @@ -5,6 +5,7 @@ set -ex export INCLUDE_STORYBOOK=${BUILD_STORYBOOK:-no}; +export INCLUDE_WASM=${BUILD_WASM:-yes}; export CURRENT_VERSION=$1; export BUILD_DATE=$(date -R); @@ -17,14 +18,18 @@ export TS=$(date +%s); export NODE_ENV=production; corepack enable; -corepack up || exit 1; +corepack install || exit 1; yarn install || exit 1; rm -rf resources/public; rm -rf target/dist; yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS || exit 1 -yarn run build:wasm || exit 1; + +if [ "$INCLUDE_WASM" = "yes" ]; then + yarn run build:wasm || exit 1; +fi + yarn run build:app:libs || exit 1; yarn run build:app:assets || exit 1; @@ -36,7 +41,10 @@ sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/render.html; sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/rasterizer.html; sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/index.html; sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/rasterizer.html; -sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js; + +if [ "$INCLUDE_WASM" = "yes" ]; then + sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js; +fi if [ "$INCLUDE_STORYBOOK" = "yes" ]; then # build storybook diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 8817dafd93..f3bd2b97fb 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -34,6 +34,7 @@ [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctst] [app.common.types.shape.layout :as ctl] + [app.common.types.shape.text :as types.text] [app.common.types.typography :as ctt] [app.common.uuid :as uuid] [app.config :as cf] @@ -2192,27 +2193,27 @@ (ptk/reify ::paste-html-text ptk/WatchEvent (watch [_ state _] - (let [root (dwtxt/create-root-from-html html) - content (tc/dom->cljs root) + (let [root (dwtxt/create-root-from-html html) + content (tc/dom->cljs root)] + (when (types.text/valid-content? content) + (let [id (uuid/next) + width (max 8 (min (* 7 (count text)) 700)) + height 16 + {:keys [x y]} (calculate-paste-position state) - id (uuid/next) - width (max 8 (min (* 7 (count text)) 700)) - height 16 - {:keys [x y]} (calculate-paste-position state) - - shape {:id id - :type :text - :name (txt/generate-shape-name text) - :x x - :y y - :width width - :height height - :grow-type (if (> (count text) 100) :auto-height :auto-width) - :content content} - undo-id (js/Symbol)] - (rx/of (dwu/start-undo-transaction undo-id) - (dwsh/create-and-add-shape :text x y shape) - (dwu/commit-undo-transaction undo-id)))))) + shape {:id id + :type :text + :name (txt/generate-shape-name text) + :x x + :y y + :width width + :height height + :grow-type (if (> (count text) 100) :auto-height :auto-width) + :content content} + undo-id (js/Symbol)] + (rx/of (dwu/start-undo-transaction undo-id) + (dwsh/create-and-add-shape :text x y shape) + (dwu/commit-undo-transaction undo-id)))))))) (defn- paste-text [text] diff --git a/frontend/src/app/main/data/workspace/edition.cljs b/frontend/src/app/main/data/workspace/edition.cljs index e4fdf6cce8..07f61eedd1 100644 --- a/frontend/src/app/main/data/workspace/edition.cljs +++ b/frontend/src/app/main/data/workspace/edition.cljs @@ -48,7 +48,7 @@ ptk/UpdateEvent (update [_ state] (-> state - (update :workspace-local dissoc :edition) + (update :workspace-local dissoc :edition :edit-path) (update :workspace-drawing dissoc :tool :object :lock) (dissoc :workspace-grid-edition))) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index d92bfd4a6d..3a67bc278a 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -22,6 +22,7 @@ [app.main.data.helpers :as dsh] [app.main.data.modal :as md] [app.main.data.workspace.collapse :as dwc] + [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.specialized-panel :as-alias dwsp] [app.main.data.workspace.undo :as dwu] [app.main.data.workspace.zoom :as dwz] @@ -305,8 +306,9 @@ (watch [_ state _] (let [params-without-board (-> (rt/get-params state) (dissoc :board-id))] - (rx/of ::dwsp/interrupt) - (rx/of (rt/nav :workspace params-without-board {::rt/replace true})))) + (rx/of ::dwsp/interrupt + (dwe/clear-edition-mode) + (rt/nav :workspace params-without-board {::rt/replace true})))) ptk/UpdateEvent (update [_ state] diff --git a/frontend/src/app/main/ui/workspace/palette.cljs b/frontend/src/app/main/ui/workspace/palette.cljs index ff13a57b13..f4c3afec5e 100644 --- a/frontend/src/app/main/ui/workspace/palette.cljs +++ b/frontend/src/app/main/ui/workspace/palette.cljs @@ -147,6 +147,7 @@ (swap! state* assoc :width width))) [:div {:class (stl/css :palette-wrapper) + :id "palette-wrapper" :style (calculate-palette-padding rulers?) :data-testid "palette"} (when-not workspace-read-only? diff --git a/frontend/src/app/main/ui/workspace/palette.scss b/frontend/src/app/main/ui/workspace/palette.scss index 9474a79090..c4896a1c19 100644 --- a/frontend/src/app/main/ui/workspace/palette.scss +++ b/frontend/src/app/main/ui/workspace/palette.scss @@ -4,6 +4,10 @@ // // Copyright (c) KALEIDOS INC +@use "../ds/spacing.scss" as *; +@use "../ds/z-index.scss" as *; +@use "../ds/_sizes.scss" as *; + @import "refactor/common-refactor.scss"; .palette-wrapper { @@ -12,6 +16,12 @@ left: 0; bottom: 0; padding-bottom: $s-4; + + /** Aligns AI Chat button **/ + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-s); } .palettes { @@ -164,3 +174,27 @@ padding-bottom: $s-8; } } + +/** AI Chat button styles **/ +.help-btn { + z-index: var(--z-index-panels); + flex-shrink: 0; + @extend .button-secondary; + inline-size: $sz-40; + block-size: $sz-40; + border-radius: $br-circle; + border: none; + &.selected { + @extend .button-icon-selected; + } + &:hover { + border: none; + } +} + +.icon-help { + @extend .button-icon; + stroke: var(--icon-foreground); + inline-size: var(--sp-xxl); + block-size: var(--sp-xxl); +} diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 96a9883c3b..e004d9e384 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -129,6 +129,12 @@ input-ref (mf/use-ref nil) team (mf/deref refs/team) + permissions (get team :permissions) + + display-share-button? + (and (not (:is-default team)) + (or (:is-admin permissions) + (:is-owner permissions))) nav-to-viewer (mf/use-fn @@ -216,7 +222,7 @@ :on-click toggle-history} i/history]]) - (when (not (:is-default team)) + (when display-share-button? [:a {:class (stl/css :viewer-btn) :title (tr "workspace.header.share") :on-click open-share-dialog} diff --git a/frontend/src/app/util/code_gen/markup_html.cljs b/frontend/src/app/util/code_gen/markup_html.cljs index cb21d00ed7..b34b4d026b 100644 --- a/frontend/src/app/util/code_gen/markup_html.cljs +++ b/frontend/src/app/util/code_gen/markup_html.cljs @@ -38,7 +38,8 @@ indent)) (cfh/text-shape? shape) - (let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true}))] + (let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true})) + text-shape-html (str/replace text-shape-html #"style\s*=\s*[\"'][^\"']*[\"']" "")] (dm/fmt "%
\n%\n%
" indent (dm/str "shape " (d/name (:type shape)) " " diff --git a/frontend/text-editor/src/editor/content/dom/Content.js b/frontend/text-editor/src/editor/content/dom/Content.js index 9f3ed49ffc..9c6c8efccc 100644 --- a/frontend/text-editor/src/editor/content/dom/Content.js +++ b/frontend/text-editor/src/editor/content/dom/Content.js @@ -83,7 +83,10 @@ export function mapContentFragmentFromDocument(document, root, styleDefaults) { currentNode = nodeIterator.nextNode(); } - fragment.appendChild(currentParagraph); + if (currentParagraph) { + fragment.appendChild(currentParagraph); + } + if (fragment.children.length === 1) { const isContentInline = isContentFragmentFromDocumentInline(document); if (isContentInline) { diff --git a/manage.sh b/manage.sh index 5af31dd055..afd3fd201f 100755 --- a/manage.sh +++ b/manage.sh @@ -115,6 +115,7 @@ function build { --mount source=`pwd`,type=bind,target=/home/penpot/penpot \ -e EXTERNAL_UID=$CURRENT_USER_ID \ -e BUILD_STORYBOOK=$BUILD_STORYBOOK \ + -e BUILD_WASM=$BUILD_WASM \ -e SHADOWCLJS_EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS \ -e JAVA_OPTS="$JAVA_OPTS" \ -w /home/penpot/penpot/$1 \