mirror of
https://github.com/penpot/penpot.git
synced 2025-05-21 18:06:11 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
80bf7cc1e5
30 changed files with 356 additions and 219 deletions
|
@ -21,8 +21,8 @@
|
||||||
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
|
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
|
||||||
|
|
||||||
funcool/yetti
|
funcool/yetti
|
||||||
{:git/tag "v9.15"
|
{:git/tag "v9.16"
|
||||||
:git/sha "aa9b967"
|
:git/sha "7df3e08"
|
||||||
:git/url "https://github.com/funcool/yetti.git"
|
:git/url "https://github.com/funcool/yetti.git"
|
||||||
:exclusions [org.slf4j/slf4j-api]}
|
:exclusions [org.slf4j/slf4j-api]}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ penpot - error report v2 {{id}}
|
||||||
|
|
||||||
{% if value %}
|
{% if value %}
|
||||||
<div class="table-row multiline">
|
<div class="table-row multiline">
|
||||||
<div id="value" class="table-key">VALIDATION VALUE: </div>
|
<div id="value" class="table-key">VALUE: </div>
|
||||||
<div class="table-val">
|
<div class="table-val">
|
||||||
<pre>{{value}}</pre>
|
<pre>{{value}}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -264,7 +264,7 @@
|
||||||
content->>'~:hint' AS hint
|
content->>'~:hint' AS hint
|
||||||
FROM server_error_report
|
FROM server_error_report
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT 100")
|
LIMIT 200")
|
||||||
|
|
||||||
(defn error-list-handler
|
(defn error-list-handler
|
||||||
[{:keys [::db/pool]} request]
|
[{:keys [::db/pool]} request]
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
|
[app.config :as cf]
|
||||||
[app.http :as-alias http]
|
[app.http :as-alias http]
|
||||||
[app.http.access-token :as-alias actoken]
|
[app.http.access-token :as-alias actoken]
|
||||||
[app.http.session :as-alias session]
|
[app.http.session :as-alias session]
|
||||||
|
@ -30,14 +31,14 @@
|
||||||
(let [claims (-> {}
|
(let [claims (-> {}
|
||||||
(into (::session/token-claims request))
|
(into (::session/token-claims request))
|
||||||
(into (::actoken/token-claims request)))]
|
(into (::actoken/token-claims request)))]
|
||||||
{:path (:path request)
|
{:request/path (:path request)
|
||||||
:method (:method request)
|
:request/method (:method request)
|
||||||
:params (:params request)
|
:request/params (:params request)
|
||||||
:ip-addr (parse-client-ip request)
|
:request/user-agent (yrq/get-header request "user-agent")
|
||||||
:user-agent (yrq/get-header request "user-agent")
|
:request/ip-addr (parse-client-ip request)
|
||||||
:profile-id (:uid claims)
|
:request/profile-id (:uid claims)
|
||||||
:version (or (yrq/get-header request "x-frontend-version")
|
:version/frontend (or (yrq/get-header request "x-frontend-version") "unknown")
|
||||||
"unknown")}))
|
:version/backend (:full cf/version)}))
|
||||||
|
|
||||||
(defmulti handle-exception
|
(defmulti handle-exception
|
||||||
(fn [err & _rest]
|
(fn [err & _rest]
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
(:import
|
(:import
|
||||||
com.fasterxml.jackson.core.JsonParseException
|
com.fasterxml.jackson.core.JsonParseException
|
||||||
com.fasterxml.jackson.core.io.JsonEOFException
|
com.fasterxml.jackson.core.io.JsonEOFException
|
||||||
|
com.fasterxml.jackson.databind.exc.MismatchedInputException
|
||||||
io.undertow.server.RequestTooBigException
|
io.undertow.server.RequestTooBigException
|
||||||
java.io.OutputStream
|
java.io.InputStream
|
||||||
java.io.InputStream))
|
java.io.OutputStream))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
@ -78,11 +79,13 @@
|
||||||
|
|
||||||
|
|
||||||
(or (instance? JsonEOFException cause)
|
(or (instance? JsonEOFException cause)
|
||||||
(instance? JsonParseException cause))
|
(instance? JsonParseException cause)
|
||||||
|
(instance? MismatchedInputException cause))
|
||||||
(raise (ex/error :type :validation
|
(raise (ex/error :type :validation
|
||||||
:code :malformed-json
|
:code :malformed-json
|
||||||
:hint (ex-message cause)
|
:hint (ex-message cause)
|
||||||
:cause cause))
|
:cause cause))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(raise cause)))]
|
(raise cause)))]
|
||||||
|
|
||||||
|
@ -118,7 +121,9 @@
|
||||||
(t/write! tw data)))
|
(t/write! tw data)))
|
||||||
(catch java.io.IOException _)
|
(catch java.io.IOException _)
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/error :hint "unexpected error on encoding response" :cause cause))
|
(binding [l/*context* {:value data}]
|
||||||
|
(l/error :hint "unexpected error on encoding response"
|
||||||
|
:cause cause)))
|
||||||
(finally
|
(finally
|
||||||
(.close ^OutputStream output-stream))))))
|
(.close ^OutputStream output-stream))))))
|
||||||
|
|
||||||
|
@ -131,8 +136,9 @@
|
||||||
|
|
||||||
(catch java.io.IOException _)
|
(catch java.io.IOException _)
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/error :hint "unexpected error on encoding response"
|
(binding [l/*context* {:value data}]
|
||||||
:cause cause))
|
(l/error :hint "unexpected error on encoding response"
|
||||||
|
:cause cause)))
|
||||||
(finally
|
(finally
|
||||||
(.close ^OutputStream output-stream))))))
|
(.close ^OutputStream output-stream))))))
|
||||||
|
|
||||||
|
|
|
@ -40,35 +40,33 @@
|
||||||
[{:keys [::l/context ::l/message ::l/props ::l/logger ::l/level ::l/cause] :as record}]
|
[{:keys [::l/context ::l/message ::l/props ::l/logger ::l/level ::l/cause] :as record}]
|
||||||
(us/assert! ::l/record record)
|
(us/assert! ::l/record record)
|
||||||
|
|
||||||
(let [data (ex-data cause)]
|
(let [data (ex-data cause)
|
||||||
|
ctx (-> context
|
||||||
|
(assoc :tenant (cf/get :tenant))
|
||||||
|
(assoc :host (cf/get :host))
|
||||||
|
(assoc :public-uri (cf/get :public-uri))
|
||||||
|
(assoc :logger/name logger)
|
||||||
|
(assoc :logger/level level)
|
||||||
|
(dissoc :request/params :value :params :data))]
|
||||||
(merge
|
(merge
|
||||||
{:context (-> context
|
{:context (-> (into (sorted-map) ctx)
|
||||||
(assoc :tenant (cf/get :tenant))
|
(pp/pprint-str :width 200 :length 50 :level 10))
|
||||||
(assoc :host (cf/get :host))
|
:props (pp/pprint-str props :width 200 :length 50)
|
||||||
(assoc :public-uri (cf/get :public-uri))
|
|
||||||
(assoc :version (:full cf/version))
|
|
||||||
(assoc :logger-name logger)
|
|
||||||
(assoc :logger-level level)
|
|
||||||
(dissoc :params)
|
|
||||||
(pp/pprint-str :width 200))
|
|
||||||
|
|
||||||
:props (pp/pprint-str props :width 200)
|
|
||||||
:hint (or (ex-message cause) @message)
|
:hint (or (ex-message cause) @message)
|
||||||
:trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false)}
|
:trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false)}
|
||||||
|
|
||||||
(when-let [params (:params context)]
|
(when-let [params (or (:request/params context) (:params context))]
|
||||||
{:params (pp/pprint-str params :width 200)})
|
{:params (pp/pprint-str params :width 200)})
|
||||||
|
|
||||||
|
(when-let [value (:value context)]
|
||||||
|
{:value (pp/pprint-str value :width 200 :length 50 :level 10)})
|
||||||
|
|
||||||
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
|
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
|
||||||
{:data (pp/pprint-str data :width 200)})
|
{:data (pp/pprint-str data :width 200)})
|
||||||
|
|
||||||
(when-let [value (-> data ::sm/explain :value)]
|
(when-let [explain (ex/explain data {:level 10 :length 50})]
|
||||||
{:value (pp/pprint-str value :width 200)})
|
|
||||||
|
|
||||||
(when-let [explain (ex/explain data)]
|
|
||||||
{:explain explain}))))
|
{:explain explain}))))
|
||||||
|
|
||||||
|
|
||||||
(defn error-record?
|
(defn error-record?
|
||||||
[{:keys [::l/level ::l/cause]}]
|
[{:keys [::l/level ::l/cause]}]
|
||||||
(and (= :error level)
|
(and (= :error level)
|
||||||
|
|
|
@ -30,7 +30,9 @@
|
||||||
"```\n"
|
"```\n"
|
||||||
"- host: `" (:host report) "`\n"
|
"- host: `" (:host report) "`\n"
|
||||||
"- tenant: `" (:tenant report) "`\n"
|
"- tenant: `" (:tenant report) "`\n"
|
||||||
"- version: `" (:version report) "`\n"
|
"- request-path: `" (:request-path report) "`\n"
|
||||||
|
"- frontend-version: `" (:frontend-version report) "`\n"
|
||||||
|
"- backend-version: `" (:backend-version report) "`\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Trace:\n"
|
"Trace:\n"
|
||||||
(:trace report)
|
(:trace report)
|
||||||
|
@ -50,13 +52,15 @@
|
||||||
(defn record->report
|
(defn record->report
|
||||||
[{:keys [::l/context ::l/id ::l/cause] :as record}]
|
[{:keys [::l/context ::l/id ::l/cause] :as record}]
|
||||||
(us/assert! ::l/record record)
|
(us/assert! ::l/record record)
|
||||||
{:id id
|
{:id id
|
||||||
:tenant (cf/get :tenant)
|
:tenant (cf/get :tenant)
|
||||||
:host (cf/get :host)
|
:host (cf/get :host)
|
||||||
:public-uri (cf/get :public-uri)
|
:public-uri (cf/get :public-uri)
|
||||||
:version (:full cf/version)
|
:backend-version (or (:version/backend context) (:full cf/version))
|
||||||
:profile-id (:profile-id context)
|
:frontend-version (:version/frontend context)
|
||||||
:trace (ex/format-throwable cause :detail? false :header? false)})
|
:profile-id (:request/profile-id context)
|
||||||
|
:request-path (:request/path context)
|
||||||
|
:trace (ex/format-throwable cause :detail? false :header? false)})
|
||||||
|
|
||||||
(defn handle-event
|
(defn handle-event
|
||||||
[cfg record]
|
[cfg record]
|
||||||
|
|
|
@ -327,6 +327,9 @@
|
||||||
{:name "0105-mod-file-change-table"
|
{:name "0105-mod-file-change-table"
|
||||||
:fn (mg/resource "app/migrations/sql/0105-mod-file-change-table.sql")}
|
:fn (mg/resource "app/migrations/sql/0105-mod-file-change-table.sql")}
|
||||||
|
|
||||||
|
{:name "0105-mod-server-error-report-table"
|
||||||
|
:fn (mg/resource "app/migrations/sql/0105-mod-server-error-report-table.sql")}
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
(defn apply-migrations!
|
(defn apply-migrations!
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
CREATE INDEX server_error_report__created_at__idx
|
||||||
|
ON server_error_report ( created_at );
|
|
@ -489,16 +489,8 @@
|
||||||
(l/error :hint "worker: unhandled exception" :cause cause))))))
|
(l/error :hint "worker: unhandled exception" :cause cause))))))
|
||||||
|
|
||||||
(defn- get-error-context
|
(defn- get-error-context
|
||||||
[error item]
|
[_ item]
|
||||||
(let [data (ex-data error)]
|
{:params item})
|
||||||
(merge
|
|
||||||
{:hint (ex-message error)
|
|
||||||
:spec-problems (some->> data ::s/problems (take 10) seq vec)
|
|
||||||
:spec-value (some->> data ::s/value)
|
|
||||||
:data (some-> data (dissoc ::s/problems ::s/value ::s/spec))
|
|
||||||
:params item}
|
|
||||||
(when-let [explain (ex/explain data)]
|
|
||||||
{:spec-explain explain}))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; CRON
|
;; CRON
|
||||||
|
@ -597,10 +589,10 @@
|
||||||
(catch InterruptedException _
|
(catch InterruptedException _
|
||||||
(l/debug :hint "cron: task interrupted" :task-id id))
|
(l/debug :hint "cron: task interrupted" :task-id id))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/error :hint "cron: unhandled exception on running task"
|
(binding [l/*context* (get-error-context cause task)]
|
||||||
::l/context (get-error-context cause task)
|
(l/error :hint "cron: unhandled exception on running task"
|
||||||
:task-id id
|
:task-id id
|
||||||
:cause cause))
|
:cause cause)))
|
||||||
(finally
|
(finally
|
||||||
(when-not (px/interrupted? :current)
|
(when-not (px/interrupted? :current)
|
||||||
(schedule-cron-task cfg task))))))
|
(schedule-cron-task cfg task))))))
|
||||||
|
|
|
@ -93,6 +93,13 @@
|
||||||
[:component-id {:optional true} ::sm/uuid]
|
[:component-id {:optional true} ::sm/uuid]
|
||||||
[:ignore-touched {:optional true} :boolean]]]
|
[:ignore-touched {:optional true} :boolean]]]
|
||||||
|
|
||||||
|
[:fix-obj
|
||||||
|
[:map {:title "FixObjChange"}
|
||||||
|
[:type [:= :fix-obj]]
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:page-id {:optional true} ::sm/uuid]
|
||||||
|
[:component-id {:optional true} ::sm/uuid]]]
|
||||||
|
|
||||||
[:mov-objects
|
[:mov-objects
|
||||||
[:map {:title "MovObjectsChange"}
|
[:map {:title "MovObjectsChange"}
|
||||||
[:type [:= :mov-objects]]
|
[:type [:= :mov-objects]]
|
||||||
|
@ -392,6 +399,12 @@
|
||||||
(d/update-in-when data [:pages-index page-id] ctst/delete-shape id ignore-touched)
|
(d/update-in-when data [:pages-index page-id] ctst/delete-shape id ignore-touched)
|
||||||
(d/update-in-when data [:components component-id] ctst/delete-shape id ignore-touched)))
|
(d/update-in-when data [:components component-id] ctst/delete-shape id ignore-touched)))
|
||||||
|
|
||||||
|
(defmethod process-change :fix-obj
|
||||||
|
[data {:keys [page-id component-id] :as params}]
|
||||||
|
(if page-id
|
||||||
|
(d/update-in-when data [:pages-index page-id] ctst/fix-shape-children params)
|
||||||
|
(d/update-in-when data [:components component-id] ctst/fix-shape-children params)))
|
||||||
|
|
||||||
;; FIXME: remove, seems like this method is already unused
|
;; FIXME: remove, seems like this method is already unused
|
||||||
;; reg-objects operation "regenerates" the geometry and selrect of the parent groups
|
;; reg-objects operation "regenerates" the geometry and selrect of the parent groups
|
||||||
(defmethod process-change :reg-objects
|
(defmethod process-change :reg-objects
|
||||||
|
|
|
@ -29,8 +29,10 @@
|
||||||
(def uuid-rx
|
(def uuid-rx
|
||||||
#"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
|
#"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")
|
||||||
|
|
||||||
(def max-safe-int (int 1e6))
|
;; Integer/MAX_VALUE
|
||||||
(def min-safe-int (int -1e6))
|
(def max-safe-int 2147483647)
|
||||||
|
;; Integer/MIN_VALUE
|
||||||
|
(def min-safe-int -2147483648)
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; DEFAULT SPECS
|
;; DEFAULT SPECS
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
[:map {:title "Fill"}
|
[:map {:title "Fill"}
|
||||||
[:fill-color {:optional true} ::ctc/rgb-color]
|
[:fill-color {:optional true} ::ctc/rgb-color]
|
||||||
[:fill-opacity {:optional true} ::sm/safe-number]
|
[:fill-opacity {:optional true} ::sm/safe-number]
|
||||||
[:fill-color-gradient {:optional true} ::ctc/gradient]
|
[:fill-color-gradient {:optional true} [:maybe ::ctc/gradient]]
|
||||||
[:fill-color-ref-file {:optional true} [:maybe ::sm/uuid]]
|
[:fill-color-ref-file {:optional true} [:maybe ::sm/uuid]]
|
||||||
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]])
|
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]])
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@
|
||||||
(sm/def! ::group-attrs
|
(sm/def! ::group-attrs
|
||||||
[:map {:title "GroupAttrs"}
|
[:map {:title "GroupAttrs"}
|
||||||
[:type [:= :group]]
|
[:type [:= :group]]
|
||||||
[:shapes [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]])
|
[:shapes {:optional true} [:maybe [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]]])
|
||||||
|
|
||||||
(sm/def! ::frame-attrs
|
(sm/def! ::frame-attrs
|
||||||
[:map {:title "FrameAttrs"}
|
[:map {:title "FrameAttrs"}
|
||||||
|
@ -214,18 +214,19 @@
|
||||||
(sm/def! ::bool-attrs
|
(sm/def! ::bool-attrs
|
||||||
[:map {:title "BoolAttrs"}
|
[:map {:title "BoolAttrs"}
|
||||||
[:type [:= :bool]]
|
[:type [:= :bool]]
|
||||||
[:shapes [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]
|
[:shapes {:optional true} [:maybe [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]]
|
||||||
|
|
||||||
;; FIXME: improve this schema
|
;; FIXME: improve this schema
|
||||||
[:bool-type :keyword]
|
[:bool-type :keyword]
|
||||||
|
|
||||||
;; FIXME: improve this schema
|
|
||||||
[:bool-content
|
[:bool-content
|
||||||
[:vector {:gen/max 2}
|
[:vector {:gen/max 2}
|
||||||
[:map
|
[:map
|
||||||
[:command :keyword]
|
[:command :keyword]
|
||||||
[:relative {:optional true} :boolean]
|
[:relative {:optional true} :boolean]
|
||||||
[:params [:map-of {:gen/max 5} :keyword ::sm/safe-number]]]]]])
|
[:prev-pos {:optional true} ::gpt/point]
|
||||||
|
[:params {:optional true}
|
||||||
|
[:map-of {:gen/max 5} :keyword ::sm/safe-number]]]]]])
|
||||||
|
|
||||||
(sm/def! ::rect-attrs
|
(sm/def! ::rect-attrs
|
||||||
[:map {:title "RectAttrs"}
|
[:map {:title "RectAttrs"}
|
||||||
|
@ -252,7 +253,12 @@
|
||||||
(sm/def! ::path-attrs
|
(sm/def! ::path-attrs
|
||||||
[:map {:title "PathAttrs"}
|
[:map {:title "PathAttrs"}
|
||||||
[:type [:= :path]]
|
[:type [:= :path]]
|
||||||
|
[:x {:optional true} [:maybe ::sm/safe-number]]
|
||||||
|
[:y {:optional true} [:maybe ::sm/safe-number]]
|
||||||
|
[:width {:optional true} [:maybe ::sm/safe-number]]
|
||||||
|
[:height {:optional true} [:maybe ::sm/safe-number]]
|
||||||
[:content
|
[:content
|
||||||
|
{:optional true}
|
||||||
[:vector
|
[:vector
|
||||||
[:map
|
[:map
|
||||||
[:command :keyword]
|
[:command :keyword]
|
||||||
|
|
|
@ -21,44 +21,46 @@
|
||||||
[:type [:= "root"]]
|
[:type [:= "root"]]
|
||||||
[:key {:optional true} :string]
|
[:key {:optional true} :string]
|
||||||
[:children
|
[:children
|
||||||
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
{:optional true}
|
||||||
[:map
|
[:maybe
|
||||||
[:type [:= "paragraph-set"]]
|
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
||||||
[:key {:optional true} :string]
|
[:map
|
||||||
[:children
|
[:type [:= "paragraph-set"]]
|
||||||
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
[:key {:optional true} :string]
|
||||||
[:map
|
[:children
|
||||||
[:type [:= "paragraph"]]
|
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
||||||
[:key {:optional true} :string]
|
[:map
|
||||||
[:fills {:optional true}
|
[:type [:= "paragraph"]]
|
||||||
[:maybe
|
[:key {:optional true} :string]
|
||||||
[:vector {:gen/max 2} ::shape/fill]]]
|
[:fills {:optional true}
|
||||||
[:font-family {:optional true} :string]
|
[:maybe
|
||||||
[:font-size {:optional true} :string]
|
[:vector {:gen/max 2} ::shape/fill]]]
|
||||||
[:font-style {:optional true} :string]
|
[:font-family {:optional true} :string]
|
||||||
[:font-weight {:optional true} :string]
|
[:font-size {:optional true} :string]
|
||||||
[:direction {:optional true} :string]
|
[:font-style {:optional true} :string]
|
||||||
[:text-decoration {:optional true} :string]
|
[:font-weight {:optional true} :string]
|
||||||
[:text-transform {:optional true} :string]
|
[:direction {:optional true} :string]
|
||||||
[:typography-ref-id {:optional true} [:maybe ::sm/uuid]]
|
[:text-decoration {:optional true} :string]
|
||||||
[:typography-ref-file {:optional true} [:maybe ::sm/uuid]]
|
[:text-transform {:optional true} :string]
|
||||||
[:children
|
[:typography-ref-id {:optional true} [:maybe ::sm/uuid]]
|
||||||
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
[:typography-ref-file {:optional true} [:maybe ::sm/uuid]]
|
||||||
[:map
|
[:children
|
||||||
[:text :string]
|
[:vector {:min 1 :gen/max 2 :gen/min 1}
|
||||||
[:key {:optional true} :string]
|
[:map
|
||||||
[:fills {:optional true}
|
[:text :string]
|
||||||
[:maybe
|
[:key {:optional true} :string]
|
||||||
[:vector {:gen/max 2} ::shape/fill]]]
|
[:fills {:optional true}
|
||||||
[:font-family {:optional true} :string]
|
[:maybe
|
||||||
[:font-size {:optional true} :string]
|
[:vector {:gen/max 2} ::shape/fill]]]
|
||||||
[:font-style {:optional true} :string]
|
[:font-family {:optional true} :string]
|
||||||
[:font-weight {:optional true} :string]
|
[:font-size {:optional true} :string]
|
||||||
[:direction {:optional true} :string]
|
[:font-style {:optional true} :string]
|
||||||
[:text-decoration {:optional true} :string]
|
[:font-weight {:optional true} :string]
|
||||||
[:text-transform {:optional true} :string]
|
[:direction {:optional true} :string]
|
||||||
[:typography-ref-id {:optional true} [:maybe ::sm/uuid]]
|
[:text-decoration {:optional true} :string]
|
||||||
[:typography-ref-file {:optional true} [:maybe ::sm/uuid]]]]]]]]]]]])
|
[:text-transform {:optional true} :string]
|
||||||
|
[:typography-ref-id {:optional true} [:maybe ::sm/uuid]]
|
||||||
|
[:typography-ref-file {:optional true} [:maybe ::sm/uuid]]]]]]]]]]]]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -90,16 +90,24 @@
|
||||||
|
|
||||||
(delete-from-objects [objects]
|
(delete-from-objects [objects]
|
||||||
(if-let [target (get objects shape-id)]
|
(if-let [target (get objects shape-id)]
|
||||||
(let [parent-id (or (:parent-id target)
|
(let [parent-id (or (:parent-id target)
|
||||||
(:frame-id target))
|
(:frame-id target))
|
||||||
children-ids (cph/get-children-ids objects shape-id)]
|
children-ids (cph/get-children-ids objects shape-id)]
|
||||||
(-> (reduce dissoc objects children-ids)
|
(-> (reduce dissoc objects (cons shape-id children-ids))
|
||||||
(dissoc shape-id)
|
|
||||||
(d/update-when parent-id delete-from-parent)))
|
(d/update-when parent-id delete-from-parent)))
|
||||||
objects))]
|
objects))]
|
||||||
|
|
||||||
(update container :objects delete-from-objects))))
|
(update container :objects delete-from-objects))))
|
||||||
|
|
||||||
|
(defn fix-shape-children
|
||||||
|
"Checks and fix the children relations of the shape. If a children does not
|
||||||
|
exists on the objects tree, it will be removed from shape."
|
||||||
|
[{:keys [objects] :as container} {:keys [id] :as params}]
|
||||||
|
(let [contains? (partial contains? objects)]
|
||||||
|
(d/update-in-when container [:objects id :shapes]
|
||||||
|
(fn [shapes]
|
||||||
|
(into [] (filter contains?) shapes)))))
|
||||||
|
|
||||||
(defn get-frames
|
(defn get-frames
|
||||||
"Retrieves all frame objects as vector"
|
"Retrieves all frame objects as vector"
|
||||||
([objects] (get-frames objects nil))
|
([objects] (get-frames objects nil))
|
||||||
|
@ -349,6 +357,7 @@
|
||||||
(some? force-id) force-id
|
(some? force-id) force-id
|
||||||
keep-ids? (:id object)
|
keep-ids? (:id object)
|
||||||
:else (uuid/next))]
|
:else (uuid/next))]
|
||||||
|
|
||||||
(loop [child-ids (seq (:shapes object))
|
(loop [child-ids (seq (:shapes object))
|
||||||
new-direct-children []
|
new-direct-children []
|
||||||
new-children []
|
new-children []
|
||||||
|
|
|
@ -104,7 +104,7 @@ WORKDIR /opt/penpot/exporter
|
||||||
USER penpot:penpot
|
USER penpot:penpot
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
yarn; \
|
yarn --network-timeout 1000000; \
|
||||||
yarn run playwright install chromium;
|
yarn --network-timeout 1000000 run playwright install chromium;
|
||||||
|
|
||||||
CMD ["node", "app.js"]
|
CMD ["node", "app.js"]
|
||||||
|
|
|
@ -50,6 +50,23 @@
|
||||||
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
||||||
(mf/mount (mf/element modal) (dom/get-element "modal")))
|
(mf/mount (mf/element modal) (dom/get-element "modal")))
|
||||||
|
|
||||||
|
(defn- initialize-profile
|
||||||
|
"Event used mainly on application bootstrap; it fetches the profile
|
||||||
|
and if and only if the fetched profile corresponds to an
|
||||||
|
authenticated user; proceed to fetch teams."
|
||||||
|
[stream]
|
||||||
|
(rx/merge
|
||||||
|
(rx/of (du/fetch-profile))
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::profile-fetched))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map deref)
|
||||||
|
(rx/mapcat (fn [profile]
|
||||||
|
(if (du/is-authenticated? profile)
|
||||||
|
(rx/of (du/fetch-teams))
|
||||||
|
(rx/empty))))
|
||||||
|
(rx/observe-on :async))))
|
||||||
|
|
||||||
(defn initialize
|
(defn initialize
|
||||||
[]
|
[]
|
||||||
(ptk/reify ::initialize
|
(ptk/reify ::initialize
|
||||||
|
@ -61,14 +78,19 @@
|
||||||
(watch [_ _ stream]
|
(watch [_ _ stream]
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(rx/of (ev/initialize)
|
(rx/of (ev/initialize)
|
||||||
(feat/initialize)
|
(feat/initialize))
|
||||||
(du/initialize-profile))
|
|
||||||
|
|
||||||
|
(initialize-profile stream)
|
||||||
|
|
||||||
|
;; Once profile is fetched, initialize all penpot application
|
||||||
|
;; routes
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter du/profile-fetched?)
|
(rx/filter du/profile-fetched?)
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map #(rt/init-routes)))
|
(rx/map #(rt/init-routes)))
|
||||||
|
|
||||||
|
;; Once profile fetched and the current user is authenticated,
|
||||||
|
;; proceed to initialize the websockets connection.
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter du/profile-fetched?)
|
(rx/filter du/profile-fetched?)
|
||||||
(rx/map deref)
|
(rx/map deref)
|
||||||
|
|
|
@ -117,28 +117,6 @@
|
||||||
(->> (rp/cmd! :get-profile)
|
(->> (rp/cmd! :get-profile)
|
||||||
(rx/map profile-fetched)))))
|
(rx/map profile-fetched)))))
|
||||||
|
|
||||||
;; --- EVENT: INITIALIZE PROFILE
|
|
||||||
|
|
||||||
(defn initialize-profile
|
|
||||||
"Event used mainly on application bootstrap; it fetches the profile
|
|
||||||
and if and only if the fetched profile corresponds to an
|
|
||||||
authenticated user; proceed to fetch teams."
|
|
||||||
[]
|
|
||||||
(ptk/reify ::initialize-profile
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ _ stream]
|
|
||||||
(rx/merge
|
|
||||||
(rx/of (fetch-profile))
|
|
||||||
(->> stream
|
|
||||||
(rx/filter (ptk/type? ::profile-fetched))
|
|
||||||
(rx/take 1)
|
|
||||||
(rx/map deref)
|
|
||||||
(rx/mapcat (fn [profile]
|
|
||||||
(if (= uuid/zero (:id profile))
|
|
||||||
(rx/empty)
|
|
||||||
(rx/of (fetch-teams)))))
|
|
||||||
(rx/observe-on :async))))))
|
|
||||||
|
|
||||||
;; --- EVENT: login
|
;; --- EVENT: login
|
||||||
|
|
||||||
(defn- logged-in
|
(defn- logged-in
|
||||||
|
@ -164,7 +142,8 @@
|
||||||
(when (is-authenticated? profile)
|
(when (is-authenticated? profile)
|
||||||
(->> (rx/of (profile-fetched profile)
|
(->> (rx/of (profile-fetched profile)
|
||||||
(fetch-teams)
|
(fetch-teams)
|
||||||
(get-redirect-event))
|
(get-redirect-event)
|
||||||
|
(ws/initialize))
|
||||||
(rx/observe-on :async)))))))
|
(rx/observe-on :async)))))))
|
||||||
|
|
||||||
(declare login-from-register)
|
(declare login-from-register)
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
[app.main.data.workspace.drawing.common :as dwdc]
|
[app.main.data.workspace.drawing.common :as dwdc]
|
||||||
[app.main.data.workspace.edition :as dwe]
|
[app.main.data.workspace.edition :as dwe]
|
||||||
[app.main.data.workspace.fix-bool-contents :as fbc]
|
[app.main.data.workspace.fix-bool-contents :as fbc]
|
||||||
|
[app.main.data.workspace.fix-broken-shape-links :as fbs]
|
||||||
[app.main.data.workspace.fix-deleted-fonts :as fdf]
|
[app.main.data.workspace.fix-deleted-fonts :as fdf]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.main.data.workspace.guides :as dwgu]
|
[app.main.data.workspace.guides :as dwgu]
|
||||||
|
@ -132,8 +133,10 @@
|
||||||
has-graphics? (-> file :media seq)
|
has-graphics? (-> file :media seq)
|
||||||
components-v2 (features/active-feature? state :components-v2)]
|
components-v2 (features/active-feature? state :components-v2)]
|
||||||
(rx/merge
|
(rx/merge
|
||||||
(rx/of (fbc/fix-bool-contents))
|
(rx/of (fbc/fix-bool-contents)
|
||||||
(rx/of (fdf/fix-deleted-fonts))
|
(fdf/fix-deleted-fonts)
|
||||||
|
(fbs/fix-broken-shapes))
|
||||||
|
|
||||||
(if (and has-graphics? components-v2)
|
(if (and has-graphics? components-v2)
|
||||||
(rx/of (remove-graphics (:id file) (:name file)))
|
(rx/of (remove-graphics (:id file) (:name file)))
|
||||||
(rx/empty)))))))
|
(rx/empty)))))))
|
||||||
|
@ -1832,9 +1835,14 @@
|
||||||
detach? (or (foreign-instance? shape paste-objects state)
|
detach? (or (foreign-instance? shape paste-objects state)
|
||||||
(and (ctk/in-component-copy-not-root? shape)
|
(and (ctk/in-component-copy-not-root? shape)
|
||||||
(not= (:id component-shape)
|
(not= (:id component-shape)
|
||||||
(:id component-shape-parent))))]
|
(:id component-shape-parent))))
|
||||||
|
assign-shapes? (and (or (cph/group-shape? shape)
|
||||||
|
(cph/bool-shape? shape))
|
||||||
|
(nil? (:shapes shape)))]
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc :frame-id frame-id :parent-id parent-id)
|
(assoc :frame-id frame-id :parent-id parent-id)
|
||||||
|
(cond-> assign-shapes?
|
||||||
|
(assoc :shapes []))
|
||||||
(cond-> detach?
|
(cond-> detach?
|
||||||
;; this is used later, if the paste needs to create a new component from the detached shape
|
;; this is used later, if the paste needs to create a new component from the detached shape
|
||||||
(-> (assoc :saved-component-root (:component-root shape))
|
(-> (assoc :saved-component-root (:component-root shape))
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.changes :as cpc]
|
[app.common.pages.changes :as cpc]
|
||||||
|
@ -206,6 +207,7 @@
|
||||||
path (if (= file-id current-file-id)
|
path (if (= file-id current-file-id)
|
||||||
[:workspace-data]
|
[:workspace-data]
|
||||||
[:workspace-libraries file-id :data])]
|
[:workspace-libraries file-id :data])]
|
||||||
|
|
||||||
(try
|
(try
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expect valid vector of changes"
|
"expect valid vector of changes"
|
||||||
|
@ -218,7 +220,11 @@
|
||||||
(ctst/update-object-indices page-id))))
|
(ctst/update-object-indices page-id))))
|
||||||
|
|
||||||
(catch :default err
|
(catch :default err
|
||||||
(log/error :js/error err)
|
(when-let [data (ex-data err)]
|
||||||
|
(js/console.log (ex/explain data)))
|
||||||
|
|
||||||
|
(when (ex/error? err)
|
||||||
|
(js/console.log (.-stack ^js err)))
|
||||||
(vreset! error err)
|
(vreset! error err)
|
||||||
state))))
|
state))))
|
||||||
|
|
||||||
|
|
|
@ -353,9 +353,12 @@
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-global :picking-color?] true)
|
(assoc-in [:workspace-global :picking-color?] true)
|
||||||
(assoc ::md/modal {:id (random-uuid)
|
(assoc ::md/modal {:id (random-uuid)
|
||||||
:data {:color colors/black :opacity 1}
|
|
||||||
:type :colorpicker
|
:type :colorpicker
|
||||||
:props {:on-change handle-change-color}
|
:props {:data {:color colors/black
|
||||||
|
:opacity 1}
|
||||||
|
:disable-opacity false
|
||||||
|
:disable-gradient false
|
||||||
|
:on-change handle-change-color}
|
||||||
:allow-click-outside true})))))))
|
:allow-click-outside true})))))))
|
||||||
|
|
||||||
(defn color-att->text
|
(defn color-att->text
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
;; 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.main.data.workspace.fix-broken-shape-links
|
||||||
|
(:require
|
||||||
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
(defn- generate-changes
|
||||||
|
[attr {:keys [objects id] :as container}]
|
||||||
|
(let [base {:type :fix-obj attr id}
|
||||||
|
contains? (partial contains? objects)
|
||||||
|
xform (comp
|
||||||
|
;; FIXME: Ensure all obj have id field (this is needed
|
||||||
|
;; because some bug adds an ephimeral shape with id ZERO,
|
||||||
|
;; with a single attr `:shapes` having a vector of ids
|
||||||
|
;; pointing to not existing shapes). That happens on
|
||||||
|
;; components. THIS IS A WORKAOURD
|
||||||
|
(map (fn [[id obj]]
|
||||||
|
(if (some? (:id obj))
|
||||||
|
obj
|
||||||
|
(assoc obj :id id))))
|
||||||
|
|
||||||
|
;; Remove all valid shapes
|
||||||
|
(remove (fn [obj]
|
||||||
|
(every? contains? (:shapes obj))))
|
||||||
|
|
||||||
|
(map (fn [obj]
|
||||||
|
(assoc base :id (:id obj)))))]
|
||||||
|
|
||||||
|
(sequence xform objects)))
|
||||||
|
|
||||||
|
(defn fix-broken-shapes
|
||||||
|
[]
|
||||||
|
(ptk/reify ::fix-broken-shape-links
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [data (get state :workspace-data)
|
||||||
|
changes (concat
|
||||||
|
(mapcat (partial generate-changes :page-id)
|
||||||
|
(vals (:pages-index data)))
|
||||||
|
(mapcat (partial generate-changes :component-id)
|
||||||
|
(vals (:components data))))]
|
||||||
|
|
||||||
|
(if (seq changes)
|
||||||
|
(rx/of (dch/commit-changes
|
||||||
|
{:origin it
|
||||||
|
:redo-changes (vec changes)
|
||||||
|
:undo-changes []
|
||||||
|
:save-undo? false}))
|
||||||
|
(rx/empty))))))
|
|
@ -419,6 +419,8 @@
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [frame? (cph/frame-shape? obj)
|
(let [frame? (cph/frame-shape? obj)
|
||||||
|
group? (cph/group-shape? obj)
|
||||||
|
bool? (cph/bool-shape? obj)
|
||||||
new-id (ids-map (:id obj))
|
new-id (ids-map (:id obj))
|
||||||
parent-id (or parent-id frame-id)
|
parent-id (or parent-id frame-id)
|
||||||
name (:name obj)
|
name (:name obj)
|
||||||
|
@ -437,9 +439,15 @@
|
||||||
:name name
|
:name name
|
||||||
:parent-id parent-id
|
:parent-id parent-id
|
||||||
:frame-id frame-id)
|
:frame-id frame-id)
|
||||||
|
|
||||||
(dissoc :shapes
|
(dissoc :shapes
|
||||||
:main-instance?
|
:main-instance?
|
||||||
:use-for-thumbnail?)
|
:use-for-thumbnail?)
|
||||||
|
|
||||||
|
(cond->
|
||||||
|
(or group? bool?)
|
||||||
|
(assoc :shapes []))
|
||||||
|
|
||||||
(gsh/move delta)
|
(gsh/move delta)
|
||||||
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects))
|
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects))
|
||||||
|
|
||||||
|
|
|
@ -252,7 +252,7 @@
|
||||||
(let [all-ids (into empty-parents ids)
|
(let [all-ids (into empty-parents ids)
|
||||||
contains? (partial contains? all-ids)
|
contains? (partial contains? all-ids)
|
||||||
xform (comp (map lookup)
|
xform (comp (map lookup)
|
||||||
(filter cph/group-shape?)
|
(filter #(or (cph/group-shape? %) (cph/bool-shape? %)))
|
||||||
(remove #(->> (:shapes %) (remove contains?) seq))
|
(remove #(->> (:shapes %) (remove contains?) seq))
|
||||||
(map :id))
|
(map :id))
|
||||||
parents (into #{} xform all-parents)]
|
parents (into #{} xform all-parents)]
|
||||||
|
|
|
@ -116,56 +116,64 @@
|
||||||
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fill-opacity])
|
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fill-opacity])
|
||||||
(d/parse-double 1)))))))
|
(d/parse-double 1)))))))
|
||||||
|
|
||||||
(defn setup-stroke [shape]
|
(defn- setup-stroke
|
||||||
(let [stroke-linecap (-> (or (dm/get-in shape [:svg-attrs :stroke-linecap])
|
[shape]
|
||||||
(dm/get-in shape [:svg-attrs :style :stroke-linecap]))
|
(let [attrs (get shape :svg-attrs)
|
||||||
((d/nilf str/trim))
|
style (get attrs :style)
|
||||||
((d/nilf keyword)))
|
|
||||||
color-attr (str/trim (dm/get-in shape [:svg-attrs :stroke]))
|
|
||||||
color-attr (if (= color-attr "currentColor") clr/black color-attr)
|
|
||||||
color-style (str/trim (dm/get-in shape [:svg-attrs :style :stroke]))
|
|
||||||
color-style (if (= color-style "currentColor") clr/black color-style)
|
|
||||||
|
|
||||||
shape
|
stroke (or (str/trim (:stroke attrs))
|
||||||
(cond-> shape
|
(str/trim (:stroke style)))
|
||||||
;; Color present as attribute
|
|
||||||
(uc/color? color-attr)
|
|
||||||
(-> (update :svg-attrs dissoc :stroke)
|
|
||||||
(assoc-in [:strokes 0 :stroke-color] (uc/parse-color color-attr)))
|
|
||||||
|
|
||||||
;; Color present as style
|
color (cond
|
||||||
(uc/color? color-style)
|
(= stroke "currentColor") clr/black
|
||||||
(-> (update-in [:svg-attrs :style] dissoc :stroke)
|
(= stroke "none") nil
|
||||||
(assoc-in [:strokes 0 :stroke-color] (uc/parse-color color-style)))
|
:else (uc/parse-color stroke))
|
||||||
|
|
||||||
(dm/get-in shape [:svg-attrs :stroke-opacity])
|
opacity (when (some? color)
|
||||||
(-> (update :svg-attrs dissoc :stroke-opacity)
|
(d/parse-double
|
||||||
(assoc-in [:strokes 0 :stroke-opacity] (-> (dm/get-in shape [:svg-attrs :stroke-opacity])
|
(or (:stroke-opacity attrs)
|
||||||
(d/parse-double 1))))
|
(:stroke-opacity style))
|
||||||
|
1))
|
||||||
|
|
||||||
(dm/get-in shape [:svg-attrs :style :stroke-opacity])
|
width (when (some? color)
|
||||||
(-> (update-in [:svg-attrs :style] dissoc :stroke-opacity)
|
(d/parse-double
|
||||||
(assoc-in [:strokes 0 :stroke-opacity] (-> (dm/get-in shape [:svg-attrs :style :stroke-opacity])
|
(or (:stroke-width attrs)
|
||||||
(d/parse-double 1))))
|
(:stroke-width style))
|
||||||
|
1))
|
||||||
|
|
||||||
(dm/get-in shape [:svg-attrs :stroke-width])
|
linecap (or (get attrs :stroke-linecap)
|
||||||
(-> (update :svg-attrs dissoc :stroke-width)
|
(get style :stroke-linecap))
|
||||||
(assoc-in [:strokes 0 :stroke-width] (-> (dm/get-in shape [:svg-attrs :stroke-width])
|
linecap (some-> linecap str/trim keyword)
|
||||||
(d/parse-double))))
|
|
||||||
|
|
||||||
(dm/get-in shape [:svg-attrs :style :stroke-width])
|
attrs (-> attrs
|
||||||
(-> (update-in [:svg-attrs :style] dissoc :stroke-width)
|
(dissoc :stroke)
|
||||||
(assoc-in [:strokes 0 :stroke-width] (-> (dm/get-in shape [:svg-attrs :style :stroke-width])
|
(dissoc :stroke-width)
|
||||||
(d/parse-double))))
|
(dissoc :stroke-opacity)
|
||||||
|
(update :style (fn [style]
|
||||||
|
(-> style
|
||||||
|
(dissoc :stroke)
|
||||||
|
(dissoc :stroke-linecap)
|
||||||
|
(dissoc :stroke-width)
|
||||||
|
(dissoc :stroke-opacity)))))]
|
||||||
|
|
||||||
(and stroke-linecap (= (:type shape) :path))
|
(cond-> (assoc shape :svg-attrs attrs)
|
||||||
(-> (update-in [:svg-attrs :style] dissoc :stroke-linecap)
|
(some? color)
|
||||||
(cond-> (#{:round :square} stroke-linecap)
|
(assoc-in [:strokes 0 :stroke-color] color)
|
||||||
(assoc :stroke-cap-start stroke-linecap
|
|
||||||
:stroke-cap-end stroke-linecap))))]
|
|
||||||
|
|
||||||
(cond-> shape
|
(and (some? color) (some? opacity))
|
||||||
(d/any-key? (dm/get-in shape [:strokes 0]) :stroke-color :stroke-opacity :stroke-width :stroke-cap-start :stroke-cap-end)
|
(assoc-in [:strokes 0 :stroke-opacity] opacity)
|
||||||
|
|
||||||
|
(and (some? color) (some? width))
|
||||||
|
(assoc-in [:strokes 0 :stroke-width] width)
|
||||||
|
|
||||||
|
(and (some? linecap) (= (:type shape) :path)
|
||||||
|
(or (= linecap :round) (= linecap :square)))
|
||||||
|
(assoc :stroke-cap-start linecap
|
||||||
|
:stroke-cap-end linecap)
|
||||||
|
|
||||||
|
(d/any-key? (dm/get-in shape [:strokes 0])
|
||||||
|
:stroke-color :stroke-opacity :stroke-width
|
||||||
|
:stroke-cap-start :stroke-cap-end)
|
||||||
(assoc-in [:strokes 0 :stroke-style] :svg))))
|
(assoc-in [:strokes 0 :stroke-style] :svg))))
|
||||||
|
|
||||||
(defn setup-opacity [shape]
|
(defn setup-opacity [shape]
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
|
|
||||||
(mf/set-ref-val! prev-val-ref node))))]
|
(mf/set-ref-val! prev-val-ref node))))]
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-layout-effect []
|
||||||
;; On dismount we need to disconnect the current observer
|
;; On dismount we need to disconnect the current observer
|
||||||
(fn []
|
(fn []
|
||||||
(when-let [observer (mf/ref-val observer-ref)]
|
(when-let [observer (mf/ref-val observer-ref)]
|
||||||
|
|
|
@ -89,7 +89,7 @@
|
||||||
(obj/merge! attrs (clj->js fill-attrs)))))
|
(obj/merge! attrs (clj->js fill-attrs)))))
|
||||||
|
|
||||||
(defn add-stroke [attrs stroke-data render-id index]
|
(defn add-stroke [attrs stroke-data render-id index]
|
||||||
(let [stroke-style (:stroke-style stroke-data :none)
|
(let [stroke-style (:stroke-style stroke-data :solid)
|
||||||
stroke-color-gradient-id (str "stroke-color-gradient_" render-id "_" index)
|
stroke-color-gradient-id (str "stroke-color-gradient_" render-id "_" index)
|
||||||
stroke-width (:stroke-width stroke-data 1)]
|
stroke-width (:stroke-width stroke-data 1)]
|
||||||
(if (not= stroke-style :none)
|
(if (not= stroke-style :none)
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
(defn- event-type-names
|
(defn- event-type-names
|
||||||
[]
|
[]
|
||||||
{:click (tr "workspace.options.interaction-on-click")
|
{:click (tr "workspace.options.interaction-on-click")
|
||||||
; TODO: need more UX research
|
;; TODO: need more UX research
|
||||||
;; :mouse-over (tr "workspace.options.interaction-while-hovering")
|
;; :mouse-over (tr "workspace.options.interaction-while-hovering")
|
||||||
;; :mouse-press (tr "workspace.options.interaction-while-pressing")
|
;; :mouse-press (tr "workspace.options.interaction-while-pressing")
|
||||||
:mouse-enter (tr "workspace.options.interaction-mouse-enter")
|
:mouse-enter (tr "workspace.options.interaction-mouse-enter")
|
||||||
|
@ -315,11 +315,12 @@
|
||||||
(when @extended-open?
|
(when @extended-open?
|
||||||
[:div.element-set-content
|
[:div.element-set-content
|
||||||
|
|
||||||
; Trigger select
|
;; Trigger select
|
||||||
[:div.interactions-element.separator
|
[:div.interactions-element.separator
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:event-type interaction))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (:event-type interaction))
|
||||||
:on-change change-event-type}
|
:on-change change-event-type}
|
||||||
(for [[value name] (event-type-names)]
|
(for [[value name] (event-type-names)]
|
||||||
(when-not (and (= value :after-delay)
|
(when-not (and (= value :after-delay)
|
||||||
|
@ -327,7 +328,7 @@
|
||||||
[:option {:key (dm/str value)
|
[:option {:key (dm/str value)
|
||||||
:value (dm/str value)} name]))]]
|
:value (dm/str value)} name]))]]
|
||||||
|
|
||||||
; Delay
|
;; Delay
|
||||||
(when (ctsi/has-delay interaction)
|
(when (ctsi/has-delay interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-delay")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-delay")]
|
||||||
|
@ -338,22 +339,24 @@
|
||||||
:title (tr "workspace.options.interaction-ms")}]
|
:title (tr "workspace.options.interaction-ms")}]
|
||||||
[:span.after (tr "workspace.options.interaction-ms")]]])
|
[:span.after (tr "workspace.options.interaction-ms")]]])
|
||||||
|
|
||||||
; Action select
|
;; Action select
|
||||||
[:div.interactions-element.separator
|
[:div.interactions-element.separator
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:action-type interaction))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (:action-type interaction))
|
||||||
:on-change change-action-type}
|
:on-change change-action-type}
|
||||||
(for [[value name] (action-type-names)]
|
(for [[value name] (action-type-names)]
|
||||||
[:option {:key (dm/str "action-" value)
|
[:option {:key (dm/str "action-" value)
|
||||||
:value (str value)} name])]]
|
:value (str value)} name])]]
|
||||||
|
|
||||||
; Destination
|
;; Destination
|
||||||
(when (ctsi/has-destination interaction)
|
(when (ctsi/has-destination interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:destination interaction))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (:destination interaction))
|
||||||
:on-change change-destination}
|
:on-change change-destination}
|
||||||
(if (= (:action-type interaction) :close-overlay)
|
(if (= (:action-type interaction) :close-overlay)
|
||||||
[:option {:value ""} (tr "workspace.options.interaction-self")]
|
[:option {:value ""} (tr "workspace.options.interaction-self")]
|
||||||
|
@ -364,7 +367,7 @@
|
||||||
[:option {:key (dm/str "destination-" (:id frame))
|
[:option {:key (dm/str "destination-" (:id frame))
|
||||||
:value (str (:id frame))} (:name frame)]))]])
|
:value (str (:id frame))} (:name frame)]))]])
|
||||||
|
|
||||||
; Preserve scroll
|
;; Preserve scroll
|
||||||
(when (ctsi/has-preserve-scroll interaction)
|
(when (ctsi/has-preserve-scroll interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:div.input-checkbox
|
[:div.input-checkbox
|
||||||
|
@ -375,7 +378,7 @@
|
||||||
[:label {:for (str "preserve-" index)}
|
[:label {:for (str "preserve-" index)}
|
||||||
(tr "workspace.options.interaction-preserve-scroll")]]])
|
(tr "workspace.options.interaction-preserve-scroll")]]])
|
||||||
|
|
||||||
; URL
|
;; URL
|
||||||
(when (ctsi/has-url interaction)
|
(when (ctsi/has-url interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-url")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-url")]
|
||||||
|
@ -386,11 +389,12 @@
|
||||||
|
|
||||||
(when (ctsi/has-overlay-opts interaction)
|
(when (ctsi/has-overlay-opts interaction)
|
||||||
[:*
|
[:*
|
||||||
; Overlay position relative-to (select)
|
;; Overlay position relative-to (select)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:position-relative-to interaction))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (:position-relative-to interaction))
|
||||||
:on-change change-position-relative-to}
|
:on-change change-position-relative-to}
|
||||||
(when (not= (:overlay-pos-type interaction) :manual)
|
(when (not= (:overlay-pos-type interaction) :manual)
|
||||||
[:*
|
[:*
|
||||||
|
@ -401,16 +405,17 @@
|
||||||
[:option {:key (dm/str "position-relative-to-" (:id shape))
|
[:option {:key (dm/str "position-relative-to-" (:id shape))
|
||||||
:value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]]
|
:value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]]
|
||||||
|
|
||||||
; Overlay position (select)
|
;; Overlay position (select)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:overlay-pos-type interaction))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (:overlay-pos-type interaction))
|
||||||
:on-change (partial change-overlay-pos-type (:id shape))}
|
:on-change (partial change-overlay-pos-type (:id shape))}
|
||||||
(for [[value name] (overlay-pos-type-names)]
|
(for [[value name] (overlay-pos-type-names)]
|
||||||
[:option {:value (str value)} name])]]
|
[:option {:value (str value)} name])]]
|
||||||
|
|
||||||
; Overlay position (buttons)
|
;; Overlay position (buttons)
|
||||||
[:div.interactions-element.interactions-pos-buttons
|
[:div.interactions-element.interactions-pos-buttons
|
||||||
[:div.element-set-actions-button
|
[:div.element-set-actions-button
|
||||||
{:class (dom/classnames :active (= overlay-pos-type :center))
|
{:class (dom/classnames :active (= overlay-pos-type :center))
|
||||||
|
@ -441,7 +446,7 @@
|
||||||
:on-click #(toggle-overlay-pos-type :bottom-center)}
|
:on-click #(toggle-overlay-pos-type :bottom-center)}
|
||||||
i/position-bottom-center]]
|
i/position-bottom-center]]
|
||||||
|
|
||||||
; Overlay click outside
|
;; Overlay click outside
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:div.input-checkbox
|
[:div.input-checkbox
|
||||||
[:input {:type "checkbox"
|
[:input {:type "checkbox"
|
||||||
|
@ -451,7 +456,7 @@
|
||||||
[:label {:for (str "close-" index)}
|
[:label {:for (str "close-" index)}
|
||||||
(tr "workspace.options.interaction-close-outside")]]]
|
(tr "workspace.options.interaction-close-outside")]]]
|
||||||
|
|
||||||
; Overlay background
|
;; Overlay background
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:div.input-checkbox
|
[:div.input-checkbox
|
||||||
[:input {:type "checkbox"
|
[:input {:type "checkbox"
|
||||||
|
@ -463,17 +468,18 @@
|
||||||
|
|
||||||
(when (ctsi/has-animation? interaction)
|
(when (ctsi/has-animation? interaction)
|
||||||
[:*
|
[:*
|
||||||
; Animation select
|
;; Animation select
|
||||||
[:div.interactions-element.separator
|
[:div.interactions-element.separator
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (-> interaction :animation :animation-type))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (-> interaction :animation :animation-type))
|
||||||
:on-change change-animation-type}
|
:on-change change-animation-type}
|
||||||
[:option {:value ""} (tr "workspace.options.interaction-animation-none")]
|
[:option {:value ""} (tr "workspace.options.interaction-animation-none")]
|
||||||
(for [[value name] (animation-type-names interaction)]
|
(for [[value name] (animation-type-names interaction)]
|
||||||
[:option {:value (str value)} name])]]
|
[:option {:value (str value)} name])]]
|
||||||
|
|
||||||
; Direction
|
;; Direction
|
||||||
(when (ctsi/has-way? interaction)
|
(when (ctsi/has-way? interaction)
|
||||||
[:div.interactions-element.interactions-way-buttons
|
[:div.interactions-element.interactions-way-buttons
|
||||||
[:div.input-radio
|
[:div.input-radio
|
||||||
|
@ -493,7 +499,7 @@
|
||||||
:on-change change-way}]
|
:on-change change-way}]
|
||||||
[:label {:for "way-out"} (tr "workspace.options.interaction-out")]]])
|
[:label {:for "way-out"} (tr "workspace.options.interaction-out")]]])
|
||||||
|
|
||||||
; Direction
|
;; Direction
|
||||||
(when (ctsi/has-direction? interaction)
|
(when (ctsi/has-direction? interaction)
|
||||||
[:div.interactions-element.interactions-direction-buttons
|
[:div.interactions-element.interactions-direction-buttons
|
||||||
[:div.element-set-actions-button
|
[:div.element-set-actions-button
|
||||||
|
@ -513,7 +519,7 @@
|
||||||
:on-click #(change-direction :up)}
|
:on-click #(change-direction :up)}
|
||||||
i/animate-up]])
|
i/animate-up]])
|
||||||
|
|
||||||
; Duration
|
;; Duration
|
||||||
(when (ctsi/has-duration? interaction)
|
(when (ctsi/has-duration? interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")]
|
||||||
|
@ -524,12 +530,13 @@
|
||||||
:title (tr "workspace.options.interaction-ms")}]
|
:title (tr "workspace.options.interaction-ms")}]
|
||||||
[:span.after (tr "workspace.options.interaction-ms")]]])
|
[:span.after (tr "workspace.options.interaction-ms")]]])
|
||||||
|
|
||||||
; Easing
|
;; Easing
|
||||||
(when (ctsi/has-easing? interaction)
|
(when (ctsi/has-easing? interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")]
|
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")]
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (-> interaction :animation :easing))
|
{:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element
|
||||||
|
:value (str (-> interaction :animation :easing))
|
||||||
:on-change change-easing}
|
:on-change change-easing}
|
||||||
(for [[value name] (easing-names)]
|
(for [[value name] (easing-names)]
|
||||||
[:option {:value (str value)} name])]
|
[:option {:value (str value)} name])]
|
||||||
|
@ -541,7 +548,7 @@
|
||||||
:ease-out i/easing-ease-out
|
:ease-out i/easing-ease-out
|
||||||
:ease-in-out i/easing-ease-in-out)]])
|
:ease-in-out i/easing-ease-in-out)]])
|
||||||
|
|
||||||
; Offset effect
|
;; Offset effect
|
||||||
(when (ctsi/has-offset-effect? interaction)
|
(when (ctsi/has-offset-effect? interaction)
|
||||||
[:div.interactions-element
|
[:div.interactions-element
|
||||||
[:div.input-checkbox
|
[:div.input-checkbox
|
||||||
|
|
|
@ -325,7 +325,7 @@
|
||||||
layout-container-ids layout-container-values
|
layout-container-ids layout-container-values
|
||||||
layout-item-ids layout-item-values]
|
layout-item-ids layout-item-values]
|
||||||
(mf/use-memo
|
(mf/use-memo
|
||||||
(mf/deps objects-no-measures)
|
(mf/deps shapes objects-no-measures)
|
||||||
(fn []
|
(fn []
|
||||||
(into
|
(into
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"Color conversion utils."
|
"Color conversion utils."
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.strings :as ust]
|
[app.util.strings :as ust]
|
||||||
|
@ -176,14 +177,16 @@
|
||||||
(= id :multiple)
|
(= id :multiple)
|
||||||
(= file-id :multiple)))
|
(= file-id :multiple)))
|
||||||
|
|
||||||
(defn color? [^string color-str]
|
(defn color?
|
||||||
(and (not (nil? color-str))
|
[color]
|
||||||
(seq color-str)
|
(and (string? color)
|
||||||
(gcolor/isValidColor color-str)))
|
(gcolor/isValidColor color)))
|
||||||
|
|
||||||
(defn parse-color [^string color-str]
|
(defn parse-color
|
||||||
(let [result (gcolor/parse color-str)]
|
[color]
|
||||||
(str (.-hex ^js result))))
|
(when (color? color)
|
||||||
|
(let [result (gcolor/parse color)]
|
||||||
|
(dm/str (.-hex ^js result)))))
|
||||||
|
|
||||||
(def color-names
|
(def color-names
|
||||||
(obj/get-keys ^js gcolor/names))
|
(obj/get-keys ^js gcolor/names))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue