diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index cbe153b340..4085b6d50d 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -23,6 +23,9 @@ {:unsorted-required-namespaces {:level :warning} + :potok/reify-type + {:level :error} + :unresolved-namespace {:level :warning :exclude [data_readers]} diff --git a/.clj-kondo/hooks/export.clj b/.clj-kondo/hooks/export.clj index f29cc1a156..e8acc8e11a 100644 --- a/.clj-kondo/hooks/export.clj +++ b/.clj-kondo/hooks/export.clj @@ -10,15 +10,34 @@ sname])] {:node result})) +(def registry (atom {})) + (defn potok-reify - [{:keys [:node]}] + [{:keys [:node :filename] :as params}] (let [[rnode rtype & other] (:children node) - result (api/list-node - (into [(api/token-node (symbol "deftype")) - (api/token-node (gensym (name (:k rtype)))) - (api/vector-node [])] - other))] - {:node result})) + rsym (symbol (str "event-type-" (name (:k rtype)))) + reg (get @registry filename #{})] + (when-not (:namespaced? rtype) + (let [{:keys [:row :col]} (meta rtype)] + (api/reg-finding! {:message "ptk/reify type should be namespaced" + :type :potok/reify-type + :row row + :col col}))) + + (if (contains? reg rsym) + (let [{:keys [:row :col]} (meta rtype)] + (api/reg-finding! {:message (str "duplicate type: " (name (:k rtype))) + :type :potok/reify-type + :row row + :col col})) + (swap! registry update filename (fnil conj #{}) rsym)) + + (let [result (api/list-node + (into [(api/token-node (symbol "deftype")) + (api/token-node rsym) + (api/vector-node [])] + other))] + {:node result}))) (defn clojure-specify [{:keys [:node]}] diff --git a/CHANGES.md b/CHANGES.md index 7ddbee7cb8..25f3d848c8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,14 +1,62 @@ -# CHANGELOG # +# CHANGELOG ## :rocket: Next +### :boom: Breaking changes ### :sparkles: New features - ### :bug: Bugs fixed ### :arrow_up: Deps updates ### :boom: Breaking changes ### :heart: Community contributions by (Thank you!) + +## 1.8.0-alpha + +### :boom: Breaking changes + +- This release includes a new approach for handling share links, and + this feature is incompatible with the previous one. This means that + all the public share links generated previously will stop working. + +### :sparkles: New features + +- Add tooltips to color picker tabs [Taiga #1814](https://tree.taiga.io/project/penpot/us/1814). +- Add styling to the end point of any open paths [Taiga #1107](https://tree.taiga.io/project/penpot/us/1107). +- Allow to zoom with ctrl + middle button [Taiga #1428](https://tree.taiga.io/project/penpot/us/1428). +- Auto placement of duplicated objects [Taiga #1386](https://tree.taiga.io/project/penpot/us/1386). +- Enable penpot SVG metadata only when exporting complete files [Taiga #1914](https://tree.taiga.io/project/penpot/us/1914?milestone=295883). +- Export to PDF all artboards of one page [Taiga #1895](https://tree.taiga.io/project/penpot/us/1895). +- Go to a undo step clicking on a history element of the list [Taiga #1374](https://tree.taiga.io/project/penpot/us/1374). +- Increment font size by 10 with shift+arrows [1047](https://github.com/penpot/penpot/issues/1047). +- New shortcut to detach components Ctrl+Shift+K [Taiga #1799](https://tree.taiga.io/project/penpot/us/1799). +- Set email inputs to type "email", to aid keyboard entry [Taiga #1921](https://tree.taiga.io/project/penpot/issue/1921). +- Use shift+move to move element orthogonally [#823](https://github.com/penpot/penpot/issues/823). +- Use space + mouse drag to pan, instead of only space [Taiga #1800](https://tree.taiga.io/project/penpot/us/1800). +- Allow navigate through pages on the viewer [Taiga #1550](https://tree.taiga.io/project/penpot/us/1550). +- Allow create share links with specific pages [Taiga #1844](https://tree.taiga.io/project/penpot/us/1844). + +### :bug: Bugs fixed + +- Prevent adding numeric suffix to layer names when not needed [Taiga #1929](https://tree.taiga.io/project/penpot/us/1929). +- Prevent deleting or moving the drafts project [Taiga #1935](https://tree.taiga.io/project/penpot/issue/1935). +- Fix problem with zoom and selection [Taiga #1919](https://tree.taiga.io/project/penpot/issue/1919) +- Fix problem with borders on shape export [#1092](https://github.com/penpot/penpot/issues/1092) +- Fix thumbnail cropping issue [Taiga #1964](https://tree.taiga.io/project/penpot/issue/1964) +- Fix repeated fetch on file selection [Taiga #1933](https://tree.taiga.io/project/penpot/issue/1933) +- Fix rename typography on text options [Taiga #1963](https://tree.taiga.io/project/penpot/issue/1963) +- Fix problems with order in groups [Taiga #1960](https://tree.taiga.io/project/penpot/issue/1960) +- Fix SVG components preview [#1134](https://github.com/penpot/penpot/issues/1134) +- Fix group renaming problem [Taiga #1969](https://tree.taiga.io/project/penpot/issue/1969) +- Fix problem with import broken images links [#1197](https://github.com/penpot/penpot/issues/1197) +- Fix problem while moving imported SVG's [#1199](https://github.com/penpot/penpot/issues/1199) + +### :arrow_up: Deps updates +### :boom: Breaking changes +### :heart: Community contributions by (Thank you!) + +- eduayme [#1129](https://github.com/penpot/penpot/pull/1129). + + ## 1.7.4-alpha ### :bug: Bugs fixed @@ -43,7 +91,6 @@ - Update frontend build tooling. -### :boom: Breaking changes ### :heart: Community contributions by (Thank you!) - soultipsy [#1100](https://github.com/penpot/penpot/pull/1100) @@ -94,10 +141,6 @@ - Fix dynamic alignment enabled with hidden objects [#1063](https://github.com/penpot/penpot/issues/1063) -### :arrow_up: Deps updates -### :boom: Breaking changes -### :heart: Community contributions by (Thank you!) - ## 1.6.5-alpha ### :bug: Bugs fixed diff --git a/backend/resources/emails-mjml/register/en.mjml b/backend/resources/emails-mjml/register/en.mjml index 177e1e8ab1..899c96b7f1 100644 --- a/backend/resources/emails-mjml/register/en.mjml +++ b/backend/resources/emails-mjml/register/en.mjml @@ -22,7 +22,7 @@ Hello {{name}}! Thanks for signing up for your Penpot account! Please verify your - email using the link below adn get started building mockups and + email using the link below and get started building mockups and prototypes today! diff --git a/backend/resources/emails/register/en.html b/backend/resources/emails/register/en.html index e813d7a596..b16eb304f8 100644 --- a/backend/resources/emails/register/en.html +++ b/backend/resources/emails/register/en.html @@ -173,7 +173,7 @@ -
Thanks for signing up for your Penpot account! Please verify your email using the link below adn get started building mockups and prototypes today!
+
Thanks for signing up for your Penpot account! Please verify your email using the link below and get started building mockups and prototypes today!
@@ -465,4 +465,4 @@ - \ No newline at end of file + diff --git a/backend/resources/emails/register/en.txt b/backend/resources/emails/register/en.txt index 84ca0d487a..41a9bd8d9f 100644 --- a/backend/resources/emails/register/en.txt +++ b/backend/resources/emails/register/en.txt @@ -1,7 +1,7 @@ Hello {{name}}! Thanks for signing up for your Penpot account! Please verify your email using the -link below adn get started building mockups and prototypes today! +link below and get started building mockups and prototypes today! {{ public-uri }}/#/auth/verify-token?token={{token}} diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj index 6e2e086bb2..6f1b940afb 100644 --- a/backend/src/app/db.clj +++ b/backend/src/app/db.clj @@ -231,9 +231,9 @@ (defn get-by-params ([ds table params] (get-by-params ds table params nil)) - ([ds table params {:keys [uncheked] :or {uncheked false} :as opts}] + ([ds table params {:keys [check-not-found] :or {check-not-found true} :as opts}] (let [res (exec-one! ds (sql/select table params opts))] - (when (and (not uncheked) (or (not res) (is-deleted? res))) + (when (and check-not-found (or (not res) (is-deleted? res))) (ex/raise :type :not-found :table table :hint "database object not found")) @@ -267,13 +267,28 @@ (instance? PGpoint v)) (defn pgarray? - [v] - (instance? PgArray v)) + ([v] (instance? PgArray v)) + ([v type] + (and (instance? PgArray v) + (= type (.getBaseTypeName ^PgArray v))))) (defn pgarray-of-uuid? [v] (and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v)))) +(defn decode-pgarray + ([v] (into [] (.getArray ^PgArray v))) + ([v in] (into in (.getArray ^PgArray v))) + ([v in xf] (into in xf (.getArray ^PgArray v)))) + +(defn pgarray->set + [v] + (set (.getArray ^PgArray v))) + +(defn pgarray->vector + [v] + (vec (.getArray ^PgArray v))) + (defn pgpoint [p] (PGpoint. (:x p) (:y p))) @@ -285,7 +300,6 @@ (.createArrayOf conn ^String type (into-array Object objects)) (.createArrayOf conn ^String type objects)))) - (defn decode-pgpoint [^PGpoint v] (gpt/point (.-x v) (.-y v))) @@ -369,15 +383,6 @@ (.setType "jsonb") (.setValue (json/encode-str data)))) -(defn pgarray->set - [v] - (set (.getArray ^PgArray v))) - -(defn pgarray->vector - [v] - (vec (.getArray ^PgArray v))) - - ;; --- Locks (defn- xact-check-param diff --git a/backend/src/app/http.clj b/backend/src/app/http.clj index 248a125dd2..3681118ad0 100644 --- a/backend/src/app/http.clj +++ b/backend/src/app/http.clj @@ -114,9 +114,14 @@ (s/def ::storage map?) (s/def ::assets map?) (s/def ::feedback fn?) +(s/def ::error-report-handler fn?) +(s/def ::audit-http-handler fn?) (defmethod ig/pre-init-spec ::router [_] - (s/keys :req-un [::rpc ::session ::mtx/metrics ::oauth ::storage ::assets ::feedback])) + (s/keys :req-un [::rpc ::session ::mtx/metrics + ::oauth ::storage ::assets ::feedback + ::error-report-handler + ::audit-http-handler])) (defmethod ig/init-key ::router [_ {:keys [session rpc oauth metrics assets feedback] :as cfg}] @@ -136,9 +141,7 @@ ["/webhooks" ["/sns" {:post (:sns-webhook cfg)}]] - ["/api" {:middleware [ - ;; Temporary disabled - #_[middleware/etag] + ["/api" {:middleware [[middleware/etag] [middleware/format-response-body] [middleware/params] [middleware/multipart-params] @@ -149,10 +152,12 @@ ["/feedback" {:middleware [(:middleware session)] :post feedback}] - ["/auth/oauth/:provider" {:post (:handler oauth)}] ["/auth/oauth/:provider/callback" {:get (:callback-handler oauth)}] + ["/audit/events" {:middleware [(:middleware session)] + :post (:audit-http-handler cfg)}] + ["/rpc" {:middleware [(:middleware session)]} ["/query/:type" {:get (:query-handler rpc) :post (:query-handler rpc)}] diff --git a/backend/src/app/http/middleware.clj b/backend/src/app/http/middleware.clj index 42babf3dc8..c6ee09f2ee 100644 --- a/backend/src/app/http/middleware.clj +++ b/backend/src/app/http/middleware.clj @@ -13,7 +13,6 @@ [buddy.core.codecs :as bc] [buddy.core.hash :as bh] [clojure.java.io :as io] - [ring.core.protocols :as rp] [ring.middleware.cookies :refer [wrap-cookies]] [ring.middleware.keyword-params :refer [wrap-keyword-params]] [ring.middleware.multipart-params :refer [wrap-multipart-params]] @@ -74,33 +73,15 @@ {:name ::parse-request-body :compile (constantly wrap-parse-request-body)}) -(defn- transit-streamable-body - [data opts] - (reify rp/StreamableResponseBody - (write-body-to-stream [_ response output-stream] - (try - (let [tw (t/writer output-stream opts)] - (t/write! tw data)) - (finally - (.close ^java.io.OutputStream output-stream)))))) - (defn- impl-format-response-body [response _request] (let [body (:body response) - opts {:type :json-verbose}] + opts {:type :json}] (cond (coll? body) (-> response (update :headers assoc "content-type" "application/transit+json") - (assoc :body (transit-streamable-body body opts))) - - ;; ;; Temporary disabled - ;; (-> response - ;; (update :headers assoc "content-type" "application/transit+json") - ;; (assoc :body - ;; (if (= :post (:request-method request)) - ;; (transit-streamable-body body opts) - ;; (t/encode body opts)))) + (assoc :body (t/encode body opts))) (nil? body) (assoc response :status 204 :body "") diff --git a/backend/src/app/loggers/audit.clj b/backend/src/app/loggers/audit.clj index dbdff89208..854884634e 100644 --- a/backend/src/app/loggers/audit.clj +++ b/backend/src/app/loggers/audit.clj @@ -23,7 +23,8 @@ [clojure.spec.alpha :as s] [cuerdas.core :as str] [integrant.core :as ig] - [lambdaisland.uri :as u])) + [lambdaisland.uri :as u] + [promesa.exec :as px])) (defn parse-client-ip [{:keys [headers] :as request}] @@ -67,6 +68,65 @@ (update event :props #(-> % clean-common clean-profile-id clean-complex-data)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HTTP Handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare persist-http-events) + +(s/def ::profile-id ::us/uuid) +(s/def ::name ::us/string) +(s/def ::type ::us/string) +(s/def ::props (s/map-of ::us/keyword any?)) +(s/def ::timestamp dt/instant?) +(s/def ::context (s/map-of ::us/keyword any?)) + +(s/def ::event + (s/keys :req-un [::type ::name ::props ::timestamp ::profile-id] + :opt-un [::context])) + +(s/def ::events (s/every ::event)) + +(defmethod ig/init-key ::http-handler + [_ {:keys [executor enabled] :as cfg}] + (fn [{:keys [params _headers _cookies profile-id] :as request}] + (when enabled + (let [events (->> (:events params) + (remove #(not= profile-id (:profile-id %))) + (us/conform ::events)) + ip-addr (parse-client-ip request) + cfg (-> cfg + (assoc :source "frontend") + (assoc :events events) + (assoc :ip-addr ip-addr))] + (px/run! executor #(persist-http-events cfg)))) + {:status 204 :body ""})) + +(defn- persist-http-events + [{:keys [pool events ip-addr source] :as cfg}] + (try + (let [columns [:id :name :source :type :tracked-at :profile-id :ip-addr :props :context] + prepare-xf (map (fn [event] + [(uuid/next) + (:name event) + source + (:type event) + (:timestamp event) + (:profile-id event) + (db/inet ip-addr) + (db/tjson (:props event)) + (db/tjson (d/without-nils (:context event)))])) + events (us/conform ::events events) + rows (into [] prepare-xf events)] + (db/insert-multi! pool :audit-log columns rows)) + (catch Throwable e + (let [xdata (ex-data e)] + (if (= :spec-validation (:code xdata)) + (l/error ::l/raw (str "spec validation on persist-events:\n" + (:explain xdata))) + (l/error :hint "error on persist-events" + :cause e)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Collector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -103,7 +163,9 @@ (recur))) (fn [& {:keys [cmd] :as params}] - (let [params (dissoc params :cmd)] + (let [params (-> params + (dissoc :cmd) + (assoc :tracked-at (dt/now)))] (case cmd :stop (a/close! input) :submit (when-not (a/offer! input params) @@ -117,13 +179,14 @@ (:name event) (:type event) (:profile-id event) + (:tracked-at event) (some-> (:ip-addr event) db/inet) - (db/tjson (:props event))])] - + (db/tjson (:props event)) + "backend"])] (aa/with-thread executor (db/with-atomic [conn pool] (db/insert-multi! conn :audit-log - [:id :name :type :profile-id :ip-addr :props] + [:id :name :type :profile-id :tracked-at :ip-addr :props :source] (sequence (map event->row) events)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -144,16 +207,22 @@ (defmethod ig/init-key ::archive-task [_ {:keys [uri enabled] :as cfg}] - (fn [_] - (when (and enabled (not uri)) - (ex/raise :type :internal - :code :task-not-configured - :hint "archive task not configured, missing uri")) - (loop [] - (let [res (archive-events cfg)] - (when (= res :continue) - (aa/thread-sleep 200) - (recur)))))) + (fn [props] + ;; NOTE: this let allows overwrite default configured values from + ;; the repl, when manually invoking the task. + (let [enabled (or enabled (:enabled props false)) + uri (or uri (:uri props)) + cfg (assoc cfg :uri uri)] + (when (and enabled (not uri)) + (ex/raise :type :internal + :code :task-not-configured + :hint "archive task not configured, missing uri")) + (when enabled + (loop [] + (let [res (archive-events cfg)] + (when (= res :continue) + (aa/thread-sleep 200) + (recur)))))))) (def sql:retrieve-batch-of-audit-log "select * from audit_log @@ -164,22 +233,27 @@ (defn archive-events [{:keys [pool uri tokens] :as cfg}] - (letfn [(decode-row [{:keys [props ip-addr] :as row}] + (letfn [(decode-row [{:keys [props ip-addr context] :as row}] (cond-> row (db/pgobject? props) (assoc :props (db/decode-transit-pgobject props)) + (db/pgobject? context) + (assoc :context (db/decode-transit-pgobject context)) + (db/pgobject? ip-addr "inet") (assoc :ip-addr (db/decode-inet ip-addr)))) - (row->event [{:keys [name type created-at profile-id props ip-addr]}] - (cond-> {:type type - :name name - :timestamp created-at - :profile-id profile-id - :props props} - (some? ip-addr) - (update :context assoc :source-ip ip-addr))) + (row->event [row] + (select-keys row [:type + :name + :source + :created-at + :tracked-at + :profile-id + :ip-addr + :props + :context])) (send [events] (let [token (tokens :generate {:iss "authentication" diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index e882f71658..bfa65c4d4c 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -28,11 +28,24 @@ {:name "actions_profile_register_count" :help "A global counter of user registrations." :type :counter} + :profile-activation {:name "actions_profile_activation_count" :help "A global counter of profile activations" + :type :counter} + + :update-file-changes + {:name "rpc_update_file_changes_total" + :help "A total number of changes submitted to update-file." + :type :counter} + + :update-file-bytes-processed + {:name "rpc_update_file_bytes_processed_total" + :help "A total number of bytes processed by update-file." :type :counter}}} + + :app.migrations/all {:main (ig/ref :app.migrations/migrations)} @@ -95,6 +108,7 @@ :storage (ig/ref :app.storage/storage) :sns-webhook (ig/ref :app.http.awsns/handler) :feedback (ig/ref :app.http.feedback/handler) + :audit-http-handler (ig/ref :app.loggers.audit/http-handler) :error-report-handler (ig/ref :app.loggers.mattermost/handler)} :app.http.assets/handlers @@ -289,6 +303,11 @@ :app.loggers.zmq/receiver {:endpoint (cf/get :loggers-zmq-uri)} + :app.loggers.audit/http-handler + {:enabled (cf/get :audit-enabled false) + :pool (ig/ref :app.db/pool) + :executor (ig/ref :app.worker/executor)} + :app.loggers.audit/collector {:enabled (cf/get :audit-enabled false) :pool (ig/ref :app.db/pool) diff --git a/backend/src/app/metrics.clj b/backend/src/app/metrics.clj index 74fb318ac9..148e9b3a2e 100644 --- a/backend/src/app/metrics.clj +++ b/backend/src/app/metrics.clj @@ -92,18 +92,14 @@ _ (when (seq labels) (.labelNames instance (into-array String labels))) instance (.register instance registry)] - (reify - clojure.lang.IDeref - (deref [_] instance) - clojure.lang.IFn - (invoke [_ cmd] - (.inc ^Counter instance)) - - (invoke [_ cmd labels] - (.. ^Counter instance - (labels (into-array String labels)) - (inc)))))) + {::instance instance + ::fn (fn [{:keys [by labels] :or {by 1}}] + (if labels + (.. ^Counter instance + (labels (into-array String labels)) + (inc by)) + (.inc ^Counter instance by)))})) (defn make-gauge [{:keys [name help registry reg labels] :as props}] @@ -115,21 +111,16 @@ (.labelNames instance (into-array String labels))) instance (.register instance registry)] - (reify - clojure.lang.IDeref - (deref [_] instance) - - clojure.lang.IFn - (invoke [_ cmd] - (case cmd - :inc (.inc ^Gauge instance) - :dec (.dec ^Gauge instance))) - - (invoke [_ cmd labels] - (let [labels (into-array String [labels])] - (case cmd - :inc (.. ^Gauge instance (labels labels) (inc)) - :dec (.. ^Gauge instance (labels labels) (dec)))))))) + {::instance instance + ::fn (fn [{:keys [cmd by labels] :or {by 1}}] + (if labels + (let [labels (into-array String [labels])] + (case cmd + :inc (.. ^Gauge instance (labels labels) (inc by)) + :dec (.. ^Gauge instance (labels labels) (dec by)))) + (case cmd + :inc (.inc ^Gauge instance by) + :dec (.dec ^Gauge instance by))))})) (def default-quantiles [[0.75 0.02] @@ -150,18 +141,14 @@ _ (when (seq labels) (.labelNames instance (into-array String labels))) instance (.register instance registry)] - (reify - clojure.lang.IDeref - (deref [_] instance) - clojure.lang.IFn - (invoke [_ cmd val] - (.observe ^Summary instance val)) - - (invoke [_ cmd val labels] - (.. ^Summary instance - (labels (into-array String labels)) - (observe val)))))) + {::instance instance + ::fn (fn [{:keys [val labels]}] + (if labels + (.. ^Summary instance + (labels (into-array String labels)) + (observe val)) + (.observe ^Summary instance val)))})) (def default-histogram-buckets [1 5 10 25 50 75 100 250 500 750 1000 2500 5000 7500]) @@ -177,18 +164,14 @@ _ (when (seq labels) (.labelNames instance (into-array String labels))) instance (.register instance registry)] - (reify - clojure.lang.IDeref - (deref [_] instance) - clojure.lang.IFn - (invoke [_ cmd val] - (.observe ^Histogram instance val)) - - (invoke [_ cmd val labels] - (.. ^Histogram instance - (labels (into-array String labels)) - (observe val)))))) + {::instance instance + ::fn (fn [{:keys [val labels]}] + (if labels + (.. ^Histogram instance + (labels (into-array String labels)) + (observe val)) + (.observe ^Histogram instance val)))})) (defn create [{:keys [type] :as props}] @@ -205,19 +188,19 @@ (with-meta (fn ([a] - (mobj :inc) + ((::fn mobj) nil) (origf a)) ([a b] - (mobj :inc) + ((::fn mobj) nil) (origf a b)) ([a b c] - (mobj :inc) + ((::fn mobj) nil) (origf a b c)) ([a b c d] - (mobj :inc) + ((::fn mobj) nil) (origf a b c d)) ([a b c d & more] - (mobj :inc) + ((::fn mobj) nil) (apply origf a b c d more))) (assoc mdata ::original origf)))) ([rootf mobj labels] @@ -226,13 +209,13 @@ (with-meta (fn ([a] - (mobj :inc labels) + ((::fn mobj) {:labels labels}) (origf a)) ([a b] - (mobj :inc labels) + ((::fn mobj) {:labels labels}) (origf a b)) ([a b & more] - (mobj :inc labels) + ((::fn mobj) {:labels labels}) (apply origf a b more))) (assoc mdata ::original origf))))) @@ -245,15 +228,15 @@ ([a] (with-measure :expr (origf a) - :cb #(mobj :observe %))) + :cb #((::fn mobj) {:val %}))) ([a b] (with-measure :expr (origf a b) - :cb #(mobj :observe %))) + :cb #((::fn mobj) {:val %}))) ([a b & more] (with-measure :expr (apply origf a b more) - :cb #(mobj :observe %)))) + :cb #((::fn mobj) {:val %})))) (assoc mdata ::original origf)))) ([rootf mobj labels] @@ -264,26 +247,26 @@ ([a] (with-measure :expr (origf a) - :cb #(mobj :observe % labels))) + :cb #((::fn mobj) {:val % :labels labels}))) ([a b] (with-measure :expr (origf a b) - :cb #(mobj :observe % labels))) + :cb #((::fn mobj) {:val % :labels labels}))) ([a b & more] (with-measure :expr (apply origf a b more) - :cb #(mobj :observe % labels)))) + :cb #((::fn mobj) {:val % :labels labels})))) (assoc mdata ::original origf))))) (defn instrument-vars! [vars {:keys [wrap] :as props}] (let [obj (create props)] (cond - (instance? Counter @obj) + (instance? Counter (::instance obj)) (doseq [var vars] (alter-var-root var (or wrap wrap-counter) obj)) - (instance? Summary @obj) + (instance? Summary (::instance obj)) (doseq [var vars] (alter-var-root var (or wrap wrap-summary) obj)) @@ -294,13 +277,13 @@ [f {:keys [wrap] :as props}] (let [obj (create props)] (cond - (instance? Counter @obj) + (instance? Counter (::instance obj)) ((or wrap wrap-counter) f obj) - (instance? Summary @obj) + (instance? Summary (::instance obj)) ((or wrap wrap-summary) f obj) - (instance? Histogram @obj) + (instance? Histogram (::instance obj)) ((or wrap wrap-summary) f obj) :else diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index c0f62cf3f1..d1afda81ee 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -193,6 +193,15 @@ {:name "0061-mod-file-table" :fn (mg/resource "app/migrations/sql/0061-mod-file-table.sql")} + + {:name "0062-fix-metadata-media" + :fn (mg/resource "app/migrations/sql/0062-fix-metadata-media.sql")} + + {:name "0063-add-share-link-table" + :fn (mg/resource "app/migrations/sql/0063-add-share-link-table.sql")} + + {:name "0064-mod-audit-log-table" + :fn (mg/resource "app/migrations/sql/0064-mod-audit-log-table.sql")} ]) diff --git a/backend/src/app/migrations/sql/0063-add-share-link-table.sql b/backend/src/app/migrations/sql/0063-add-share-link-table.sql new file mode 100644 index 0000000000..6c27900454 --- /dev/null +++ b/backend/src/app/migrations/sql/0063-add-share-link-table.sql @@ -0,0 +1,12 @@ +CREATE TABLE share_link ( + id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + file_id uuid NOT NULL REFERENCES file(id) ON DELETE CASCADE DEFERRABLE, + owner_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL DEFERRABLE, + created_at timestamptz NOT NULL DEFAULT clock_timestamp(), + + pages uuid[], + flags text[] +); + +CREATE INDEX share_link_file_id_idx ON share_link(file_id); +CREATE INDEX share_link_owner_id_idx ON share_link(owner_id); diff --git a/backend/src/app/migrations/sql/0064-mod-audit-log-table.sql b/backend/src/app/migrations/sql/0064-mod-audit-log-table.sql new file mode 100644 index 0000000000..5d85898b9f --- /dev/null +++ b/backend/src/app/migrations/sql/0064-mod-audit-log-table.sql @@ -0,0 +1,13 @@ +ALTER TABLE audit_log + ADD COLUMN tracked_at timestamptz NULL DEFAULT clock_timestamp(), + ADD COLUMN source text NULL, + ADD COLUMN context jsonb NULL; + +ALTER TABLE audit_log + ALTER COLUMN source SET STORAGE external, + ALTER COLUMN context SET STORAGE external; + +UPDATE audit_log SET source = 'backend', tracked_at=created_at; + +-- ALTER TABLE audit_log ALTER COLUMN source SET NOT NULL; +-- ALTER TABLE audit_log ALTER COLUMN tracked_at SET NOT NULL; diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index 09a2a636a9..d77f83e3e8 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -117,14 +117,14 @@ profile-id (or (:profile-id params') (:profile-id result) (::audit/profile-id resultm)) - props (d/merge params (::audit/props resultm))] + props (d/merge params' (::audit/props resultm))] (audit :cmd :submit :type (::type cfg) :name (or (::audit/name resultm) (::sv/name mdata)) :profile-id profile-id :ip-addr (audit/parse-client-ip request) - :props (audit/profile->props props)))) + :props props))) result)))) @@ -175,6 +175,7 @@ 'app.rpc.mutations.management 'app.rpc.mutations.ldap 'app.rpc.mutations.fonts + 'app.rpc.mutations.share-link 'app.rpc.mutations.verify-token) (map (partial process-method cfg)) (into {})))) diff --git a/backend/src/app/rpc/mutations/files.clj b/backend/src/app/rpc/mutations/files.clj index 6436f0fc92..d84911ef84 100644 --- a/backend/src/app/rpc/mutations/files.clj +++ b/backend/src/app/rpc/mutations/files.clj @@ -12,6 +12,7 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.db :as db] + [app.metrics :as mtx] [app.rpc.permissions :as perms] [app.rpc.queries.files :as files] [app.rpc.queries.projects :as proj] @@ -291,7 +292,7 @@ (simpl/del-object backend file))) (defn- update-file - [{:keys [conn] :as cfg} {:keys [file changes changes-with-metadata session-id profile-id] :as params}] + [{:keys [conn metrics] :as cfg} {:keys [file changes changes-with-metadata session-id profile-id] :as params}] (when (> (:revn params) (:revn file)) @@ -301,14 +302,22 @@ :context {:incoming-revn (:revn params) :stored-revn (:revn file)})) - (let [changes (if changes-with-metadata + (let [mtx1 (get-in metrics [:definitions :update-file-changes]) + mtx2 (get-in metrics [:definitions :update-file-bytes-processed]) + + changes (if changes-with-metadata (mapcat :changes changes-with-metadata) changes) + ;; Trace the number of changes processed + _ ((::mtx/fn mtx1) {:by (count changes)}) + ts (dt/now) file (-> (files/retrieve-data cfg file) (update :revn inc) (update :data (fn [data] + ;; Trace the length of bytes of processed data + ((::mtx/fn mtx2) {:by (alength data)}) (-> data (blob/decode) (assoc :id (:id file)) diff --git a/backend/src/app/rpc/mutations/profile.clj b/backend/src/app/rpc/mutations/profile.clj index cc1dce0799..099092ba59 100644 --- a/backend/src/app/rpc/mutations/profile.clj +++ b/backend/src/app/rpc/mutations/profile.clj @@ -15,6 +15,7 @@ [app.http.oauth :refer [extract-props]] [app.loggers.audit :as audit] [app.media :as media] + [app.metrics :as mtx] [app.rpc.mutations.projects :as projects] [app.rpc.mutations.teams :as teams] [app.rpc.queries.profile :as profile] @@ -150,7 +151,8 @@ transaction is completed." [metrics] (fn [] - ((get-in metrics [:definitions :profile-register]) :inc))) + (let [mobj (get-in metrics [:definitions :profile-register])] + ((::mtx/fn mobj) {:by 1})))) (defn register-profile [{:keys [conn tokens session metrics] :as cfg} {:keys [token] :as params}] diff --git a/backend/src/app/rpc/mutations/share_link.clj b/backend/src/app/rpc/mutations/share_link.clj new file mode 100644 index 0000000000..0e366957fd --- /dev/null +++ b/backend/src/app/rpc/mutations/share_link.clj @@ -0,0 +1,67 @@ +;; 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) UXBOX Labs SL + +(ns app.rpc.mutations.share-link + "Share link related rpc mutation methods." + (:require + [app.common.spec :as us] + [app.common.uuid :as uuid] + [app.db :as db] + [app.rpc.queries.files :as files] + [app.util.services :as sv] + [clojure.spec.alpha :as s])) + +;; --- Helpers & Specs + +(s/def ::id ::us/uuid) +(s/def ::profile-id ::us/uuid) +(s/def ::file-id ::us/uuid) +(s/def ::flags (s/every ::us/string :kind set?)) +(s/def ::pages (s/every ::us/uuid :kind set?)) + +;; --- Mutation: Create Share Link + +(declare create-share-link) + +(s/def ::create-share-link + (s/keys :req-un [::profile-id ::file-id ::flags] + :opt-un [::pages])) + +(sv/defmethod ::create-share-link + [{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] + (db/with-atomic [conn pool] + (files/check-edition-permissions! conn profile-id file-id) + (create-share-link conn params))) + +(defn create-share-link + [conn {:keys [profile-id file-id pages flags]}] + (let [pages (db/create-array conn "uuid" pages) + flags (->> (map name flags) + (db/create-array conn "text")) + slink (db/insert! conn :share-link + {:id (uuid/next) + :file-id file-id + :flags flags + :pages pages + :owner-id profile-id})] + (-> slink + (update :pages db/decode-pgarray #{}) + (update :flags db/decode-pgarray #{})))) + +;; --- Mutation: Delete Share Link + +(declare delete-share-link) + +(s/def ::delete-share-link + (s/keys :req-un [::profile-id ::id])) + +(sv/defmethod ::delete-share-link + [{:keys [pool] :as cfg} {:keys [profile-id id] :as params}] + (db/with-atomic [conn pool] + (let [slink (db/get-by-id conn :share-link id)] + (files/check-edition-permissions! conn profile-id (:file-id slink)) + (db/delete! conn :share-link {:id id}) + nil))) diff --git a/backend/src/app/rpc/mutations/verify_token.clj b/backend/src/app/rpc/mutations/verify_token.clj index e294cfb739..61b5f9abb8 100644 --- a/backend/src/app/rpc/mutations/verify_token.clj +++ b/backend/src/app/rpc/mutations/verify_token.clj @@ -9,6 +9,7 @@ [app.common.exceptions :as ex] [app.common.spec :as us] [app.db :as db] + [app.metrics :as mtx] [app.rpc.mutations.teams :as teams] [app.rpc.queries.profile :as profile] [app.util.services :as sv] @@ -42,7 +43,8 @@ transaction is completed." [metrics] (fn [] - ((get-in metrics [:definitions :profile-activation]) :inc))) + (let [mobj (get-in metrics [:definitions :profile-activation])] + ((::mtx/fn mobj) {:by 1})))) (defmethod process-token :verify-email [{:keys [conn session metrics] :as cfg} _ {:keys [profile-id] :as claims}] diff --git a/backend/src/app/rpc/permissions.clj b/backend/src/app/rpc/permissions.clj index 221b883632..9481bcd575 100644 --- a/backend/src/app/rpc/permissions.clj +++ b/backend/src/app/rpc/permissions.clj @@ -37,6 +37,41 @@ :is-admin false :can-edit false))) +(defn make-edition-predicate-fn + "A simple factory for edition permission predicate functions." + [qfn] + (us/assert fn? qfn) + (fn [& args] + (let [rows (apply qfn args)] + (when-not (or (empty? rows) + (not (or (some :can-edit rows) + (some :is-admin rows) + (some :is-owner rows)))) + rows)))) + +(defn make-read-predicate-fn + "A simple factory for read permission predicate functions." + [qfn] + (us/assert fn? qfn) + (fn [& args] + (let [rows (apply qfn args)] + (when (seq rows) + rows)))) + +(defn make-check-fn + "Helper that converts a predicate permission function to a check + function (function that raises an exception)." + [pred] + (fn [& args] + (when-not (seq (apply pred args)) + (ex/raise :type :not-found + :code :object-not-found + :hint "not found")))) + + +;; TODO: the following functions are deprecated and replaced with the +;; new ones. Should not be used. + (defn make-edition-check-fn "A simple factory for edition permission check functions." [qfn] diff --git a/backend/src/app/rpc/queries/files.clj b/backend/src/app/rpc/queries/files.clj index 55f536d5f3..032032216f 100644 --- a/backend/src/app/rpc/queries/files.clj +++ b/backend/src/app/rpc/queries/files.clj @@ -61,16 +61,23 @@ (defn- retrieve-file-permissions [conn profile-id file-id] - (db/exec! conn [sql:file-permissions - file-id profile-id - file-id profile-id - file-id profile-id])) + (when (and profile-id file-id) + (db/exec! conn [sql:file-permissions + file-id profile-id + file-id profile-id + file-id profile-id]))) + +(def has-edit-permissions? + (perms/make-edition-predicate-fn retrieve-file-permissions)) + +(def has-read-permissions? + (perms/make-read-predicate-fn retrieve-file-permissions)) (def check-edition-permissions! - (perms/make-edition-check-fn retrieve-file-permissions)) + (perms/make-check-fn has-edit-permissions?)) (def check-read-permissions! - (perms/make-read-check-fn retrieve-file-permissions)) + (perms/make-check-fn has-read-permissions?)) ;; --- Query: Files search diff --git a/backend/src/app/rpc/queries/viewer.clj b/backend/src/app/rpc/queries/viewer.clj index dfe95314cc..2f5334d995 100644 --- a/backend/src/app/rpc/queries/viewer.clj +++ b/backend/src/app/rpc/queries/viewer.clj @@ -14,24 +14,98 @@ [app.util.services :as sv] [clojure.spec.alpha :as s])) +;; --- Query: View Only Bundle + +(defn- decode-share-link-row + [row] + (-> row + (update :flags db/decode-pgarray #{}) + (update :pages db/decode-pgarray #{}))) + +(defn- retrieve-project + [conn id] + (db/get-by-id conn :project id {:columns [:id :name :team-id]})) + +(defn- retrieve-share-link + [{:keys [conn]} file-id id] + (some-> (db/get-by-params conn :share-link + {:id id :file-id file-id} + {:check-not-found false}) + (decode-share-link-row))) + +(defn- retrieve-bundle + [{:keys [conn] :as cfg} file-id] + (let [file (files/retrieve-file cfg file-id) + project (retrieve-project conn (:project-id file)) + libs (files/retrieve-file-libraries cfg false file-id) + users (teams/retrieve-users conn (:team-id project)) + + links (->> (db/query conn :share-link {:file-id file-id}) + (mapv decode-share-link-row)) + + fonts (db/query conn :team-font-variant + {:team-id (:team-id project) + :deleted-at nil})] + {:file file + :users users + :fonts fonts + :project project + :share-links links + :libraries libs})) + +(s/def ::file-id ::us/uuid) +(s/def ::profile-id ::us/uuid) +(s/def ::share-id ::us/uuid) + +(s/def ::view-only-bundle + (s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id])) + +(sv/defmethod ::view-only-bundle {:auth false} + [{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}] + (db/with-atomic [conn pool] + (let [cfg (assoc cfg :conn conn) + bundle (retrieve-bundle cfg file-id) + slink (retrieve-share-link cfg file-id share-id)] + + ;; When we have neither profile nor share, we just return a not + ;; found response to the user. + (when (and (not profile-id) + (not slink)) + (ex/raise :type :not-found + :code :object-not-found)) + + ;; When we have only profile, we need to check read permissiones + ;; on file. + (when (and profile-id (not slink)) + (files/check-read-permissions! conn profile-id file-id)) + + (cond-> bundle + ;; If we have current profile, put + (some? profile-id) + (as-> $ (let [edit? (boolean (files/has-edit-permissions? conn profile-id file-id)) + read? (boolean (files/has-read-permissions? conn profile-id file-id))] + (-> (assoc $ :permissions {:read read? :edit edit?}) + (cond-> (not edit?) (dissoc :share-links))))) + + (some? slink) + (assoc :share slink) + + (and (some? slink) + (not (contains? (:flags slink) "view-all-pages"))) + (update-in [:file :data] (fn [data] + (let [allowed-pages (:pages slink)] + (-> data + (update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages))) + (update :pages-index (fn [index] (select-keys index allowed-pages))))))))))) + ;; --- Query: Viewer Bundle (by Page ID) +;; DEPRECATED: should be removed in 1.9.x + (declare check-shared-token!) (declare retrieve-shared-token) -(def ^:private - sql:project - "select p.id, p.name, p.team_id - from project as p - where p.id = ? - and p.deleted_at is null") - -(defn- retrieve-project - [conn id] - (db/exec-one! conn [sql:project id])) - (s/def ::id ::us/uuid) -(s/def ::file-id ::us/uuid) (s/def ::page-id ::us/uuid) (s/def ::token ::us/string) @@ -81,6 +155,3 @@ [conn file-id page-id] (let [sql "select * from file_share_token where file_id=? and page_id=?"] (db/exec-one! conn [sql file-id page-id]))) - - - diff --git a/backend/src/app/tasks/delete_object.clj b/backend/src/app/tasks/delete_object.clj index 2a30a63242..b1b3e701b1 100644 --- a/backend/src/app/tasks/delete_object.clj +++ b/backend/src/app/tasks/delete_object.clj @@ -60,7 +60,7 @@ (defmethod handle-deletion :team-font-variant [{:keys [conn storage]} {:keys [id] :as props}] - (let [font (db/get-by-id conn :team-font-variant id {:uncheked true}) + (let [font (db/get-by-id conn :team-font-variant id {:check-not-found false}) storage (assoc storage :conn conn)] (when (:deleted-at font) (db/delete! conn :team-font-variant {:id id}) diff --git a/backend/test/app/services_viewer_test.clj b/backend/test/app/services_viewer_test.clj index d3176f81ac..1b9d7c0133 100644 --- a/backend/test/app/services_viewer_test.clj +++ b/backend/test/app/services_viewer_test.clj @@ -16,18 +16,18 @@ (t/use-fixtures :each th/database-reset) (t/deftest retrieve-bundle - (let [prof (th/create-profile* 1 {:is-active true}) - prof2 (th/create-profile* 2 {:is-active true}) - team-id (:default-team-id prof) - proj-id (:default-project-id prof) + (let [prof (th/create-profile* 1 {:is-active true}) + prof2 (th/create-profile* 2 {:is-active true}) + team-id (:default-team-id prof) + proj-id (:default-project-id prof) - file (th/create-file* 1 {:profile-id (:id prof) - :project-id proj-id - :is-shared false}) - token (atom nil)] + file (th/create-file* 1 {:profile-id (:id prof) + :project-id proj-id + :is-shared false}) + share-id (atom nil)] (t/testing "authenticated with page-id" - (let [data {::th/type :viewer-bundle + (let [data {::th/type :view-only-bundle :profile-id (:id prof) :file-id (:id file) :page-id (get-in file [:data :pages 0])} @@ -38,64 +38,67 @@ (t/is (nil? (:error out))) (let [result (:result out)] - (t/is (contains? result :token)) - (t/is (contains? result :page)) + (t/is (contains? result :share-links)) + (t/is (contains? result :permissions)) + (t/is (contains? result :libraries)) (t/is (contains? result :file)) (t/is (contains? result :project))))) (t/testing "generate share token" - (let [data {::th/type :create-file-share-token + (let [data {::th/type :create-share-link :profile-id (:id prof) :file-id (:id file) - :page-id (get-in file [:data :pages 0])} + :pages #{(get-in file [:data :pages 0])} + :flags #{}} out (th/mutation! data)] ;; (th/print-result! out) (t/is (nil? (:error out))) (let [result (:result out)] - (t/is (string? (:token result))) - (reset! token (:token result))))) + (t/is (uuid? (:id result))) + (reset! share-id (:id result))))) (t/testing "not authenticated with page-id" - (let [data {::th/type :viewer-bundle + (let [data {::th/type :view-only-bundle :profile-id (:id prof2) :file-id (:id file) :page-id (get-in file [:data :pages 0])} out (th/query! data)] ;; (th/print-result! out) - (let [error (:error out) + (let [error (:error out) error-data (ex-data error)] (t/is (th/ex-info? error)) (t/is (= (:type error-data) :not-found)) (t/is (= (:code error-data) :object-not-found))))) - ;; (t/testing "authenticated with token & profile" - ;; (let [data {::sq/type :viewer-bundle - ;; :profile-id (:id prof2) - ;; :token @token - ;; :file-id (:id file) - ;; :page-id (get-in file [:data :pages 0])} - ;; out (th/try-on! (sq/handle data))] + (t/testing "authenticated with token & profile" + (let [data {::th/type :view-only-bundle + :profile-id (:id prof2) + :share-id @share-id + :file-id (:id file) + :page-id (get-in file [:data :pages 0])} + out (th/query! data)] - ;; ;; (th/print-result! out) + ;; (th/print-result! out) + (t/is (nil? (:error out))) - ;; (let [result (:result out)] - ;; (t/is (contains? result :page)) - ;; (t/is (contains? result :file)) - ;; (t/is (contains? result :project))))) + (let [result (:result out)] + (t/is (contains? result :share)) + (t/is (contains? result :file)) + (t/is (contains? result :project))))) - ;; (t/testing "authenticated with token" - ;; (let [data {::sq/type :viewer-bundle - ;; :token @token - ;; :file-id (:id file) - ;; :page-id (get-in file [:data :pages 0])} - ;; out (th/try-on! (sq/handle data))] + (t/testing "authenticated with token" + (let [data {::th/type :view-only-bundle + :share-id @share-id + :file-id (:id file) + :page-id (get-in file [:data :pages 0])} + out (th/query! data)] - ;; ;; (th/print-result! out) + ;; (th/print-result! out) + (let [result (:result out)] + (t/is (contains? result :file)) + (t/is (contains? result :share)) + (t/is (contains? result :project))))) - ;; (let [result (:result out)] - ;; (t/is (contains? result :page)) - ;; (t/is (contains? result :file)) - ;; (t/is (contains? result :project))))) )) diff --git a/backend/test/app/test_helpers.clj b/backend/test/app/test_helpers.clj index c7541a4a03..ba89991238 100644 --- a/backend/test/app/test_helpers.clj +++ b/backend/test/app/test_helpers.clj @@ -228,9 +228,12 @@ ([params] (update-file* *pool* params)) ([conn {:keys [file-id changes session-id profile-id revn] :or {session-id (uuid/next) revn 0}}] - (let [file (db/get-by-id conn :file file-id) - msgbus (:app.msgbus/msgbus *system*)] - (#'files/update-file {:conn conn :msgbus msgbus} + (let [file (db/get-by-id conn :file file-id) + msgbus (:app.msgbus/msgbus *system*) + metrics (:app.metrics/metrics *system*)] + (#'files/update-file {:conn conn + :msgbus msgbus + :metrics metrics} {:file file :revn revn :changes changes diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc new file mode 100644 index 0000000000..a5a051f7c7 --- /dev/null +++ b/common/src/app/common/flags.cljc @@ -0,0 +1,32 @@ +;; 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) UXBOX Labs SL + +(ns app.common.flags + "Flags parsing algorithm." + (:require + [cuerdas.core :as str])) + +(defn parse + [default flags] + (loop [flags (seq flags) + result default] + (let [item (first flags)] + (if (nil? item) + result + (let [sname (name item)] + (cond + (str/starts-with? sname "enable-") + (recur (rest flags) + (conj result (keyword (subs sname 7)))) + + (str/starts-with? sname "disable-") + (recur (rest flags) + (disj result (keyword (subs sname 8)))) + + :else + (recur (rest flags) result))))))) + + diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 63dc6baae7..97250b3612 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -487,6 +487,7 @@ (d/parse-double) (* (get-in modifiers [:resize-vector :x] 1)) (* (get-in modifiers [:resize-vector-2 :x] 1)) + (mth/precision 2) (str))] (attrs/merge attrs {:font-size font-size})))] (update shape :content #(txt/transform-nodes diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index 8a99ac3f3c..d97c9f05c6 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -37,6 +37,8 @@ :stroke-style :stroke-group :stroke-width :stroke-group :stroke-alignment :stroke-group + :stroke-cap-start :stroke-group + :stroke-cap-end :stroke-group :rx :radius-group :ry :radius-group :r1 :radius-group diff --git a/common/src/app/common/pages/init.cljc b/common/src/app/common/pages/init.cljc index ca42025f43..a37584c235 100644 --- a/common/src/app/common/pages/init.cljc +++ b/common/src/app/common/pages/init.cljc @@ -15,7 +15,7 @@ (def empty-page-data {:options {} - :name "Page" + :name "Page-1" :objects {root {:id root @@ -38,7 +38,7 @@ (def ^:private minimal-shapes [{:type :rect - :name "Rect" + :name "Rect-1" :fill-color default-color :fill-opacity 1 :stroke-style :none @@ -52,7 +52,7 @@ {:type :image} {:type :circle - :name "Circle" + :name "Circle-1" :fill-color default-color :fill-opacity 1 :stroke-style :none @@ -62,7 +62,7 @@ :stroke-opacity 0} {:type :path - :name "Path" + :name "Path-1" :stroke-style :solid :stroke-alignment :center :stroke-width 2 @@ -70,7 +70,7 @@ :stroke-opacity 1} {:type :frame - :name "Artboard" + :name "Artboard-1" :fill-color "#ffffff" :fill-opacity 1 :stroke-style :none @@ -80,7 +80,7 @@ :stroke-opacity 0} {:type :text - :name "Text" + :name "Text-1" :content nil} {:type :svg-raw}]) diff --git a/common/src/app/common/pages/spec.cljc b/common/src/app/common/pages/spec.cljc index 9c445ce6de..aec1120161 100644 --- a/common/src/app/common/pages/spec.cljc +++ b/common/src/app/common/pages/spec.cljc @@ -10,6 +10,7 @@ [app.common.geom.point :as gpt] [app.common.spec :as us] [app.common.uuid :as uuid] + [clojure.set :as set] [clojure.spec.alpha :as s])) ;; --- Specs @@ -254,6 +255,17 @@ (s/def :internal.shape/stroke-color-ref-id (s/nilable uuid?)) (s/def :internal.shape/stroke-opacity ::safe-number) (s/def :internal.shape/stroke-style #{:solid :dotted :dashed :mixed :none :svg}) + +(def stroke-caps-line #{:round :square}) +(def stroke-caps-marker #{:line-arrow :triangle-arrow :square-marker :circle-marker :diamond-marker}) +(def stroke-caps (set/union stroke-caps-line stroke-caps-marker)) +(s/def :internal.shape/stroke-cap-start stroke-caps) +(s/def :internal.shape/stroke-cap-end stroke-caps) + +(defn has-caps? + [shape] + (= (:type shape) :path)) + (s/def :internal.shape/stroke-width ::safe-number) (s/def :internal.shape/stroke-alignment #{:center :inner :outer}) (s/def :internal.shape/text-align #{"left" "right" "center" "justify"}) @@ -342,6 +354,8 @@ :internal.shape/stroke-style :internal.shape/stroke-width :internal.shape/stroke-alignment + :internal.shape/stroke-cap-start + :internal.shape/stroke-cap-end :internal.shape/text-align :internal.shape/transform :internal.shape/transform-inverse diff --git a/docker/devenv/Dockerfile b/docker/devenv/Dockerfile index e6deb917ca..bdfbd33411 100644 --- a/docker/devenv/Dockerfile +++ b/docker/devenv/Dockerfile @@ -3,10 +3,10 @@ LABEL maintainer="Andrey Antukh " ARG DEBIAN_FRONTEND=noninteractive -ENV NODE_VERSION=v14.17.3 \ - CLOJURE_VERSION=1.10.3.929 \ - CLJKONDO_VERSION=2021.06.18 \ - BABASHKA_VERSION=0.5.0 \ +ENV NODE_VERSION=v14.17.5 \ + CLOJURE_VERSION=1.10.3.933 \ + CLJKONDO_VERSION=2021.07.28 \ + BABASHKA_VERSION=0.5.1 \ LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 @@ -44,6 +44,7 @@ RUN set -ex; \ python \ build-essential \ imagemagick \ + ghostscript \ netpbm \ potrace \ webp \ @@ -97,6 +98,15 @@ RUN set -ex; \ ; \ rm -rf /var/lib/apt/lists/*; +RUN set -x; \ + apt-get -qq update; \ + curl -LfsSo /tmp/chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb; \ + dpkg -i /tmp/chrome.deb; \ + apt-get -fy install; \ + rm -rf /var/lib/apt/lists/*; \ + rm -rf /tmp/chrome.deb; + + RUN set -ex; \ curl -LfsSo /tmp/openjdk.tar.gz https://github.com/AdoptOpenJDK/openjdk16-binaries/releases/download/jdk-16.0.1%2B9/OpenJDK16U-jdk_x64_linux_hotspot_16.0.1_9.tar.gz; \ mkdir -p /usr/lib/jvm/openjdk16; \ diff --git a/docker/gitpod/Dockerfile b/docker/gitpod/Dockerfile index 7db973a46f..00d9069764 100644 --- a/docker/gitpod/Dockerfile +++ b/docker/gitpod/Dockerfile @@ -9,6 +9,7 @@ FROM gitpod/workspace-postgres RUN set -ex; \ brew install redis; \ brew install imagemagick; \ + brew install ghostscript; \ brew install mailhog; \ brew install openldap; \ sudo mkdir -p /var/log/nginx; \ diff --git a/docker/images/Dockerfile.exporter b/docker/images/Dockerfile.exporter index d9829f6212..6e5955aeed 100644 --- a/docker/images/Dockerfile.exporter +++ b/docker/images/Dockerfile.exporter @@ -1,11 +1,11 @@ -FROM ubuntu:20.04 +FROM debian:bullseye LABEL maintainer="Andrey Antukh " ARG DEBIAN_FRONTEND=noninteractive ENV LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 \ - NODE_VERSION=v14.16.0 + NODE_VERSION=v14.17.5 RUN set -ex; \ mkdir -p /etc/resolvconf/resolv.conf.d; \ @@ -20,6 +20,7 @@ RUN set -ex; \ apt-get -qq update; \ apt-get -qqy install \ imagemagick \ + ghostscript \ netpbm \ potrace \ gconf-service \ @@ -55,9 +56,9 @@ RUN set -ex; \ libxss1 \ libxtst6 \ fonts-liberation \ - libappindicator1 \ libnss3 \ libgbm1 \ + chromium \ ; \ rm -rf /var/lib/apt/lists/*; diff --git a/exporter/deps.edn b/exporter/deps.edn index 2d1b03b6c0..5c55ace1ec 100644 --- a/exporter/deps.edn +++ b/exporter/deps.edn @@ -4,7 +4,7 @@ binaryage/devtools {:mvn/version "RELEASE"} metosin/reitit-core {:mvn/version "0.5.13"} lambdaisland/glogi {:mvn/version "1.0.106"} - funcool/beicon {:mvn/version "2021.04.29-0"} + funcool/beicon {:mvn/version "2021.07.05-1"} } :aliases {:outdated @@ -14,7 +14,7 @@ :dev {:extra-deps - {thheller/shadow-cljs {:mvn/version "2.14.1"}}} + {thheller/shadow-cljs {:mvn/version "2.15.2"}}} :shadow-cljs {:main-opts ["-m" "shadow.cljs.devtools.cli"]} diff --git a/exporter/package.json b/exporter/package.json index a7422dbf47..52cfa25018 100644 --- a/exporter/package.json +++ b/exporter/package.json @@ -9,18 +9,18 @@ "author": "UXBOX LABS SL", "license": "SEE LICENSE IN ", "dependencies": { + "generic-pool": "^3.8.2", "inflation": "^2.0.0", - "jszip": "^3.6.0", + "jszip": "^3.7.0", "koa": "^2.13.0", - "luxon": "^1.27.0", - "puppeteer": "^10.0.0", - "puppeteer-cluster": "^0.22.0", + "luxon": "^2.0.1", + "puppeteer-core": "^10.1.0", "raw-body": "^2.4.1", "xml-js": "^1.6.11", "xregexp": "^5.0.2" }, "devDependencies": { - "shadow-cljs": "^2.14.2", + "shadow-cljs": "^2.15.2", "source-map-support": "^0.5.19" } } diff --git a/exporter/src/app/browser.cljs b/exporter/src/app/browser.cljs index 9df73c0663..35f1b9a0b7 100644 --- a/exporter/src/app/browser.cljs +++ b/exporter/src/app/browser.cljs @@ -6,8 +6,10 @@ (ns app.browser (:require - ["puppeteer-cluster" :as ppc] + ["puppeteer-core" :as pp] + ["generic-pool" :as gp] [app.common.data :as d] + [app.common.uuid :as uuid] [app.config :as cf] [lambdaisland.glogi :as log] [promesa.core :as p])) @@ -20,12 +22,6 @@ (str "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36")) -(defn exec! - [browser f] - (.execute ^js browser (fn [props] - (let [page (unchecked-get props "page")] - (f page))))) - (defn set-cookie! [page {:keys [key value domain]}] (.setCookie ^js page #js {:name key @@ -73,12 +69,14 @@ (defn pdf ([page] (pdf page nil)) - ([page {:keys [viewport omit-background? prefer-css-page-size?] + ([page {:keys [viewport omit-background? prefer-css-page-size? save-path] :or {viewport {} omit-background? true - prefer-css-page-size? true}}] + prefer-css-page-size? true + save-path nil}}] (let [viewport (d/merge default-viewport viewport)] - (.pdf ^js page #js {:width (:width viewport) + (.pdf ^js page #js {:path save-path + :width (:width viewport) :height (:height viewport) :scale (:scale viewport) :omitBackground omit-background? @@ -100,36 +98,76 @@ ;; --- BROWSER STATE -(def instance (atom nil)) +(defonce pool (atom nil)) +(defonce pool-browser-id (atom 1)) -(defn- create-browser - [concurrency strategy] - (let [strategy (case strategy - :browser (.-CONCURRENCY_BROWSER ^js ppc/Cluster) - :incognito (.-CONCURRENCY_CONTEXT ^js ppc/Cluster) - :page (.-CONCURRENCY_PAGE ^js ppc/Cluster)) - opts #js {:concurrency strategy - :maxConcurrency concurrency - :puppeteerOptions #js {:args #js ["--no-sandbox"]}}] - (.launch ^js ppc/Cluster opts))) +(def browser-pool-factory + (letfn [(create [] + (let [path (cf/get :browser-executable-path "/usr/bin/google-chrome")] + (-> (pp/launch #js {:executablePath path :args #js ["--no-sandbox"]}) + (p/then (fn [browser] + (let [id (deref pool-browser-id)] + (log/info :origin "factory" :action "create" :browser-id id) + (unchecked-set browser "__num_use" 0) + (unchecked-set browser "__id" id) + (swap! pool-browser-id inc) + browser)))))) + (destroy [obj] + (let [id (unchecked-get obj "__id")] + (log/info :origin "factory" :action "destroy" :browser-id id) + (.close ^js obj))) + (validate [obj] + (let [max-use (cf/get :browser-max-usage 10) + num-use (unchecked-get obj "__num_use") + id (unchecked-get obj "__id")] + + (log/info :origin "factory" :action "validate" :browser-id id :max-use max-use :num-use num-use :obj obj) + (if (> num-use max-use) + (p/resolved false) + (do + (unchecked-set obj "__num_use" (inc num-use)) + (p/resolved (.isConnected ^js obj))))))] + + #js {:create create + :destroy destroy + :validate validate})) (defn init [] - (let [concurrency (cf/get :browser-concurrency) - strategy (cf/get :browser-strategy)] - (-> (create-browser concurrency strategy) - (p/then #(reset! instance %)) - (p/catch (fn [error] - (log/error :msg "failed to initialize browser") - (js/console.error error)))))) + (log/info :msg "initializing browser pool") + (let [opts #js {:max (cf/get :browser-pool-max 3) + :min (cf/get :browser-pool-min 0) + :testOnBorrow true + :evictionRunIntervalMillis 30000 + :numTestsPerEvictionRun 5 + :acquireTimeoutMillis 120000 ; 2min + :idleTimeoutMillis 30000}] + (reset! pool (gp/createPool browser-pool-factory opts)) + (p/resolved nil))) (defn stop [] - (if-let [instance @instance] - (p/do! - (.idle ^js instance) - (.close ^js instance) - (log/info :msg "shutdown headless browser")) - (p/resolved nil))) + (when-let [pool (deref pool)] + (log/info :msg "finalizing browser pool") + (-> (.drain ^js pool) + (p/then (fn [] (.clear ^js pool)))))) + +(defn exec! + [f] + (letfn [(on-acquire [pool browser] + (p/let [ctx (.createIncognitoBrowserContext ^js browser) + page (.newPage ^js ctx)] + (-> (p/do! (f page)) + (p/handle + (fn [result error] + (-> (p/do! (.close ^js ctx) + (.release ^js pool browser)) + (p/handle (fn [_ _] + (if result + (p/resolved result) + (p/rejected error))))))))))] + (when-let [pool (deref pool)] + (-> (.acquire ^js pool) + (p/then (partial on-acquire pool)))))) diff --git a/exporter/src/app/http.cljs b/exporter/src/app/http.cljs index 4dca3cac41..c8d3d41686 100644 --- a/exporter/src/app/http.cljs +++ b/exporter/src/app/http.cljs @@ -8,13 +8,15 @@ (:require [app.config :as cf] [app.http.export :refer [export-handler]] + [app.http.export-frames :refer [export-frames-handler]] [app.http.impl :as impl] [lambdaisland.glogi :as log] [promesa.core :as p] [reitit.core :as r])) (def routes - [["/export" {:handler export-handler}]]) + [["/export-frames" {:handler export-frames-handler}] + ["/export" {:handler export-handler}]]) (def instance (atom nil)) diff --git a/exporter/src/app/http/export_frames.cljs b/exporter/src/app/http/export_frames.cljs new file mode 100644 index 0000000000..86339e6e1b --- /dev/null +++ b/exporter/src/app/http/export_frames.cljs @@ -0,0 +1,69 @@ +;; 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) UXBOX Labs SL + +(ns app.http.export-frames + (:require + ["path" :as path] + [app.common.exceptions :as exc :include-macros true] + [app.common.spec :as us] + [app.renderer.pdf :as rp] + [app.util.shell :as sh] + [cljs.spec.alpha :as s] + [cuerdas.core :as str] + [promesa.core :as p])) + +(s/def ::name ::us/string) +(s/def ::file-id ::us/uuid) +(s/def ::page-id ::us/uuid) +(s/def ::frame-id ::us/uuid) +(s/def ::frame-ids (s/coll-of ::frame-id :kind vector?)) + +(s/def ::handler-params + (s/keys :req-un [::file-id ::page-id ::frame-ids])) + +(defn- export-frame + [tdpath file-id page-id token frame-id spaths] + (p/let [spath (path/join tdpath (str frame-id ".pdf")) + result (rp/render {:name (str frame-id) + :suffix "" + :token token + :file-id file-id + :page-id page-id + :object-id frame-id + :scale 1 + :save-path spath})] + (cons spath spaths))) + +(defn- join-files + [tdpath file-id paths] + (let [output-path (path/join tdpath (str file-id ".pdf")) + paths-str (str/join " " paths)] + (-> (sh/run-cmd! (str "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile='" output-path "' " paths-str)) + (p/then (constantly output-path))))) + +(defn- clean-tmp-data + [tdpath data] + (p/do! + (sh/rmdir! tdpath) + data)) + +(defn export-frames-handler + [{:keys [params cookies] :as request}] + (let [{:keys [name file-id page-id frame-ids]} (us/conform ::handler-params params) + token (.get ^js cookies "auth-token")] + (p/let [tdpath (sh/create-tmpdir! "pdfexport-") + data (-> (reduce (fn [promis frame-id] + (p/then promis (partial export-frame tdpath file-id page-id token frame-id))) + (p/future []) + frame-ids) + (p/then (partial join-files tdpath file-id)) + (p/then sh/read-file) + (p/then (partial clean-tmp-data tdpath)))] + {:status 200 + :body data + :headers {"content-type" "application/pdf" + "content-length" (.-length data)}}))) + diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs index d80d53ab65..c900a08623 100644 --- a/exporter/src/app/renderer/bitmap.cljs +++ b/exporter/src/app/renderer/bitmap.cljs @@ -29,7 +29,7 @@ :value token})) (defn screenshot-object - [browser {:keys [file-id page-id object-id token scale type]}] + [{:keys [file-id page-id object-id token scale type]}] (letfn [(handle [page] (let [path (str "/render-object/" file-id "/" page-id "/" object-id) uri (-> (u/uri (cf/get :public-uri)) @@ -55,7 +55,7 @@ :png (bw/screenshot dom {:omit-background? true :type type}) :jpeg (bw/screenshot dom {:omit-background? false :type type}))))))] - (bw/exec! browser handle))) + (bw/exec! handle))) (s/def ::name ::us/string) (s/def ::suffix ::us/string) @@ -74,22 +74,16 @@ (defn render [params] (us/assert ::render-params params) - (let [browser @bw/instance] - (when-not browser - (ex/raise :type :internal - :code :browser-not-ready - :hint "browser cluster is not initialized yet")) - - (p/let [content (screenshot-object browser params)] - {:content content - :filename (or (:filename params) - (str (:name params) - (:suffix params "") - (case (:type params) - :png ".png" - :jpeg ".jpg"))) - :length (alength content) - :mime-type (case (:type params) - :png "image/png" - :jpeg "image/jpeg")}))) + (p/let [content (screenshot-object params)] + {:content content + :filename (or (:filename params) + (str (:name params) + (:suffix params "") + (case (:type params) + :png ".png" + :jpeg ".jpg"))) + :length (alength content) + :mime-type (case (:type params) + :png "image/png" + :jpeg "image/jpeg")})) diff --git a/exporter/src/app/renderer/pdf.cljs b/exporter/src/app/renderer/pdf.cljs index 56efd4e46c..7f88bc2384 100644 --- a/exporter/src/app/renderer/pdf.cljs +++ b/exporter/src/app/renderer/pdf.cljs @@ -26,7 +26,7 @@ :value token})) (defn pdf-from-object - [browser {:keys [file-id page-id object-id token scale type]}] + [{:keys [file-id page-id object-id token scale type save-path]}] (letfn [(handle [page] (let [path (str "/render-object/" file-id "/" page-id "/" object-id) uri (-> (u/uri (cf/get :public-uri)) @@ -39,12 +39,14 @@ (log/info :uri uri) (let [options {:cookie cookie}] (p/do! - (bw/configure-page! page options) - (bw/navigate! page uri) - (bw/wait-for page "#screenshot") - (bw/pdf page))))] + (bw/configure-page! page options) + (bw/navigate! page uri) + (bw/wait-for page "#screenshot") + (if save-path + (bw/pdf page {:save-path save-path}) + (bw/pdf page)))))] - (bw/exec! browser handle))) + (bw/exec! handle))) (s/def ::name ::us/string) (s/def ::suffix ::us/string) @@ -54,26 +56,21 @@ (s/def ::scale ::us/number) (s/def ::token ::us/string) (s/def ::filename ::us/string) +(s/def ::save-path ::us/string) (s/def ::render-params (s/keys :req-un [::name ::suffix ::object-id ::page-id ::scale ::token ::file-id] - :opt-un [::filename])) + :opt-un [::filename ::save-path])) (defn render [params] (us/assert ::render-params params) - (let [browser @bw/instance] - (when-not browser - (ex/raise :type :internal - :code :browser-not-ready - :hint "browser cluster is not initialized yet")) - - (p/let [content (pdf-from-object browser params)] - {:content content - :filename (or (:filename params) - (str (:name params) - (:suffix params "") - ".pdf")) - :length (alength content) - :mime-type "application/pdf"}))) + (p/let [content (pdf-from-object params)] + {:content content + :filename (or (:filename params) + (str (:name params) + (:suffix params "") + ".pdf")) + :length (alength content) + :mime-type "application/pdf"})) diff --git a/exporter/src/app/renderer/svg.cljs b/exporter/src/app/renderer/svg.cljs index 64f298ca04..93e0649f4c 100644 --- a/exporter/src/app/renderer/svg.cljs +++ b/exporter/src/app/renderer/svg.cljs @@ -114,7 +114,7 @@ (defn- render-object - [browser {:keys [page-id file-id object-id token scale suffix type]}] + [{:keys [page-id file-id object-id token scale suffix type]}] (letfn [(convert-to-ppm [pngpath] (log/trace :fn :convert-to-ppm) (let [basepath (path/dirname pngpath) @@ -279,7 +279,7 @@ rctx {:cookie cookie :uri (str uri)}] (log/info :uri (:uri rctx)) - (bw/exec! browser (partial handle rctx))))) + (bw/exec! (partial handle rctx))))) (s/def ::name ::us/string) (s/def ::suffix ::us/string) @@ -298,18 +298,11 @@ (defn render [params] (us/assert ::render-params params) - (let [browser @bw/instance] - (when-not browser - (ex/raise :type :internal - :code :browser-not-ready - :hint "browser cluster is not initialized yet")) - - - (p/let [content (render-object browser params)] - {:content content - :filename (or (:filename params) - (str (:name params) - (:suffix params "") - ".svg")) - :length (alength content) - :mime-type "image/svg+xml"}))) + (p/let [content (render-object params)] + {:content content + :filename (or (:filename params) + (str (:name params) + (:suffix params "") + ".svg")) + :length (alength content) + :mime-type "image/svg+xml"})) diff --git a/exporter/yarn.lock b/exporter/yarn.lock index e381d7f927..62e1b751bb 100644 --- a/exporter/yarn.lock +++ b/exporter/yarn.lock @@ -2,23 +2,23 @@ # yarn lockfile v1 -"@babel/runtime-corejs3@^7.12.1": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz#6bf5fbc0b961f8e3202888cb2cd0fb7a0a9a3f66" - integrity sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg== +"@babel/runtime-corejs3@^7.14.9": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.3.tgz#28754263988198f2a928c09733ade2fb4d28089d" + integrity sha512-30A3lP+sRL6ml8uhoJSs+8jwpKzbw8CqBvDc1laeptxPm5FahumJxirigcbD2qTs71Sonvj1cyZB0OKGAmxQ+A== dependencies: - core-js-pure "^3.0.0" + core-js-pure "^3.16.0" regenerator-runtime "^0.13.4" "@types/node@*": - version "15.6.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.2.tgz#c61d49f38af70da32424b5322eee21f97e627175" - integrity sha512-dxcOx8801kMo3KlU+C+/ctWrzREAH7YvoF3aoVpRdqgs+Kf7flp+PJDN/EX5bME3suDUZHsxes9hpvBmzYlWbA== + version "16.6.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50" + integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA== "@types/yauzl@^2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" - integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== dependencies: "@types/node" "*" @@ -60,11 +60,6 @@ assert@^1.1.1: object-assign "^4.1.1" util "0.10.3" -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -174,9 +169,9 @@ buffer-crc32@~0.2.3: integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-xor@^1.0.3: version "1.0.3" @@ -271,10 +266,10 @@ cookies@~0.8.0: depd "~2.0.0" keygrip "~1.1.0" -core-js-pure@^3.0.0: - version "3.13.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.13.1.tgz#5d139d346780f015f67225f45ee2362a6bed6ba1" - integrity sha512-wVlh0IAi2t1iOEh16y4u1TRk6ubd4KvLE8dlMi+3QUI6SfKphQUh7tAwihGGSQ8affxEXpVIPpOdf9kjR4v4Pw== +core-js-pure@^3.16.0: + version "3.16.2" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.2.tgz#0ef4b79cabafb251ea86eb7d139b42bd98c533e8" + integrity sha512-oxKe64UH049mJqrKkynWp6Vu0Rlm/BTXO/bJZuN2mmR3RtOFNepLlSWDd1eo16PzHpQAoNG97rLU1V/YxesJjw== core-util-is@~1.0.0: version "1.0.2" @@ -329,7 +324,14 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -debug@4, debug@4.3.1, debug@^4.1.1: +debug@4, debug@^4.1.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +debug@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -376,10 +378,10 @@ destroy@^1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -devtools-protocol@0.0.883894: - version "0.0.883894" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.883894.tgz#d403f2c75cd6d71c916aee8dde9258da988a4da9" - integrity sha512-33idhm54QJzf3Q7QofMgCvIVSd2o9H3kQPWaKT/fhoZh+digc+WSiMhbkeG3iN79WY4Hwr9G05NpbhEVrsOYAg== +devtools-protocol@0.0.901419: + version "0.0.901419" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" + integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== diffie-hellman@^5.0.0: version "5.0.3" @@ -484,6 +486,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +generic-pool@^3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.8.2.tgz#aab4f280adb522fdfbdc5e5b64d718d3683f04e9" + integrity sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg== + get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -503,6 +510,18 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + hash-base@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" @@ -618,9 +637,11 @@ inherits@2.0.3: integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= is-generator-function@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" - integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" @@ -632,10 +653,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -jszip@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9" - integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ== +jszip@^3.7.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9" + integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -712,10 +733,10 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -luxon@^1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.27.0.tgz#ae10c69113d85dab8f15f5e8390d0cbeddf4f00f" - integrity sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA== +luxon@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.0.2.tgz#11f2cd4a11655fdf92e076b5782d7ede5bcdd133" + integrity sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg== md5.js@^1.3.4: version "1.3.5" @@ -739,17 +760,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.48.0: - version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== +mime-db@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-types@^2.1.18, mime-types@~2.1.24: - version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: - mime-db "1.48.0" + mime-db "1.49.0" minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" @@ -986,20 +1007,13 @@ punycode@^1.2.4: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -puppeteer-cluster@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/puppeteer-cluster/-/puppeteer-cluster-0.22.0.tgz#4ab214671f414f15ad6a94a4b61ed0b4172e86e6" - integrity sha512-hmydtMwfVM+idFIDzS8OXetnujHGre7RY3BGL+3njy9+r8Dcu3VALkZHfuBEPf6byKssTCgzxU1BvLczifXd5w== - dependencies: - debug "^4.1.1" - -puppeteer@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-10.0.0.tgz#1b597c956103e2d989ca17f41ba4693b20a3640c" - integrity sha512-AxHvCb9IWmmP3gMW+epxdj92Gglii+6Z4sb+W+zc2hTTu10HF0yg6hGXot5O74uYkVqG3lfDRLfnRpi6WOwi5A== +puppeteer-core@^10.1.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-10.2.0.tgz#8d6606cf345fc0e421bc0612055579ea53234111" + integrity sha512-c1COxSnfynsE6Mtt+dW0t3TITjF9Ku4dnJbFMDDVhLQuMTYSpz4rkSP37qvzcSo3k02/Ac3GYWk0/ncp6DKZNA== dependencies: debug "4.3.1" - devtools-protocol "0.0.883894" + devtools-protocol "0.0.901419" extract-zip "2.0.1" https-proxy-agent "5.0.0" node-fetch "2.6.1" @@ -1074,9 +1088,9 @@ readline-sync@^1.4.7: integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== rimraf@3.0.2: version "3.0.2" @@ -1146,17 +1160,17 @@ shadow-cljs-jar@1.3.2: resolved "https://registry.yarnpkg.com/shadow-cljs-jar/-/shadow-cljs-jar-1.3.2.tgz#97273afe1747b6a2311917c1c88d9e243c81957b" integrity sha512-XmeffAZHv8z7451kzeq9oKh8fh278Ak+UIOGGrapyqrFBB773xN8vMQ3O7J7TYLnb9BUwcqadKkmgaq7q6fhZg== -shadow-cljs@^2.14.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.14.2.tgz#dba651ea124028064aea6fa9a390f257cb6eede4" - integrity sha512-ficaYfBAATzJ6OGt/GbIl393+cqLchzNkdTrM2PY4ttbsAOyBfWd39t+PZcYpCqemXjkgfBdZt9DJda7WaHJGA== +shadow-cljs@^2.15.2: + version "2.15.4" + resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.15.4.tgz#0d657fc8ab9a02d8980db5c49cb1622e8fc6fa52" + integrity sha512-xn8UsiVpOf2LTsQZLsCa910CcMCYdMRT6STAsgveOEIncC9cunGdqE7cTq69vTmIijVQmzf0A1nALidyzO3Hcw== dependencies: node-libs-browser "^2.2.1" readline-sync "^1.4.7" shadow-cljs-jar "1.3.2" source-map-support "^0.4.15" which "^1.3.1" - ws "^3.0.0" + ws "^7.4.6" source-map-support@^0.4.15: version "0.4.18" @@ -1282,11 +1296,6 @@ type-is@^1.6.16: media-typer "0.3.0" mime-types "~2.1.24" -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - unbzip2-stream@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" @@ -1354,14 +1363,10 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@^3.0.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" +ws@^7.4.6: + version "7.5.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" + integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== xml-js@^1.6.11: version "1.6.11" @@ -1371,11 +1376,11 @@ xml-js@^1.6.11: sax "^1.2.4" xregexp@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-5.0.2.tgz#798aa7757836f39cdbdeeba3daf94d75f7a9dcc1" - integrity sha512-JPNfN40YMNSDxZrahMrmtNH1QqPJp0/qNeEJM2nnOlhcBdfCCjekPYFV2OnwKxwvpEYglH1RBotbpRRaEuCG8Q== + version "5.1.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-5.1.0.tgz#c87e7ae5ffa5fdc520f898a467dcba02b0d391e9" + integrity sha512-PynwUWtXnSZr8tpQlDPMZfPTyv78EYuA4oI959ukxcQ0a9O/lvndLVKy5wpImzzA26eMxpZmnAXJYiQA13AtWA== dependencies: - "@babel/runtime-corejs3" "^7.12.1" + "@babel/runtime-corejs3" "^7.14.9" xtend@^4.0.0: version "4.0.2" diff --git a/frontend/resources/images/cap-circle-marker.svg b/frontend/resources/images/cap-circle-marker.svg new file mode 100644 index 0000000000..b41388e506 --- /dev/null +++ b/frontend/resources/images/cap-circle-marker.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-diamond-marker.svg b/frontend/resources/images/cap-diamond-marker.svg new file mode 100644 index 0000000000..0e23183406 --- /dev/null +++ b/frontend/resources/images/cap-diamond-marker.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-line-arrow.svg b/frontend/resources/images/cap-line-arrow.svg new file mode 100644 index 0000000000..0df0673ba7 --- /dev/null +++ b/frontend/resources/images/cap-line-arrow.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-round.svg b/frontend/resources/images/cap-round.svg new file mode 100644 index 0000000000..594e02575f --- /dev/null +++ b/frontend/resources/images/cap-round.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-square-marker.svg b/frontend/resources/images/cap-square-marker.svg new file mode 100644 index 0000000000..2340ce571b --- /dev/null +++ b/frontend/resources/images/cap-square-marker.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-square.svg b/frontend/resources/images/cap-square.svg new file mode 100644 index 0000000000..a2e3b6260b --- /dev/null +++ b/frontend/resources/images/cap-square.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/cap-triangle-arrow.svg b/frontend/resources/images/cap-triangle-arrow.svg new file mode 100644 index 0000000000..f294d01cf5 --- /dev/null +++ b/frontend/resources/images/cap-triangle-arrow.svg @@ -0,0 +1 @@ + diff --git a/frontend/resources/images/features/export-artboards.gif b/frontend/resources/images/features/export-artboards.gif new file mode 100644 index 0000000000..480a5213bc Binary files /dev/null and b/frontend/resources/images/features/export-artboards.gif differ diff --git a/frontend/resources/images/features/navigate-history.gif b/frontend/resources/images/features/navigate-history.gif new file mode 100644 index 0000000000..53c08b4552 Binary files /dev/null and b/frontend/resources/images/features/navigate-history.gif differ diff --git a/frontend/resources/images/features/share-viewer.gif b/frontend/resources/images/features/share-viewer.gif new file mode 100644 index 0000000000..487099cbcb Binary files /dev/null and b/frontend/resources/images/features/share-viewer.gif differ diff --git a/frontend/resources/images/features/stroke-caps.gif b/frontend/resources/images/features/stroke-caps.gif new file mode 100644 index 0000000000..7b823bf8c1 Binary files /dev/null and b/frontend/resources/images/features/stroke-caps.gif differ diff --git a/frontend/resources/images/icons/switch.svg b/frontend/resources/images/icons/switch.svg new file mode 100644 index 0000000000..d6bf0ab85e --- /dev/null +++ b/frontend/resources/images/icons/switch.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/styles/common/framework.scss b/frontend/resources/styles/common/framework.scss index f74c58e25b..3f542ceefe 100644 --- a/frontend/resources/styles/common/framework.scss +++ b/frontend/resources/styles/common/framework.scss @@ -131,6 +131,24 @@ } } +.btn-text-dark { + @extend %btn; + background: $color-gray-60; + color: $color-gray-20; + + svg { + fill: $color-gray-20; + } + &:hover { + background: $color-primary; + color: $color-gray-60; + svg { + fill: $color-gray-60; + } + } +} + + .btn-gray { @extend %btn; background: $color-gray-30; @@ -588,7 +606,6 @@ input.element-name { box-sizing: border-box; flex-shrink: 0; } - } &.column { @@ -975,6 +992,14 @@ input[type=range]:focus::-ms-fill-upper { } } + &.tooltip-expand { + &:hover { + &::after { + min-width: 100%; + } + } + } + &.tooltip-bottom-left { &:hover { &::after { @@ -1130,7 +1155,7 @@ input[type=range]:focus::-ms-fill-upper { padding-left: 16px; top: 16px; right: 16px; - z-index: 13; + z-index: 1005; display: flex; align-items: center; diff --git a/frontend/resources/styles/main-default.scss b/frontend/resources/styles/main-default.scss index 27c7c36102..5801d9c0b4 100644 --- a/frontend/resources/styles/main-default.scss +++ b/frontend/resources/styles/main-default.scss @@ -88,3 +88,4 @@ @import "main/partials/color-bullet"; @import "main/partials/handoff"; @import "main/partials/exception-page"; +@import "main/partials/share-link"; diff --git a/frontend/resources/styles/main/partials/dropdown.scss b/frontend/resources/styles/main/partials/dropdown.scss index 0802e04d2e..27500670a5 100644 --- a/frontend/resources/styles/main/partials/dropdown.scss +++ b/frontend/resources/styles/main/partials/dropdown.scss @@ -53,8 +53,8 @@ .icon { display: flex; align-items: center; - width: 25px; - height: 25px; + width: 20px; + height: 20px; margin-right: 7px; } } diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index b6e34844ee..0522cb6cdc 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -154,6 +154,10 @@ .modal-footer .action-buttons { justify-content: space-around; } + + .fields-container { + margin-top: 1rem; + } } .confirm-dialog { @@ -807,7 +811,7 @@ border-top-left-radius: 5px; border-bottom-left-radius: 5px; height: 100%; - width: 106%; + width: 115%; } } } diff --git a/frontend/resources/styles/main/partials/share-link.scss b/frontend/resources/styles/main/partials/share-link.scss new file mode 100644 index 0000000000..af0dd9a110 --- /dev/null +++ b/frontend/resources/styles/main/partials/share-link.scss @@ -0,0 +1,141 @@ +.share-link-dialog { + width: 475px; + background-color: $color-white; + + .modal-footer { + display: flex; + align-items: center; + justify-content: flex-end; + height: unset; + padding: 16px 26px; + + .btn-primary, + .btn-secondary, + .btn-warning { + width: 126px; + margin-bottom: 0px; + + &:not(:last-child) { + margin-right: 10px; + } + } + + .confirm-dialog { + display: flex; + flex-direction: column; + background-color: unset; + + .description { + font-size: $fs14; + + margin-bottom: 16px; + + } + .actions { + display: flex; + justify-content: flex-end; + } + } + } + + + .modal-content { + padding: 26px; + + &:first-child { + border-top: 0px; + } + + .title { + display: flex; + justify-content: space-between; + + h2 { + font-size: $fs18; + color: $color-black; + } + + .modal-close-button { + margin-right: 0px; + } + } + + + .share-link-section { + margin-top: 12px; + label { + font-size: $fs11; + color: $color-black; + } + + .hint { + padding-top: 10px; + font-size: $fs14; + color: $color-gray-40; + } + + .help-icon { + cursor: pointer; + } + } + + .view-mode, + .access-mode { + display: flex; + flex-direction: column; + + .title { + color: $color-black; + font-weight: 400; + } + + .items { + padding-left: 20px; + display: flex; + + > .input-checkbox, > .input-radio { + display: flex; + user-select: none; + + /* input { */ + /* appearance: checkbox; */ + /* } */ + + label { + display: flex; + align-items: center; + color: $color-black; + + .hint { + margin-left: 5px; + color: $color-gray-30; + } + } + + &.disabled { + label { + color: $color-gray-30; + } + } + } + } + } + + .pages-selection { + padding-left: 20px; + max-height: 200px; + overflow-y: scroll; + user-select: none; + + label { + color: $color-black; + } + } + + .custom-input { + input { + padding: 0 40px 0 15px; + } + } + } +} diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 72faa15efd..0380d9303f 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -1316,7 +1316,7 @@ &::after { content: ' '; - background-color: $color-gray-20; + background-color: $color-gray-30; } &.active, @@ -1436,5 +1436,57 @@ } } } - +} + +.cap-select { + background-color: transparent; + border: 1px solid transparent; + border-bottom-color: $color-gray-40; + color: $color-gray-10; + cursor: pointer; + font-size: $fs11; + margin: $x-small; + overflow: hidden; + padding: $x-small; + padding-right: 20px; + position: relative; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + + & .cap-select-button { + svg { + fill: $color-gray-10; + height: 11px; + position: absolute; + right: 5px; + top: 6px; + width: 11px; + } + } + + &:hover { + border-color: $color-gray-40; + } + + &:focus { + border-color: $color-primary; + } +} + +.cap-select-dropdown { + right: 5px; + top: 30px; + z-index: 12; + min-width: 200px; + position: fixed; + + & li.separator { + border-top: 1px solid $color-gray-10; + } + + & li img { + width: 16px; + margin-right: $small; + } } diff --git a/frontend/resources/styles/main/partials/viewer-header.scss b/frontend/resources/styles/main/partials/viewer-header.scss index 605a08ec3a..24b9d802be 100644 --- a/frontend/resources/styles/main/partials/viewer-header.scss +++ b/frontend/resources/styles/main/partials/viewer-header.scss @@ -42,56 +42,64 @@ } } - .view-options { - .icon { - align-items: center; - cursor: pointer; - display: flex; - justify-content: center; + .options-zone { + align-items: center; + display: flex; + // width: 384px; + justify-content: flex-end; + position: relative; - svg { - fill: $color-gray-30; - height: 30px; - width: 28px; - } + > * { + margin-left: $big; + } - &:hover { - > svg { - fill: $color-primary; - } + .btn-primary { + flex-shrink: 0; + } + + .zoom-widget { + .dropdown { + top: 45px; + left: 25px; } } - .dropdown { - min-width: 260px; - left: 0px; - top: 40px; - } - - .view-options-dropdown { + .view-options { align-items: center; cursor: pointer; display: flex; + width: 90px; - span { + > span { color: $color-gray-10; font-size: $fs13; margin-right: $x-small; } - svg { - fill: $color-gray-10; - height: 12px; - width: 12px; - } - } - } + > .icon { + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; - .file-menu { - .dropdown { - min-width: 100px; - right: 0px; - top: 40px; + svg { + fill: $color-gray-10; + height: 12px; + width: 12px; + } + + &:hover { + > svg { + fill: $color-primary; + } + } + } + + .dropdown { + min-width: 260px; + top: 45px; + left: -25px; + } } } @@ -100,39 +108,50 @@ cursor: pointer; display: flex; padding: $x-small; + position: relative; - svg { - fill: $color-gray-20; - height: 20px; - margin-right: $small; - width: 20px; - } + .icon { + display: flex; + justify-content: center; + align-items: center; - span { - color: $color-gray-20; - margin-right: $x-small; - font-size: $fs14; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - &.frame-name { - color: $color-white; + svg { + fill: $color-gray-20; + height: 12px; + margin-right: $small; + width: 12px; } } - .show-thumbnails-button svg { - fill: $color-white; - height: 10px; - width: 10px; + .breadcrumb, .current-frame { + display: flex; + position: relative; + + > span { + color: $color-gray-20; + margin-right: $x-small; + font-size: $fs14; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + > .dropdown { + top: 45px; + right: 10px; + } } - .page-name { - color: $color-white; - } + .current-frame { + display: flex; + span { + color: $color-white; + margin-right: $x-small; + } - .counters { - margin-left: $size-3; + .counters { + color: $color-gray-20; + } } } @@ -166,133 +185,6 @@ } } - .options-zone { - align-items: center; - display: flex; - width: 384px; - justify-content: flex-end; - position: relative; - - > * { - margin-left: $big; - } - - .btn-share { - display: flex; - align-items: center; - justify-content: center; - width: 25px; - height: 25px; - cursor: pointer; - - svg { - fill: $color-gray-20; - width: 20px; - height: 20px; - } - } - - .btn-primary { - flex-shrink: 0; - } - } - - .share-link-dropdown { - background-color: $color-white; - border-radius: $br-small; - box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); - display: flex; - flex-direction: column; - left: -135px; - position: absolute; - padding: 1rem; - top: 45px; - width: 400px; - - .share-link-title { - color: $color-black; - font-size: $fs15; - padding-bottom: 1rem; - } - - .share-link-subtitle { - color: $color-gray-40; - padding-bottom: 1rem; - } - - .share-link-buttons { - display: flex; - justify-content: center; - align-items: center; - - .btn-warning, - .btn-primary { - width: 50%; - } - - } - - .share-link-input { - border: 1px solid $color-gray-20; - border-radius: 3px; - display: flex; - height: 40px; - justify-content: space-between; - margin-bottom: 1rem; - padding: 9px $small; - overflow: hidden; - - .link { - &:before { - content: ''; - position: absolute; - width: 50%; - background: linear-gradient(45deg, transparent, #ffffff); - height: 100%; - top: 0; - left: 0; - pointer-events: none; - margin-left: 50%; - } - overflow: hidden; - white-space: nowrap; - position: relative; - color: $color-gray-50; - line-height: 1.5; - user-select: all; - overflow: hidden; - } - - .link-button { - color: $color-primary-dark; - cursor: pointer; - flex-shrink: 0; - font-size: $fs15; - - &:hover { - color: $color-black; - } - } - } - - &:before { - background-color: $color-white; - content: ""; - height: 16px; - left: 53%; - position: absolute; - transform: rotate(45deg); - top: -5px; - width: 16px; - } - - } - - .zoom-dropdown { - left: 180px; - top: 40px; - } - .users-zone { align-items: center; cursor: pointer; diff --git a/frontend/resources/styles/main/partials/viewer-thumbnails.scss b/frontend/resources/styles/main/partials/viewer-thumbnails.scss index d630c8bfc3..b60a92847f 100644 --- a/frontend/resources/styles/main/partials/viewer-thumbnails.scss +++ b/frontend/resources/styles/main/partials/viewer-thumbnails.scss @@ -1,4 +1,3 @@ - .viewer-thumbnails { grid-row: 1 / span 1; grid-column: 1 / span 1; @@ -9,6 +8,11 @@ flex-direction: column; z-index: 12; + &.invisible { + visibility: hidden; + pointer-events: none; + } + &.expanded { grid-row: 1 / span 2; @@ -159,7 +163,7 @@ &:hover { border-color: $color-primary; - border-width: 2px; + outline: 2px solid $color-primary; } } diff --git a/frontend/resources/styles/main/partials/zoom-widget.scss b/frontend/resources/styles/main/partials/zoom-widget.scss index 4e68d88072..b251923214 100644 --- a/frontend/resources/styles/main/partials/zoom-widget.scss +++ b/frontend/resources/styles/main/partials/zoom-widget.scss @@ -8,13 +8,13 @@ margin-left: $x-small; } - .dropdown-button svg { + .icon svg { fill: $color-gray-10; height: 10px; width: 10px; } - .zoom-dropdown { + .dropdown { position: absolute; z-index: 12; width: 210px; diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs index 0cc0a34273..24346505de 100644 --- a/frontend/src/app/config.cljs +++ b/frontend/src/app/config.cljs @@ -6,6 +6,7 @@ (ns app.config (:require + [app.common.flags :as flags] [app.common.spec :as us] [app.common.uri :as u] [app.common.version :as v] @@ -53,10 +54,14 @@ :browser :webworker)) +(def default-flags + #{:registration :demo-users}) + (defn- parse-flags [global] - (let [flags (obj/get global "penpotFlags" "")] - (into #{} (map keyword) (str/words flags)))) + (let [flags (obj/get global "penpotFlags" "") + flags (into #{} (map keyword) (str/words flags))] + (flags/parse default-flags flags))) (defn- parse-version [global] @@ -68,26 +73,27 @@ (def default-theme "default") (def default-language "en") -(def demo-warning (obj/get global "penpotDemoWarning" false)) -(def feedback-enabled (obj/get global "penpotFeedbackEnabled" false)) -(def allow-demo-users (obj/get global "penpotAllowDemoUsers" true)) (def google-client-id (obj/get global "penpotGoogleClientID" nil)) (def gitlab-client-id (obj/get global "penpotGitlabClientID" nil)) (def github-client-id (obj/get global "penpotGithubClientID" nil)) (def oidc-client-id (obj/get global "penpotOIDCClientID" nil)) -(def login-with-ldap (obj/get global "penpotLoginWithLDAP" false)) -(def registration-enabled (obj/get global "penpotRegistrationEnabled" true)) (def worker-uri (obj/get global "penpotWorkerURI" "/js/worker.js")) (def translations (obj/get global "penpotTranslations")) (def themes (obj/get global "penpotThemes")) -(def analytics (obj/get global "penpotAnalyticsEnabled" false)) -(def flags (delay (parse-flags global))) +(def flags (atom (parse-flags global))) +(def version (atom (parse-version global))) +(def target (atom (parse-target global))) +(def browser (atom (parse-browser))) +(def platform (atom (parse-platform))) -(def version (delay (parse-version global))) -(def target (delay (parse-target global))) -(def browser (delay (parse-browser))) -(def platform (delay (parse-platform))) +;; mantain for backward compatibility +(let [login-with-ldap (obj/get global "penpotLoginWithLDAP" false) + registration (obj/get global "penpotRegistrationEnabled" true)] + (when login-with-ldap + (swap! flags conj :login-with-ldap)) + (when (false? registration) + (swap! flags disj :registration))) (def public-uri (let [uri (u/uri (or (obj/get global "penpotPublicURI") diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index ce862ff133..9a6842730a 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -42,10 +42,13 @@ (if-let [conform (get-in match [:data :conform])] (let [spath (get conform :path-params ::any) squery (get conform :query-params ::any)] - (-> (dissoc match :params) - (assoc :path-params (us/conform spath (get match :path-params)) - :query-params (us/conform squery (get match :query-params))))) - match))) + (try + (-> (dissoc match :params) + (assoc :path-params (us/conform spath (get match :path-params)) + :query-params (us/conform squery (get match :query-params)))) + (catch :default _ + nil))) + match))) (defn on-navigate [router path] diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index 5250b7202d..95387f5ecc 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -72,7 +72,7 @@ (update :workspace-drawing dissoc :comment) (update-in [:comments id] assoc (:id comment) comment)))] - (ptk/reify ::create-thread + (ptk/reify ::create-comment-thread ptk/WatchEvent (watch [_ _ _] (->> (rp/mutation :create-comment-thread params) @@ -94,6 +94,8 @@ [{:keys [id is-resolved] :as thread}] (us/assert ::comment-thread thread) (ptk/reify ::update-comment-thread + IDeref + (-deref [_] {:is-resolved is-resolved}) ptk/UpdateEvent (update [_ state] @@ -122,7 +124,7 @@ (defn update-comment [{:keys [id content thread-id] :as comment}] (us/assert ::comment comment) - (ptk/reify :update-comment + (ptk/reify ::update-comment ptk/UpdateEvent (update [_ state] (d/update-in-when state [:comments thread-id id] assoc :content content)) @@ -135,7 +137,7 @@ (defn delete-comment-thread [{:keys [id] :as thread}] (us/assert ::comment-thread thread) - (ptk/reify :delete-comment-thread + (ptk/reify ::delete-comment-thread ptk/UpdateEvent (update [_ state] (-> state @@ -150,7 +152,7 @@ (defn delete-comment [{:keys [id thread-id] :as comment}] (us/assert ::comment comment) - (ptk/reify :delete-comment + (ptk/reify ::delete-comment ptk/UpdateEvent (update [_ state] (d/update-in-when state [:comments thread-id] dissoc id)) @@ -212,7 +214,7 @@ (defn open-thread [{:keys [id] :as thread}] (us/assert ::comment-thread thread) - (ptk/reify ::open-thread + (ptk/reify ::open-comment-thread ptk/UpdateEvent (update [_ state] (-> state @@ -221,7 +223,7 @@ (defn close-thread [] - (ptk/reify ::close-thread + (ptk/reify ::close-comment-thread ptk/UpdateEvent (update [_ state] (-> state diff --git a/frontend/src/app/main/data/common.cljs b/frontend/src/app/main/data/common.cljs new file mode 100644 index 0000000000..f4a3026501 --- /dev/null +++ b/frontend/src/app/main/data/common.cljs @@ -0,0 +1,46 @@ +;; 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) UXBOX Labs SL + +(ns app.main.data.common + "A general purpose events." + (:require + [app.main.repo :as rp] + [beicon.core :as rx] + [potok.core :as ptk])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; SHARE LINK +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn share-link-created + [link] + (ptk/reify ::share-link-created + ptk/UpdateEvent + (update [_ state] + (update state :share-links (fnil conj []) link)))) + +(defn create-share-link + [params] + (ptk/reify ::create-share-link + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/mutation! :create-share-link params) + (rx/map share-link-created))))) + +(defn delete-share-link + [{:keys [id] :as link}] + (ptk/reify ::delete-share-link + ptk/UpdateEvent + (update [_ state] + (update state :share-links + (fn [links] + (filterv #(not= id (:id %)) links)))) + + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/mutation! :delete-share-link {:id id}) + (rx/ignore))))) + diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index 3879896a48..6df1557ad6 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.spec :as us] [app.common.uuid :as uuid] + [app.main.data.events :as ev] [app.main.data.fonts :as df] [app.main.data.media :as di] [app.main.data.users :as du] @@ -386,6 +387,9 @@ (us/assert ::us/email email) (us/assert ::us/keyword role) (ptk/reify ::invite-team-member + IDeref + (-deref [_] {:role role}) + ptk/WatchEvent (watch [_ state _] (let [{:keys [on-success on-error] @@ -475,6 +479,10 @@ (us/assert ::us/uuid id) (us/assert ::us/uuid team-id) (ptk/reify ::move-project + IDeref + (-deref [_] + {:id id :team-id team-id}) + ptk/WatchEvent (watch [_ _ _] (let [{:keys [on-success on-error] @@ -566,6 +574,10 @@ [{:keys [id name] :as params}] (us/assert ::file params) (ptk/reify ::rename-file + IDeref + (-deref [_] + {::ev/origin "dashboard" :id id :name name}) + ptk/UpdateEvent (update [_ state] (-> state @@ -585,6 +597,10 @@ [{:keys [id is-shared] :as params}] (us/assert ::file params) (ptk/reify ::set-file-shared + IDeref + (-deref [_] + {::ev/origin "dashboard" :id id :shared is-shared}) + ptk/UpdateEvent (update [_ state] (-> state @@ -663,12 +679,16 @@ (us/assert ::set-of-uuid ids) (us/assert ::us/uuid project-id) (ptk/reify ::move-files + IDeref + (-deref [_] + {:num-files (count ids) + :project-id project-id}) + ptk/WatchEvent (watch [_ _ _] (let [{:keys [on-success on-error] :or {on-success identity on-error rx/throw}} (meta params)] - (->> (rp/mutation! :move-files {:ids ids :project-id project-id}) (rx/tap on-success) (rx/catch on-error)))))) @@ -690,14 +710,14 @@ (defn go-to-files ([project-id] - (ptk/reify ::go-to-files + (ptk/reify ::go-to-files-1 ptk/WatchEvent (watch [_ state _] (let [team-id (:current-team-id state)] (rx/of (rt/nav :dashboard-files {:team-id team-id :project-id project-id})))))) ([team-id project-id] - (ptk/reify ::go-to-files + (ptk/reify ::go-to-files-2 ptk/WatchEvent (watch [_ _ _] (rx/of (rt/nav :dashboard-files {:team-id team-id @@ -719,13 +739,13 @@ (defn go-to-projects ([] - (ptk/reify ::go-to-projects + (ptk/reify ::go-to-projects-0 ptk/WatchEvent (watch [_ state _] (let [team-id (:current-team-id state)] (rx/of (rt/nav :dashboard-projects {:team-id team-id})))))) ([team-id] - (ptk/reify ::go-to-projects + (ptk/reify ::go-to-projects-1 ptk/WatchEvent (watch [_ _ _] (du/set-current-team! team-id) diff --git a/frontend/src/app/main/data/events.cljs b/frontend/src/app/main/data/events.cljs index 83c2a835f5..b51b34ebd0 100644 --- a/frontend/src/app/main/data/events.cljs +++ b/frontend/src/app/main/data/events.cljs @@ -71,18 +71,84 @@ ;; --- EVENT TRANSLATION -(defmulti ^:private process-event ptk/type) +(derive :app.main.data.comments/create-comment ::generic-action) +(derive :app.main.data.comments/create-comment-thread ::generic-action) +(derive :app.main.data.comments/delete-comment ::generic-action) +(derive :app.main.data.comments/delete-comment-thread ::generic-action) +(derive :app.main.data.comments/open-comment-thread ::generic-action) +(derive :app.main.data.comments/update-comment ::generic-action) +(derive :app.main.data.comments/update-comment-thread ::generic-action) +(derive :app.main.data.comments/update-comment-thread-status ::generic-action) +(derive :app.main.data.dashboard/delete-team-member ::generic-action) +(derive :app.main.data.dashboard/duplicate-project ::generic-action) +(derive :app.main.data.dashboard/file-created ::generic-action) +(derive :app.main.data.dashboard/invite-team-member ::generic-action) +(derive :app.main.data.dashboard/leave-team ::generic-action) +(derive :app.main.data.dashboard/move-files ::generic-action) +(derive :app.main.data.dashboard/move-project ::generic-action) +(derive :app.main.data.dashboard/project-created ::generic-action) +(derive :app.main.data.dashboard/rename-file ::generic-action) +(derive :app.main.data.dashboard/set-file-shared ::generic-action) +(derive :app.main.data.dashboard/update-team-member-role ::generic-action) +(derive :app.main.data.dashboard/update-team-photo ::generic-action) +(derive :app.main.data.fonts/add-font ::generic-action) +(derive :app.main.data.fonts/delete-font ::generic-action) +(derive :app.main.data.fonts/delete-font-variant ::generic-action) +(derive :app.main.data.users/logout ::generic-action) +(derive :app.main.data.users/request-email-change ::generic-action) +(derive :app.main.data.users/update-password ::generic-action) +(derive :app.main.data.users/update-photo ::generic-action) +(derive :app.main.data.workspace.comments/open-comment-thread ::generic-action) +(derive :app.main.data.workspace.libraries/add-color ::generic-action) +(derive :app.main.data.workspace.libraries/add-media ::generic-action) +(derive :app.main.data.workspace.libraries/add-typography ::generic-action) +(derive :app.main.data.workspace.libraries/delete-color ::generic-action) +(derive :app.main.data.workspace.libraries/delete-media ::generic-action) +(derive :app.main.data.workspace.libraries/delete-typography ::generic-action) +(derive :app.main.data.workspace.persistence/attach-library ::generic-action) +(derive :app.main.data.workspace.persistence/detach-library ::generic-action) +(derive :app.main.data.workspace.persistence/set-file-shard ::generic-action) +(derive :app.main.data.workspace/create-page ::generic-action) +(derive :app.main.data.workspace/set-workspace-layout ::generic-action) + + +(defmulti process-event ptk/type) (defmethod process-event :default [_] nil) (defmethod process-event ::event [event] - (let [data (deref event)] + (let [data (deref event) + origin (::origin data)] (when (::name data) (d/without-nils {:type (::type data "action") :name (::name data) :context (::context data) - :props (dissoc data ::name ::type ::context)})))) + :props (-> data + (dissoc ::name) + (dissoc ::type) + (dissoc ::origin) + (dissoc ::context) + (cond-> origin (assoc :origin origin)))})))) + +(defmethod process-event ::generic-action + [event] + (let [type (ptk/type event) + mdata (meta event) + data (if (satisfies? IDeref event) + (deref event) + {}) + + name (or (::name mdata) + (name type))] + + {:type "action" + :name (name type) + :props (merge data (d/without-nils (::props mdata))) + :context (d/without-nils + {:event-origin (::origin mdata) + :event-namespace (namespace type) + :event-symbol (name type)})})) (defmethod process-event :app.util.router/navigated [event] @@ -113,42 +179,6 @@ :profile-id (:id data) :props (d/without-nils props)})) -(defmethod process-event :app.main.data.dashboard/project-created - [event] - (let [data (deref event)] - {:type "action" - :name "create-project" - :props {:id (:id data) - :team-id (:team-id data)}})) - -(defmethod process-event :app.main.data.dashboard/file-created - [event] - (let [data (deref event)] - {:type "action" - :name "create-file" - :props {:id (:id data) - :project-id (:project-id data)}})) - -(defmethod process-event :app.main.data.workspace/create-page - [event] - (let [data (deref event)] - {:type "action" - :name "create-page" - :props {:id (:id data) - :file-id (:file-id data) - :project-id (:project-id data)}})) - -(defn- event->generic-action - [_ name] - {:type "action" - :name name - :props {}}) - -(defmethod process-event :app.main.data.users/logout - [event] - (event->generic-action event "signout")) - - ;; --- MAIN LOOP (defn- append-to-buffer @@ -164,7 +194,7 @@ (defn- persist-events [events] (if (seq events) - (let [uri (u/join cf/public-uri "events") + (let [uri (u/join cf/public-uri "api/audit/events") params {:events events}] (->> (http/send! {:uri uri :method :post @@ -203,8 +233,7 @@ ptk/EffectEvent (effect [_ _ stream] - (let [events (methods process-event) - session (atom nil) + (let [session (atom nil) profile (->> (rx/from-atom storage {:emit-current-value? true}) (rx/map :profile) @@ -215,12 +244,9 @@ (rx/with-latest-from profile) (rx/map (fn [result] (let [event (aget result 0) - profile-id (aget result 1) - type (ptk/type event) - impl-fn (get events type)] - (when (fn? impl-fn) - (some-> (impl-fn event) - (update :profile-id #(or % profile-id))))))) + profile-id (aget result 1)] + (some-> (process-event event) + (update :profile-id #(or % profile-id)))))) (rx/filter :profile-id) (rx/map (fn [event] (let [session* (or @session (dt/now)) @@ -242,6 +268,6 @@ (defmethod ptk/resolve ::initialize [_ params] - (if cf/analytics + (if (contains? @cf/flags :audit-log) (initialize) (ptk/data-event ::initialize params))) diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index a8ecf71270..81d00c9e50 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -187,6 +187,9 @@ (defn add-font [font] (ptk/reify ::add-font + IDeref + (-deref [_] (select-keys font [:font-family :font-style :font-weight])) + ptk/UpdateEvent (update [_ state] (update state :dashboard-fonts assoc (:id font) font)))) diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index b1fc5b7964..07ad91268b 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -24,6 +24,10 @@ ;; --- COMMON SPECS +(defn is-authenticated? + [{:keys [id]}] + (and (uuid? id) (not= id uuid/zero))) + (s/def ::id ::us/uuid) (s/def ::fullname ::us/string) (s/def ::email ::us/email) diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index c6dc46a37d..c30ed5bf28 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -14,24 +14,12 @@ [app.main.data.comments :as dcm] [app.main.data.fonts :as df] [app.main.repo :as rp] + [app.util.globals :as ug] [app.util.router :as rt] [beicon.core :as rx] [cljs.spec.alpha :as s] [potok.core :as ptk])) -;; --- General Specs - -(s/def ::id ::us/uuid) -(s/def ::name ::us/string) - -(s/def ::project (s/keys :req-un [::id ::name])) -(s/def ::file (s/keys :req-un [::id ::name])) -(s/def ::page ::cp/page) - -(s/def ::bundle - (s/keys :req-un [::project ::file ::page])) - - ;; --- Local State Initialization (def ^:private @@ -49,25 +37,24 @@ (declare fetch-bundle) (declare bundle-fetched) -(s/def ::page-id ::us/uuid) (s/def ::file-id ::us/uuid) (s/def ::index ::us/integer) -(s/def ::token (s/nilable ::us/string)) +(s/def ::page-id (s/nilable ::us/uuid)) +(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::section ::us/string) (s/def ::initialize-params - (s/keys :req-un [::page-id ::file-id] - :opt-un [::token])) + (s/keys :req-un [::file-id] + :opt-un [::share-id ::page-id])) (defn initialize - [{:keys [page-id file-id] :as params}] + [{:keys [file-id] :as params}] (us/assert ::initialize-params params) (ptk/reify ::initialize ptk/UpdateEvent (update [_ state] (-> state (assoc :current-file-id file-id) - (assoc :current-page-id page-id) (update :viewer-local (fn [lstate] (if (nil? lstate) @@ -77,55 +64,72 @@ ptk/WatchEvent (watch [_ _ _] (rx/of (fetch-bundle params) - (fetch-comment-threads params))))) + (fetch-comment-threads params))) -;; --- Data Fetching + ptk/EffectEvent + (effect [_ _ _] + ;; Set the window name, the window name is used on inter-tab + ;; navigation; in other words: when a user opens a tab with a + ;; name, if there are already opened tab with that name, the + ;; browser just focus the opened tab instead of creating new + ;; tab. + (let [name (str "viewer-" file-id)] + (unchecked-set ug/global "name" name))))) -(s/def ::fetch-bundle-params - (s/keys :req-un [::page-id ::file-id] - :opt-un [::token])) +(defn finalize + [_] + (ptk/reify ::finalize + ptk/UpdateEvent + (update [_ state] + (dissoc state :viewer)))) -(defn fetch-bundle - [{:keys [page-id file-id token] :as params}] - (us/assert ::fetch-bundle-params params) - (ptk/reify ::fetch-file - ptk/WatchEvent - (watch [_ _ _] - (let [params (cond-> {:page-id page-id - :file-id file-id} - (string? token) (assoc :token token))] - (->> (rp/query :viewer-bundle params) - (rx/mapcat - (fn [{:keys [fonts] :as bundle}] - (rx/of (df/fonts-fetched fonts) - (bundle-fetched bundle))))))))) - -(defn- extract-frames - [objects] +(defn select-frames + [{:keys [objects] :as page}] (let [root (get objects uuid/zero)] (into [] (comp (map #(get objects %)) (filter #(= :frame (:type %)))) (reverse (:shapes root))))) +;; --- Data Fetching + +(s/def ::fetch-bundle-params + (s/keys :req-un [::page-id ::file-id] + :opt-un [::share-id])) + +(defn fetch-bundle + [{:keys [file-id share-id] :as params}] + (us/assert ::fetch-bundle-params params) + (ptk/reify ::fetch-file + ptk/WatchEvent + (watch [_ _ _] + (let [params' (cond-> {:file-id file-id} + (uuid? share-id) (assoc :share-id share-id))] + (->> (rp/query :view-only-bundle params') + (rx/mapcat + (fn [{:keys [fonts] :as bundle}] + (rx/of (df/fonts-fetched fonts) + (bundle-fetched (merge bundle params)))))))))) + + (defn bundle-fetched - [{:keys [project file page share-token token libraries users] :as bundle}] - (us/verify ::bundle bundle) - (ptk/reify ::bundle-fetched - ptk/UpdateEvent - (update [_ state] - (let [objects (:objects page) - frames (extract-frames objects)] + [{:keys [project file share-links libraries users permissions] :as bundle}] + (let [pages (->> (get-in file [:data :pages]) + (map (fn [page-id] + (let [data (get-in file [:data :pages-index page-id])] + [page-id (assoc data :frames (select-frames data))]))) + (into {}))] + + (ptk/reify ::bundle-fetched + ptk/UpdateEvent + (update [_ state] (-> state - (assoc :viewer-libraries (d/index-by :id libraries)) - (update :viewer-data assoc - :project project - :objects objects - :users (d/index-by :id users) - :file file - :page page - :frames frames - :token token - :share-token share-token)))))) + (assoc :share-links share-links) + (assoc :viewer {:libraries (d/index-by :id libraries) + :users (d/index-by :id users) + :permissions permissions + :project project + :pages pages + :file file})))))) (defn fetch-comment-threads [{:keys [file-id page-id] :as params}] @@ -168,32 +172,6 @@ (->> (rp/query :comments {:thread-id thread-id}) (rx/map #(partial fetched %))))))) -(defn create-share-link - [] - (ptk/reify ::create-share-link - ptk/WatchEvent - (watch [_ state _] - (let [file-id (:current-file-id state) - page-id (:current-page-id state)] - (->> (rp/mutation! :create-file-share-token {:file-id file-id - :page-id page-id}) - (rx/map (fn [{:keys [token]}] - #(assoc-in % [:viewer-data :token] token)))))))) - -(defn delete-share-link - [] - (ptk/reify ::delete-share-link - ptk/WatchEvent - (watch [_ state _] - (let [file-id (:current-file-id state) - page-id (:current-page-id state) - token (get-in state [:viewer-data :token]) - params {:file-id file-id - :page-id page-id - :token token}] - (->> (rp/mutation :delete-file-share-token params) - (rx/map (fn [_] #(update % :viewer-data dissoc :token)))))))) - ;; --- Zoom Management (def increase-zoom @@ -245,29 +223,32 @@ ptk/WatchEvent (watch [_ state _] (let [route (:route state) - screen (-> route :data :name keyword) qparams (:query-params route) pparams (:path-params route) index (:index qparams)] (when (pos? index) (rx/of (dcm/close-thread) - (rt/nav screen pparams (assoc qparams :index (dec index))))))))) + (rt/nav :viewer pparams (assoc qparams :index (dec index))))))))) (def select-next-frame - (ptk/reify ::select-prev-frame + (ptk/reify ::select-next-frame ptk/WatchEvent (watch [_ state _] + (prn "select-next-frame") (let [route (:route state) - screen (-> route :data :name keyword) - qparams (:query-params route) pparams (:path-params route) + qparams (:query-params route) + + page-id (:page-id qparams) index (:index qparams) - total (count (get-in state [:viewer-data :frames]))] + + total (count (get-in state [:viewer :pages page-id :frames]))] + (when (< index (dec total)) (rx/of (dcm/close-thread) - (rt/nav screen pparams (assoc qparams :index (inc index))))))))) + (rt/nav :viewer pparams (assoc qparams :index (inc index))))))))) (s/def ::interactions-mode #{:hide :show :show-on-click}) @@ -309,7 +290,7 @@ (defn go-to-frame-by-index [index] - (ptk/reify ::go-to-frame + (ptk/reify ::go-to-frame-by-index ptk/WatchEvent (watch [_ state _] (let [route (:route state) @@ -324,12 +305,15 @@ (ptk/reify ::go-to-frame ptk/WatchEvent (watch [_ state _] - (let [frames (get-in state [:viewer-data :frames]) + (let [route (:route state) + qparams (:query-params route) + page-id (:page-id qparams) + + frames (get-in state [:viewer :pages page-id :frames]) index (d/index-of-pred frames #(= (:id %) frame-id))] (when index (rx/of (go-to-frame-by-index index))))))) - (defn go-to-section [section] (ptk/reify ::go-to-section @@ -340,13 +324,6 @@ qparams (:query-params route)] (rx/of (rt/nav :viewer pparams (assoc qparams :section section))))))) - -(defn set-current-frame [frame-id] - (ptk/reify ::set-current-frame - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:viewer-data :current-frame-id] frame-id)))) - (defn deselect-all [] (ptk/reify ::deselect-all ptk/UpdateEvent @@ -376,7 +353,10 @@ (ptk/reify ::shift-select-to ptk/UpdateEvent (update [_ state] - (let [objects (get-in state [:viewer-data :objects]) + (let [route (:route state) + qparams (:query-params route) + page-id (:page-id qparams) + objects (get-in state [:viewer :pages page-id :objects]) selection (-> state (get-in [:viewer-local :selected] #{}) (conj id))] @@ -389,8 +369,13 @@ (ptk/reify ::select-all ptk/UpdateEvent (update [_ state] - (let [objects (get-in state [:viewer-data :objects]) - frame-id (get-in state [:viewer-data :current-frame-id]) + (let [route (:route state) + qparams (:query-params route) + page-id (:page-id qparams) + index (:index qparams) + objects (get-in state [:viewer :pages page-id :objects]) + frame-id (get-in state [:viewer :pages page-id :frames index :id]) + selection (->> objects (filter #(= (:frame-id (second %)) frame-id)) (map first) @@ -405,18 +390,50 @@ (let [toggled? (contains? (get-in state [:viewer-local :collapsed]) id)] (update-in state [:viewer-local :collapsed] (if toggled? disj conj) id))))) -(defn hover-shape [id hover?] +(defn hover-shape + [id hover?] (ptk/reify ::hover-shape ptk/UpdateEvent (update [_ state] (assoc-in state [:viewer-local :hover] (when hover? id))))) +;; --- NAV (defn go-to-dashboard - ([] (go-to-dashboard nil)) - ([{:keys [team-id]}] - (ptk/reify ::go-to-dashboard - ptk/WatchEvent + [] + (ptk/reify ::go-to-dashboard + ptk/WatchEvent + (watch [_ state _] + (let [team-id (get-in state [:viewer :project :team-id]) + params {:team-id team-id}] + (rx/of (rt/nav :dashboard-projects params)))))) + +(defn go-to-page + [page-id] + (ptk/reify ::go-to-page + ptk/WatchEvent (watch [_ state _] - (let [team-id (or team-id (get-in state [:viewer-data :project :team-id]))] - (rx/of (rt/nav :dashboard-projects {:team-id team-id}))))))) + (let [route (:route state) + pparams (:path-params route) + qparams (-> (:query-params route) + (assoc :index 0) + (assoc :page-id page-id)) + rname (get-in route [:data :name])] + (rx/of (rt/nav rname pparams qparams)))))) + +(defn go-to-workspace + [page-id] + (ptk/reify ::go-to-workspace + ptk/WatchEvent + (watch [_ state _] + (let [project-id (get-in state [:viewer :project :id]) + file-id (get-in state [:viewer :file :id]) + pparams {:project-id project-id :file-id file-id} + qparams {:page-id page-id}] + (rx/of (rt/nav-new-window* + {:rname :workspace + :path-params pparams + :query-params qparams + :name (str "workspace-" file-id)})))))) + + diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index c9c7258d3e..3411db1659 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -20,6 +20,7 @@ [app.common.transit :as t] [app.common.uuid :as uuid] [app.config :as cfg] + [app.main.data.events :as ev] [app.main.data.messages :as dm] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.common :as dwc] @@ -37,6 +38,7 @@ [app.main.repo :as rp] [app.main.streams :as ms] [app.main.worker :as uw] + [app.util.globals :as ug] [app.util.http :as http] [app.util.i18n :as i18n] [app.util.router :as rt] @@ -48,7 +50,6 @@ [potok.core :as ptk])) ;; (log/set-level! :trace) -;; --- Specs (s/def ::shape-attrs ::cp/shape-attrs) (s/def ::set-of-string @@ -87,7 +88,7 @@ :snap-grid :dynamic-alignment}) -(def layout-names +(def layout-presets {:assets {:del #{:sitemap :layers :document-history } :add #{:assets}} @@ -121,22 +122,31 @@ :picked-color nil :picked-color-select false}) -(declare ensure-layout) - -(defn initialize-layout - [layout-name] - (us/verify (s/nilable ::us/keyword) layout-name) - (ptk/reify ::initialize-layout +(defn ensure-layout + [lname] + (ptk/reify ::ensure-layout ptk/UpdateEvent (update [_ state] (update state :workspace-layout - (fn [layout] - (or layout default-layout)))) + (fn [stored] + (let [todel (get-in layout-presets [lname :del] #{}) + toadd (get-in layout-presets [lname :add] #{})] + (-> stored + (set/difference todel) + (set/union toadd)))))))) + +(defn setup-layout + [lname] + (us/verify (s/nilable ::us/keyword) lname) + (ptk/reify ::setup-layout + ptk/UpdateEvent + (update [_ state] + (update state :workspace-layout #(or % default-layout))) ptk/WatchEvent (watch [_ _ _] - (if (and layout-name (contains? layout-names layout-name)) - (rx/of (ensure-layout layout-name)) + (if (and lname (contains? layout-presets lname)) + (rx/of (ensure-layout lname)) (rx/of (ensure-layout :layers)))))) (defn initialize-file @@ -171,7 +181,12 @@ (->> stream (rx/filter #(= ::dwc/index-initialized %)) (rx/first) - (rx/map #(file-initialized bundle))))))))))) + (rx/map #(file-initialized bundle))))))))) + + ptk/EffectEvent + (effect [_ _ _] + (let [name (str "workspace-" file-id)] + (unchecked-set ug/global "name" name))))) (defn- file-initialized [{:keys [file users project libraries] :as bundle}] @@ -219,8 +234,10 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (dwn/finalize file-id) - ::dwp/finalize)))) + (rx/merge + (rx/of (dwn/finalize file-id)) + (->> (rx/of ::dwp/finalize) + (rx/observe-on :async)))))) (defn initialize-page [page-id] @@ -274,7 +291,7 @@ (watch [it state _] (let [pages (get-in state [:workspace-data :pages-index]) unames (dwc/retrieve-used-names pages) - name (dwc/generate-unique-name unames "Page") + name (dwc/generate-unique-name unames "Page-1") rchange {:type :add-page :id id @@ -348,7 +365,6 @@ (when (= id (:current-page-id state)) go-to-file)))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WORKSPACE File Actions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -357,6 +373,10 @@ [id name] {:pre [(uuid? id) (string? name)]} (ptk/reify ::rename-file + IDeref + (-deref [_] + {::ev/origin "workspace" :id id :name name}) + ptk/UpdateEvent (update [_ state] (assoc-in state [:workspace-file :name] name)) @@ -373,6 +393,9 @@ ;; --- Viewport Sizing +(declare increase-zoom) +(declare decrease-zoom) +(declare set-zoom) (declare zoom-to-fit-all) (defn initialize-viewport @@ -457,7 +480,6 @@ (update :height #(/ % hprop)) (assoc :left-offset left-offset)))))))))))) - (defn start-panning [] (ptk/reify ::start-panning ptk/WatchEvent @@ -484,23 +506,32 @@ (-> state (update :workspace-local dissoc :panning))))) +(defn start-zooming [pt] + (ptk/reify ::start-zooming + ptk/WatchEvent + (watch [_ state stream] + (let [stopper (->> stream (rx/filter (ptk/type? ::finish-zooming)))] + (when-not (get-in state [:workspace-local :zooming]) + (rx/concat + (rx/of #(-> % (assoc-in [:workspace-local :zooming] true))) + (->> stream + (rx/filter ms/pointer-event?) + (rx/filter #(= :delta (:source %))) + (rx/map :pt) + (rx/take-until stopper) + (rx/map (fn [delta] + (let [scale (+ 1 (/ (:y delta) 100))] ;; this number may be adjusted after user testing + (set-zoom pt scale))))))))))) -;; --- Toggle layout flag - -(defn ensure-layout - [layout-name] - (assert (contains? layout-names layout-name) - (str "unexpected layout name: " layout-name)) - (ptk/reify ::ensure-layout +(defn finish-zooming [] + (ptk/reify ::finish-zooming ptk/UpdateEvent (update [_ state] - (update state :workspace-layout - (fn [stored] - (let [todel (get-in layout-names [layout-name :del] #{}) - toadd (get-in layout-names [layout-name :add] #{})] - (-> stored - (set/difference todel) - (set/union toadd)))))))) + (-> state + (update :workspace-local dissoc :zooming))))) + + +;; --- Toggle layout flag (defn toggle-layout-flags [& flags] @@ -569,6 +600,16 @@ (update state :workspace-local #(impl-update-zoom % center (fn [z] (max (/ z 1.3) 0.01))))))) +(defn set-zoom + [center scale] + (ptk/reify ::set-zoom + ptk/UpdateEvent + (update [_ state] + (update state :workspace-local + #(impl-update-zoom % center (fn [z] (-> (* z scale) + (max 0.01) + (min 200)))))))) + (def reset-zoom (ptk/reify ::reset-zoom ptk/UpdateEvent @@ -1058,6 +1099,9 @@ :group (rx/of (dwc/select-shapes (into (d/ordered-set) [(last shapes)]))) + :svg-raw + nil + (rx/of (dwc/start-edition-mode id) (dwdp/start-path-edit id))))))))) @@ -1089,7 +1133,7 @@ (defn align-objects [axis] (us/verify ::gal/align-axis axis) - (ptk/reify :align-objects + (ptk/reify ::align-objects ptk/WatchEvent (watch [_ state _] (let [page-id (:current-page-id state) @@ -1120,7 +1164,7 @@ (defn distribute-objects [axis] (us/verify ::gal/dist-axis axis) - (ptk/reify :align-objects + (ptk/reify ::distribute-objects ptk/WatchEvent (watch [_ state _] (let [page-id (:current-page-id state) @@ -1195,7 +1239,7 @@ (rx/of (rt/nav' :workspace pparams qparams)))))) ([page-id] (us/verify ::us/uuid page-id) - (ptk/reify ::go-to-page + (ptk/reify ::go-to-page-2 ptk/WatchEvent (watch [_ state _] (let [project-id (:current-project-id state) @@ -1207,7 +1251,10 @@ (defn go-to-layout [layout] (us/verify ::layout-flag layout) - (ptk/reify ::go-to-layout + (ptk/reify ::set-workspace-layout + IDeref + (-deref [_] {:layout layout}) + ptk/WatchEvent (watch [_ state _] (let [project-id (get-in state [:workspace-project :id]) @@ -1234,10 +1281,14 @@ ptk/WatchEvent (watch [_ state _] (let [{:keys [current-file-id current-page-id]} state - params {:file-id (or file-id current-file-id) - :page-id (or page-id current-page-id)}] + pparams {:file-id (or file-id current-file-id)} + qparams {:page-id (or page-id current-page-id) + :index 0}] (rx/of ::dwp/force-persist - (rt/nav-new-window :viewer params {:index 0}))))))) + (rt/nav-new-window* {:rname :viewer + :path-params pparams + :query-params qparams + :name (str "viewer-" (:file-id pparams))}))))))) (defn go-to-dashboard ([] (go-to-dashboard nil)) @@ -1251,7 +1302,7 @@ (defn go-to-dashboard-fonts [] - (ptk/reify ::go-to-dashboard + (ptk/reify ::go-to-dashboard-fonts ptk/WatchEvent (watch [_ state _] (let [team-id (:current-team-id state)] diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index 1813fa7120..f76b288d41 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -69,7 +69,7 @@ (defn show-palette "Show the palette tool and change the library it uses" [selected] - (ptk/reify ::change-palette-selected + (ptk/reify ::show-palette ptk/UpdateEvent (update [_ state] (-> state diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index d83b51455f..8a07d1d900 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -71,7 +71,7 @@ (defn center-to-comment-thread [{:keys [position] :as thread}] (us/assert ::dcm/comment-thread thread) - (ptk/reify :center-to-comment-thread + (ptk/reify ::center-to-comment-thread ptk/UpdateEvent (update [_ state] (update state :workspace-local @@ -89,7 +89,7 @@ (defn navigate [thread] (us/assert ::dcm/comment-thread thread) - (ptk/reify ::navigate + (ptk/reify ::open-comment-thread ptk/WatchEvent (watch [_ _ stream] (let [pparams {:project-id (:project-id thread) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index b76dce9749..0fe0939d76 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -68,19 +68,17 @@ (defn generate-unique-name "A unique name generator" - ([used basename] - (generate-unique-name used basename false)) - ([used basename prefix-first?] - (s/assert ::set-of-string used) - (s/assert ::us/string basename) - (let [[prefix initial] (extract-numeric-suffix basename)] - (loop [counter initial] - (let [candidate (if (and (= 1 counter) prefix-first?) - (str prefix) - (str prefix "-" counter))] - (if (contains? used candidate) - (recur (inc counter)) - candidate)))))) + [used basename] + (s/assert ::set-of-string used) + (s/assert ::us/string basename) + (if-not (contains? used basename) + basename + (let [[prefix initial] (extract-numeric-suffix basename)] + (loop [counter initial] + (let [candidate (str prefix "-" counter)] + (if (contains? used candidate) + (recur (inc counter)) + candidate)))))) ;; --- Shape attrs (Layers Sidebar) @@ -144,6 +142,38 @@ :origin it :save-undo? false})))))))))) +(defn undo-to-index + "Repeat undoing or redoing until dest-index is reached." + [dest-index] + (ptk/reify ::undo-to-index + ptk/WatchEvent + (watch [it state _] + (let [edition (get-in state [:workspace-local :edition]) + drawing (get state :workspace-drawing)] + (when-not (or (some? edition) (not-empty drawing)) + (let [undo (:workspace-undo state) + items (:items undo) + index (or (:index undo) (dec (count items)))] + (when (and (some? items) + (<= 0 dest-index (dec (count items)))) + (let [changes (vec (apply concat + (cond + (< dest-index index) + (->> (subvec items (inc dest-index) (inc index)) + (reverse) + (map :undo-changes)) + (> dest-index index) + (->> (subvec items (inc index) (inc dest-index)) + (map :redo-changes)) + :else [])))] + (when (seq changes) + (rx/of (dwu/materialize-undo changes dest-index) + (dch/commit-changes {:redo-changes changes + :undo-changes [] + :origin it + :save-undo? false}))))))))))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shapes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/grid.cljs b/frontend/src/app/main/data/workspace/grid.cljs index b04b99f6aa..ed3e2944d8 100644 --- a/frontend/src/app/main/data/workspace/grid.cljs +++ b/frontend/src/app/main/data/workspace/grid.cljs @@ -54,7 +54,7 @@ (defn remove-frame-grid [frame-id index] - (ptk/reify ::set-frame-grid + (ptk/reify ::remove-frame-grid ptk/WatchEvent (watch [_ _ _] (rx/of (dch/update-shapes [frame-id] (fn [o] (update o :grids (fnil #(d/remove-at-index % index) [])))))))) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index db26140a8d..f3f92c2b3c 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -182,7 +182,7 @@ shapes (shapes-for-grouping objects selected)] (when-not (empty? shapes) (let [[group rchanges uchanges] - (prepare-create-group objects page-id shapes "Group" false)] + (prepare-create-group objects page-id shapes "Group-1" false)] (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges :origin it}) @@ -221,7 +221,7 @@ (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) [(first shapes) [] []] - (prepare-create-group objects page-id shapes "Group" true)) + (prepare-create-group objects page-id shapes "Group-1" true)) rchanges (d/concat rchanges [{:type :mod-obj diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 77b15d1b9d..995fb674ca 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -83,12 +83,15 @@ (defn add-color [color] - (let [id (uuid/next) - color (assoc color - :id id - :name (default-color-name color))] + (let [id (uuid/next) + color (-> color + (assoc :id id) + (assoc :name (default-color-name color)))] (us/assert ::cp/color color) (ptk/reify ::add-color + IDeref + (-deref [_] color) + ptk/WatchEvent (watch [it _ _] (let [rchg {:type :add-color @@ -211,6 +214,9 @@ (let [typography (update typography :id #(or % (uuid/next)))] (us/assert ::cp/typography typography) (ptk/reify ::add-typography + IDeref + (-deref [_] typography) + ptk/WatchEvent (watch [it _ _] (let [rchg {:type :add-typography @@ -258,17 +264,20 @@ :undo-changes [uchg] :origin it})))))) -(def add-component - "Add a new component to current file library, from the currently selected shapes." - (ptk/reify ::add-component + +(defn- add-component2 + "This is the second step of the component creation." + [selected] + (ptk/reify ::add-component2 + IDeref + (-deref [_] {:num-shapes (count selected)}) + ptk/WatchEvent (watch [it state _] (let [file-id (:current-file-id state) page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) - selected (cp/clean-loops objects selected) - shapes (dwg/shapes-for-grouping objects selected)] + shapes (dwg/shapes-for-grouping objects selected)] (when-not (empty? shapes) (let [[group rchanges uchanges] (dwlh/generate-add-component shapes objects page-id file-id)] @@ -278,6 +287,20 @@ :origin it}) (dwc/select-shapes (d/ordered-set (:id group))))))))))) +(defn add-component + "Add a new component to current file library, from the currently selected shapes. + This operation is made in two steps, first one for calculate the + shapes that will be part of the component and the second one with + the component creation." + [] + (ptk/reify ::add-component + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + selected (->> (wsh/lookup-selected state) + (cp/clean-loops objects))] + (rx/of (add-component2 selected)))))) + (defn rename-component "Rename the component with the given id, in the current file library." [id new-name] @@ -462,6 +485,31 @@ :undo-changes uchanges :origin it})))))) +(def detach-selected-components + (ptk/reify ::detach-selected-components + ptk/WatchEvent + (watch [it state _] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + local-library (dwlh/get-local-file state) + container (cp/get-container page-id :page local-library) + + selected (->> state + (wsh/lookup-selected) + (cp/clean-loops objects)) + + [rchanges uchanges] + (reduce (fn [changes id] + (dwlh/concat-changes + changes + (dwlh/generate-detach-instance id container))) + dwlh/empty-changes + selected)] + + (rx/of (dch/commit-changes {:redo-changes rchanges + :undo-changes uchanges + :origin it})))))) + (defn nav-to-component-file [file-id] (us/assert ::us/uuid file-id) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 16115953e6..226d5b2ba1 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -129,7 +129,7 @@ (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) [(first shapes) [] []] - (dwg/prepare-create-group objects page-id shapes "Component" true)) + (dwg/prepare-create-group objects page-id shapes "Component-1" true)) [new-shape new-shapes updated-shapes] (make-component-shape group objects file-id) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 93dcf92291..ac5fdbe12b 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -59,7 +59,8 @@ ptk/WatchEvent (watch [_ state stream] (let [wsession (get-in state [:ws file-id]) - stoper (rx/filter #(= ::finalize %) stream) + stoper (->> stream + (rx/filter (ptk/type? ::finalize))) interval (* 1000 60)] (->> (rx/merge ;; Each 60 seconds send a keepalive message for maintain @@ -106,7 +107,7 @@ (defn- handle-pointer-send [file-id point] - (ptk/reify ::handle-pointer-update + (ptk/reify ::handle-pointer-send ptk/EffectEvent (effect [_ state _] (let [ws (get-in state [:ws file-id]) @@ -122,11 +123,10 @@ (defn finalize [file-id] (ptk/reify ::finalize - ptk/WatchEvent - (watch [_ state _] + ptk/EffectEvent + (effect [_ state _] (when-let [ws (get-in state [:ws file-id])] - (ws/-close ws)) - (rx/of ::finalize)))) + (ws/-close ws))))) ;; --- Handle: Presence diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index a7ae00efe9..89331ebd2b 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -179,7 +179,7 @@ :right (gpt/point 1 0))) (defn finish-move-selected [] - (ptk/reify ::move-selected + (ptk/reify ::finish-move-selected ptk/UpdateEvent (update [_ state] (let [id (get-in state [:workspace-local :edition])] diff --git a/frontend/src/app/main/data/workspace/path/helpers.cljs b/frontend/src/app/main/data/workspace/path/helpers.cljs index deff63dce1..9b36e4099a 100644 --- a/frontend/src/app/main/data/workspace/path/helpers.cljs +++ b/frontend/src/app/main/data/workspace/path/helpers.cljs @@ -18,7 +18,7 @@ (defn end-path-event? [event] (or (= (ptk/type event) ::common/finish-path) - (= (ptk/type event) :esc-pressed) + (= (ptk/type event) :app.main.data.workspace.path.shortcuts/esc-pressed) (= :app.main.data.workspace.common/clear-edition-mode (ptk/type event)) (= :app.main.data.workspace/finalize-page (ptk/type event)) (= event :interrupt) ;; ESC diff --git a/frontend/src/app/main/data/workspace/path/shortcuts.cljs b/frontend/src/app/main/data/workspace/path/shortcuts.cljs index 55dbcd592d..60c7eae67c 100644 --- a/frontend/src/app/main/data/workspace/path/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/path/shortcuts.cljs @@ -20,7 +20,7 @@ ;; Shortcuts format https://github.com/ccampbell/mousetrap (defn esc-pressed [] - (ptk/reify :esc-pressed + (ptk/reify ::esc-pressed ptk/WatchEvent (watch [_ state _] ;; Not interrupt when we're editing a path diff --git a/frontend/src/app/main/data/workspace/path/undo.cljs b/frontend/src/app/main/data/workspace/path/undo.cljs index 43dcb09313..4bb34e718e 100644 --- a/frontend/src/app/main/data/workspace/path/undo.cljs +++ b/frontend/src/app/main/data/workspace/path/undo.cljs @@ -90,7 +90,7 @@ "Joins the head with the previous undo in one. This is done so when the user changes a node handlers after adding it the undo merges both in one operation only" [] - (ptk/reify ::add-undo-entry + (ptk/reify ::merge-head ptk/UpdateEvent (update [_ state] (let [id (st/get-path-id state) diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index 9471a58287..c6acba6e5a 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -12,6 +12,7 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.data.dashboard :as dd] + [app.main.data.events :as ev] [app.main.data.fonts :as df] [app.main.data.media :as di] [app.main.data.messages :as dm] @@ -275,6 +276,10 @@ [id is-shared] {:pre [(uuid? id) (boolean? is-shared)]} (ptk/reify ::set-file-shared + IDeref + (-deref [_] + {::ev/origin "workspace" :id id :shared is-shared}) + ptk/UpdateEvent (update [_ state] (assoc-in state [:workspace-file :is-shared] is-shared)) @@ -313,7 +318,7 @@ (defn link-file-to-library [file-id library-id] - (ptk/reify ::link-file-to-library + (ptk/reify ::attach-library ptk/WatchEvent (watch [_ _ _] (let [fetched #(assoc-in %2 [:workspace-libraries (:id %1)] %1) @@ -325,7 +330,7 @@ (defn unlink-file-from-library [file-id library-id] - (ptk/reify ::unlink-file-from-library + (ptk/reify ::detach-library ptk/UpdateEvent (update [_ state] (d/dissoc-in state [:workspace-libraries library-id])) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index c04bfbedf6..86208467f6 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -114,7 +114,7 @@ (defn deselect-shape [id] (us/verify ::us/uuid id) - (ptk/reify ::select-shape + (ptk/reify ::deselect-shape ptk/UpdateEvent (update [_ state] (update-in state [:workspace-local :selected] disj id)))) @@ -219,17 +219,15 @@ lks/empty-linked-set) selrect (get-in state [:workspace-local :selrect]) blocked? (fn [id] (get-in objects [id :blocked] false))] - (rx/merge - - (when selrect - (->> (uw/ask! {:cmd :selection/query - :page-id page-id - :rect selrect - :include-frames? true - :full-frame? true}) - (rx/map #(cp/clean-loops objects %)) - (rx/map #(into initial-set (filter (comp not blocked?)) %)) - (rx/map select-shapes)))))))) + (when selrect + (->> (uw/ask! {:cmd :selection/query + :page-id page-id + :rect selrect + :include-frames? true + :full-frame? true}) + (rx/map #(cp/clean-loops objects %)) + (rx/map #(into initial-set (filter (comp not blocked?)) %)) + (rx/map select-shapes))))))) (defn select-inside-group [group-id position] @@ -383,6 +381,53 @@ (into [fch] sch))) +(defn clear-memorize-duplicated + [] + (ptk/reify ::clear-memorize-duplicated + ptk/UpdateEvent + (update [_ state] + (d/dissoc-in state [:workspace-local :duplicated])))) + +(defn memorize-duplicated + "When duplicate an object, remember the operation during the following seconds. + If the user moves the duplicated object, and then duplicates it again, check + the displacement and apply it to the third copy. This is useful for doing + grids or cascades of cloned objects." + [id-original id-duplicated] + (ptk/reify ::memorize-duplicated + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-local :duplicated] {:id-original id-original + :id-duplicated id-duplicated})) + + ptk/WatchEvent + (watch [_ _ stream] + (let [stoper (rx/filter (ptk/type? ::memorize-duplicated) stream)] + (->> (rx/timer 10000) ;; This time may be adjusted after some user testing. + (rx/take-until stoper) + (rx/map clear-memorize-duplicated)))))) + +(defn calc-duplicate-delta + [obj state objects] + (let [{:keys [id-original id-duplicated]} + (get-in state [:workspace-local :duplicated])] + (if (and (not= id-original (:id obj)) + (not= id-duplicated (:id obj))) + + ;; The default is leave normal shapes in place, but put + ;; new frames to the right of the original. + (if (= (:type obj) :frame) + (gpt/point (+ (:width obj) 50) 0) + (gpt/point 0 0)) + + (let [obj-original (get objects id-original) + obj-duplicated (get objects id-duplicated) + distance (gpt/subtract (gpt/point obj-duplicated) + (gpt/point obj-original)) + new-pos (gpt/add (gpt/point obj-duplicated) distance) + delta (gpt/subtract new-pos (gpt/point obj))] + delta)))) + (def duplicate-selected (ptk/reify ::duplicate-selected ptk/WatchEvent @@ -390,7 +435,10 @@ (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) selected (wsh/lookup-selected state) - delta (gpt/point 0 0) + delta (if (= (count selected) 1) + (let [obj (get objects (first selected))] + (calc-duplicate-delta obj state objects)) + (gpt/point 0 0)) unames (dwc/retrieve-used-names objects) @@ -400,15 +448,20 @@ uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %)) (reverse rchanges)) + id-original (when (= (count selected) 1) (first selected)) + selected (->> rchanges (filter #(selected (:old-id %))) (map #(get-in % [:obj :id])) - (into (d/ordered-set)))] + (into (d/ordered-set))) + + id-duplicated (when (= (count selected) 1) (first selected))] (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges :origin it}) - (select-shapes selected)))))) + (select-shapes selected) + (memorize-duplicated id-original id-duplicated)))))) (defn change-hover-state [id value] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 9d8ce96180..d589df2e83 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -91,7 +91,11 @@ :create-component {:tooltip (ds/meta "K") :command (ds/c-mod "k") - :fn #(st/emit! dwl/add-component)} + :fn #(st/emit! (dwl/add-component))} + + :detach-component {:tooltip (ds/meta-shift "K") + :command (ds/c-mod "shift+k") + :fn #(st/emit! dwl/detach-selected-components)} :flip-vertical {:tooltip (ds/shift "V") :command "shift+v" diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 49942812a2..3193b549dc 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -95,7 +95,11 @@ (d/parse-double)))))) (defn setup-stroke [shape] - (let [shape + (let [stroke-linecap (-> (or (get-in shape [:svg-attrs :stroke-linecap]) + (get-in shape [:svg-attrs :style :stroke-linecap])) + ((d/nilf str/trim)) + ((d/nilf keyword))) + shape (cond-> shape (uc/color? (get-in shape [:svg-attrs :stroke])) (-> (update :svg-attrs dissoc :stroke) @@ -113,8 +117,16 @@ (get-in shape [:svg-attrs :style :stroke-width]) (-> (update-in [:svg-attrs :style] dissoc :stroke-width) (assoc :stroke-width (-> (get-in shape [:svg-attrs :style :stroke-width]) - (d/parse-double)))))] - (if (d/any-key? shape :stroke-color :stroke-opacity :stroke-width) + (d/parse-double)))) + + (and stroke-linecap (= (:type shape) :path)) + (-> (update-in [:svg-attrs :style] dissoc :stroke-linecap) + (cond-> + (#{:round :square} stroke-linecap) + (assoc :stroke-cap-start stroke-linecap + :stroke-cap-end stroke-linecap))))] + + (if (d/any-key? shape :stroke-color :stroke-opacity :stroke-width :stroke-cap-start :stroke-cap-end) (merge {:stroke-style :svg} shape) shape))) @@ -331,7 +343,7 @@ (let [{:keys [tag attrs]} element-data attrs (usvg/format-styles attrs) element-data (cond-> element-data (map? element-data) (assoc :attrs attrs)) - name (dwc/generate-unique-name unames (or (:id attrs) (tag->name tag)) true) + name (dwc/generate-unique-name unames (or (:id attrs) (tag->name tag))) att-refs (usvg/find-attr-references attrs) references (usvg/find-def-references (:defs svg-data) att-refs) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 713371c7ed..9426033c87 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -11,6 +11,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.math :as mth] [app.common.pages :as cp] [app.common.spec :as us] [app.main.data.workspace.changes :as dch] @@ -222,7 +223,7 @@ root transformed-root)))] (reduce set-child - (update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers)) + (assoc-in modif-tree [(:id shape) :modifiers] modifiers) children))) (defn- check-delta @@ -281,7 +282,7 @@ (defn start-resize "Enter mouse resize mode, until mouse button is released." [handler ids shape] - (letfn [(resize [shape initial layout [point lock? point-snap]] + (letfn [(resize [shape initial layout [point lock? center? point-snap]] (let [{:keys [width height]} (:selrect shape) {:keys [rotation]} shape rotation (or rotation 0) @@ -315,17 +316,34 @@ scalev) + ;; Resize origin point given the selected handler + origin (handler-resize-origin (:selrect shape) handler) + + shape-center (gsh/center-shape shape) shape-transform (:transform shape (gmt/matrix)) shape-transform-inverse (:transform-inverse shape (gmt/matrix)) - shape-center (gsh/center-shape shape) + ;; If we want resize from center, displace the shape + ;; so it is still centered after resize. + displacement (when center? + (-> shape-center + (gpt/subtract origin) + (gpt/multiply scalev) + (gpt/add origin) + (gpt/subtract shape-center) + (gpt/multiply (gpt/point -1 -1)) + (gpt/transform shape-transform))) - ;; Resize origin point given the selected handler - origin (-> (handler-resize-origin (:selrect shape) handler) - (gsh/transform-point-center shape-center shape-transform))] + origin (cond-> (gsh/transform-point-center origin shape-center shape-transform) + (some? displacement) + (gpt/add displacement)) + + displacement (when (some? displacement) + (gmt/translate-matrix displacement))] (rx/of (set-modifiers ids - {:resize-vector scalev + {:displacement displacement + :resize-vector scalev :resize-origin origin :resize-transform shape-transform :resize-scale-text scale-text @@ -334,9 +352,9 @@ ;; Unifies the instantaneous proportion lock modifier ;; activated by Shift key and the shapes own proportion ;; lock flag that can be activated on element options. - (normalize-proportion-lock [[point shift?]] + (normalize-proportion-lock [[point shift? alt?]] (let [proportion-lock? (:proportion-lock shape)] - [point (or proportion-lock? shift?)]))] + [point (or proportion-lock? shift?) alt?]))] (reify ptk/UpdateEvent (update [_ state] @@ -358,11 +376,11 @@ (rx/concat (rx/of (dch/update-shapes text-shapes-ids #(assoc % :grow-type :fixed))) (->> ms/mouse-position - (rx/with-latest vector ms/mouse-position-shift) + (rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt) (rx/map normalize-proportion-lock) - (rx/switch-map (fn [[point :as current]] - (->> (snap/closest-snap-point page-id resizing-shapes layout zoom point) - (rx/map #(conj current %))))) + (rx/switch-map (fn [[point _ _ :as current]] + (->> (snap/closest-snap-point page-id resizing-shapes layout zoom point) + (rx/map #(conj current %))))) (rx/mapcat (partial resize shape initial-position layout)) (rx/take-until stoper)) (rx/of (apply-modifiers ids) @@ -493,7 +511,7 @@ (defn- start-move-duplicate [from-position] - (ptk/reify ::start-move-selected + (ptk/reify ::start-move-duplicate ptk/WatchEvent (watch [_ _ stream] (->> stream @@ -521,10 +539,18 @@ layout (get state :workspace-layout) zoom (get-in state [:workspace-local :zoom] 1) + fix-axis (fn [[position shift?]] + (let [delta (gpt/to-vec from-position position)] + (if shift? + (if (> (mth/abs (:x delta)) (mth/abs (:y delta))) + (gpt/point (:x delta) 0) + (gpt/point 0 (:y delta))) + delta))) position (->> ms/mouse-position (rx/take-until stopper) - (rx/map #(gpt/to-vec from-position %))) + (rx/with-latest-from ms/mouse-position-shift) + (rx/map #(fix-axis %))) snap-delta (rx/concat ;; We send the nil first so the stream is not waiting for the first value diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index d48e5eebea..5c0217152d 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -44,7 +44,8 @@ (defn- calculate-dimensions [{:keys [objects] :as data} vport] - (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true}) + (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true + :include-frame-children? false}) to-finite (fn [val fallback] (if (not (mth/finite? val)) fallback val)) rect (cond->> (gsh/selection-rect shapes) (some? vport) @@ -131,7 +132,8 @@ (mf/defc page-svg {::mf/wrap [mf/memo]} - [{:keys [data width height thumbnails? embed?] :as props}] + [{:keys [data width height thumbnails? embed? include-metadata?] :as props + :or {embed? false include-metadata? false}}] (let [objects (:objects data) root (get objects uuid/zero) shapes @@ -158,35 +160,36 @@ (mf/deps objects) #(shape-wrapper-factory objects))] [:& (mf/provider embed/context) {:value embed?} - [:svg {:view-box vbox - :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg" - :xmlns:penpot "https://penpot.app/xmlns" - :style {:width "100%" - :height "100%" - :background background-color}} + [:& (mf/provider use/include-metadata-ctx) {:value include-metadata?} + [:svg {:view-box vbox + :version "1.1" + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :style {:width "100%" + :height "100%" + :background background-color}} - [:& use/export-page {:options (:options data)}] - [:& ff/fontfaces-style {:shapes root-children}] - (for [item shapes] - (let [frame? (= (:type item) :frame)] - (cond - (and frame? thumbnails? (some? (:thumbnail item))) - [:image {:xlinkHref (:thumbnail item) - :x (:x item) - :y (:y item) - :width (:width item) - :height (:height item) - ;; DEBUG - ;; :style {:filter "sepia(1)"} - }] - frame? - [:& frame-wrapper {:shape item - :key (:id item)}] - :else - [:& shape-wrapper {:shape item - :key (:id item)}])))]])) + [:& use/export-page {:options (:options data)}] + [:& ff/fontfaces-style {:shapes root-children}] + (for [item shapes] + (let [frame? (= (:type item) :frame)] + (cond + (and frame? thumbnails? (some? (:thumbnail item))) + [:image {:xlinkHref (:thumbnail item) + :x (:x item) + :y (:y item) + :width (:width item) + :height (:height item) + ;; DEBUG + ;; :style {:filter "sepia(1)"} + }] + frame? + [:& frame-wrapper {:shape item + :key (:id item)}] + :else + [:& shape-wrapper {:shape item + :key (:id item)}])))]]])) (mf/defc frame-svg {::mf/wrap [mf/memo]} @@ -197,6 +200,8 @@ frame-id (:id frame) + include-metadata? (mf/use-ctx use/include-metadata-ctx) + modifier-ids (concat [frame-id] (cp/get-children frame-id objects)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) objects (reduce update-fn objects modifier-ids) @@ -214,9 +219,9 @@ :width width :height height :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns "http://www.w3.org/2000/svg" - :xmlns:penpot "https://penpot.app/xmlns"} + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")} [:& wrapper {:shape frame :view-box vbox}]])) (mf/defc component-svg @@ -229,6 +234,8 @@ group-id (:id group) + include-metadata? (mf/use-ctx use/include-metadata-ctx) + modifier-ids (concat [group-id] (cp/get-children group-id objects)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) objects (reduce update-fn objects modifier-ids) @@ -246,10 +253,11 @@ :width width :height height :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns "http://www.w3.org/2000/svg" - :xmlns:penpot "https://penpot.app/xmlns"} - [:& wrapper {:shape group :view-box vbox}]])) + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")} + [:> shape-container {:shape group} + [:& wrapper {:shape group :view-box vbox}]]])) (mf/defc component-symbol [{:keys [id data] :as props}] @@ -287,20 +295,21 @@ (let [data (obj/get props "data") children (obj/get props "children") - embed? (obj/get props "embed?")] + embed? (obj/get props "embed?") + include-metadata? (obj/get props "include-metadata?")] [:& (mf/provider embed/context) {:value embed?} - [:svg {:version "1.1" - :xmlns "http://www.w3.org/2000/svg" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns:penpot "https://penpot.app/xmlns" - :style {:width "100vw" - :height "100vh" - :display (when-not (some? children) "none")}} + [:& (mf/provider use/include-metadata-ctx) {:value include-metadata?} + [:svg {:version "1.1" + :xmlns "http://www.w3.org/2000/svg" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + :style {:width "100vw" + :height "100vh" + :display (when-not (some? children) "none")}} + [:defs + (for [[component-id component-data] (:components data)] + [:& component-symbol {:id component-id + :key (str component-id) + :data component-data}])] - [:defs - (for [[component-id component-data] (:components data)] - [:& component-symbol {:id component-id - :key (str component-id) - :data component-data}])] - - children]])) + children]]])) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 236f4a0af2..4dcbac803e 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -38,6 +38,9 @@ (def threads-ref (l/derived :comment-threads st/state)) +(def share-links + (l/derived :share-links st/state)) + ;; ---- Dashboard refs (def dashboard-local @@ -110,6 +113,7 @@ :edit-path :tooltip :panning + :zooming :picking-color? :transform :hover @@ -286,8 +290,17 @@ ;; ---- Viewer refs +(def viewer-file + (l/derived :viewer-file st/state)) + +(def viewer-project + (l/derived :viewer-file st/state)) + (def viewer-data - (l/derived :viewer-data st/state)) + (l/derived :viewer st/state)) + +(def viewer-state + (l/derived :viewer st/state)) (def viewer-local (l/derived :viewer-local st/state)) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index e3eb8858e1..ffe3452d04 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -63,7 +63,7 @@ (->> (rx/of data) (rx/map (fn [data] - (let [elem (mf/element exports/page-svg #js {:data data :embed? true})] + (let [elem (mf/element exports/page-svg #js {:data data :embed? true :include-metadata? true})] (rds/renderToStaticMarkup elem))))))) (defn render-components @@ -82,5 +82,5 @@ (->> (rx/of data) (rx/map (fn [data] - (let [elem (mf/element exports/components-sprite-svg #js {:data data :embed? true})] + (let [elem (mf/element exports/components-sprite-svg #js {:data data :embed? true :include-metadata? true})] (rds/renderToStaticMarkup elem)))))))) diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index e9df95ee65..7ec498c401 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -107,6 +107,14 @@ :response-type :blob}) (rx/mapcat handle-response))) +(defmethod query :export-frames + [_ params] + (->> (http/send! {:method :post + :uri (u/join base-uri "export-frames") + :body (http/transit-data params) + :response-type :blob}) + (rx/mapcat handle-response))) + (derive :upload-file-media-object ::multipart-upload) (derive :update-profile-photo ::multipart-upload) (derive :update-team-photo ::multipart-upload) diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index 15a05e01ae..d12b62acca 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -14,7 +14,7 @@ ;; --- User Events -(defrecord KeyboardEvent [type key shift ctrl alt meta]) +(defrecord KeyboardEvent [type key shift ctrl alt meta editing]) (defn keyboard-event? [v] @@ -137,3 +137,14 @@ (rx/dedupe))] (rx/subscribe-with ob sub) sub)) + +(defonce keyboard-space + (let [sub (rx/behavior-subject nil) + ob (->> st/stream + (rx/filter keyboard-event?) + (rx/filter kbd/space?) + (rx/filter (comp not kbd/editing?)) + (rx/map #(= :down (:type %))) + (rx/dedupe))] + (rx/subscribe-with ob sub) + sub)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index aeafb377f8..bcec7a1b7d 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -20,14 +20,13 @@ [app.main.ui.context :as ctx] [app.main.ui.cursors :as c] [app.main.ui.dashboard :refer [dashboard]] - [app.main.ui.handoff :refer [handoff]] [app.main.ui.icons :as i] [app.main.ui.messages :as msgs] [app.main.ui.onboarding] [app.main.ui.render :as render] [app.main.ui.settings :as settings] [app.main.ui.static :as static] - [app.main.ui.viewer :refer [viewer-page]] + [app.main.ui.viewer :as viewer] [app.main.ui.workspace :as workspace] [app.util.timers :as ts] [cljs.pprint :refer [pprint]] @@ -41,25 +40,26 @@ (s/def ::page-id ::us/uuid) (s/def ::file-id ::us/uuid) -(s/def ::viewer-path-params - (s/keys :req-un [::file-id ::page-id])) - (s/def ::section ::us/keyword) (s/def ::index ::us/integer) -(s/def ::token (s/nilable ::us/string)) +(s/def ::token (s/nilable ::us/not-empty-string)) +(s/def ::share-id ::us/uuid) + +(s/def ::viewer-path-params + (s/keys :req-un [::file-id])) (s/def ::viewer-query-params (s/keys :req-un [::index] - :opt-un [::token ::section])) + :opt-un [::share-id ::section ::page-id])) (def routes [["/auth" ["/login" :auth-login] - (when cf/registration-enabled + (when (contains? @cf/flags :registration) ["/register" :auth-register]) - (when cf/registration-enabled + (when (contains? @cf/flags :registration) ["/register/validate" :auth-register-validate]) - (when cf/registration-enabled + (when (contains? @cf/flags :registration) ["/register/success" :auth-register-success]) ["/recovery/request" :auth-recovery-request] ["/recovery" :auth-recovery] @@ -71,7 +71,7 @@ ["/feedback" :settings-feedback] ["/options" :settings-options]] - ["/view/:file-id/:page-id" + ["/view/:file-id" {:name :viewer :conform {:path-params ::viewer-path-params @@ -143,26 +143,21 @@ :dashboard-team-settings) [:* #_[:div.modal-wrapper - [:& app.main.ui.onboarding/release-notes-modal {:version "1.7"}]] + [:& app.main.ui.onboarding/release-notes-modal {:version "1.8"}]] [:& dashboard {:route route}]] :viewer - (let [index (get-in route [:query-params :index]) - token (get-in route [:query-params :token]) - section (get-in route [:query-params :section] :interactions) - file-id (get-in route [:path-params :file-id]) - page-id (get-in route [:path-params :page-id])] + (let [{:keys [query-params path-params]} route + {:keys [index share-id section page-id] :or {section :interactions}} query-params + {:keys [file-id]} path-params] [:& fs/fullscreen-wrapper {} - (if (= section :handoff) - [:& handoff {:page-id page-id - :file-id file-id - :index index - :token token}] - [:& viewer-page {:page-id page-id - :file-id file-id - :section section - :index index - :token token}])]) + (if (:token query-params) + [:& viewer/breaking-change-notice] + [:& viewer/viewer-page {:page-id page-id + :file-id file-id + :section section + :index index + :share-id share-id}])]) :render-object (do diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index 3f8e89e662..399e4ddb46 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -7,7 +7,7 @@ (ns app.main.ui.auth.login (:require [app.common.spec :as us] - [app.config :as cfg] + [app.config :as cf] [app.main.data.messages :as dm] [app.main.data.users :as du] [app.main.repo :as rp] @@ -23,10 +23,10 @@ [rumext.alpha :as mf])) (def show-alt-login-buttons? - (or cfg/google-client-id - cfg/gitlab-client-id - cfg/github-client-id - cfg/oidc-client-id)) + (or cf/google-client-id + cf/gitlab-client-id + cf/github-client-id + cf/oidc-client-id)) (s/def ::email ::us/email) (s/def ::password ::us/not-empty-string) @@ -97,7 +97,7 @@ [:div.fields-row [:& fm/input {:name :email - :type "text" + :type "email" :tab-index "2" :help-icon i/at :label (tr "auth.email")}]] @@ -113,7 +113,7 @@ [:& fm/submit-button {:label (tr "auth.login-submit")}] - (when cfg/login-with-ldap + (when (contains? @cf/flags :login-with-ldap) [:& fm/submit-button {:label (tr "auth.login-with-ldap-submit") :on-click on-submit-ldap}])]]])) @@ -121,26 +121,26 @@ (mf/defc login-buttons [{:keys [params] :as props}] [:div.auth-buttons - (when cfg/google-client-id + (when cf/google-client-id [:a.btn-ocean.btn-large.btn-google-auth {:on-click #(login-with-oauth % :google params)} (tr "auth.login-with-google-submit")]) - (when cfg/gitlab-client-id + (when cf/gitlab-client-id [:a.btn-ocean.btn-large.btn-gitlab-auth {:on-click #(login-with-oauth % :gitlab params)} [:img.logo {:src "/images/icons/brand-gitlab.svg"}] (tr "auth.login-with-gitlab-submit")]) - (when cfg/github-client-id + (when cf/github-client-id [:a.btn-ocean.btn-large.btn-github-auth {:on-click #(login-with-oauth % :github params)} [:img.logo {:src "/images/icons/brand-github.svg"}] (tr "auth.login-with-github-submit")]) - (when cfg/oidc-client-id + (when cf/oidc-client-id [:a.btn-ocean.btn-large.btn-github-auth {:on-click #(login-with-oauth % :oidc params)} (tr "auth.login-with-oidc-submit")])]) @@ -166,14 +166,13 @@ [:a {:on-click #(st/emit! (rt/nav :auth-recovery-request))} (tr "auth.forgot-password")]] - (when cfg/registration-enabled + (when (contains? @cf/flags :registration) [:div.link-entry [:span (tr "auth.register") " "] [:a {:on-click #(st/emit! (rt/nav :auth-register {} params))} (tr "auth.register-submit")]])] - - (when cfg/allow-demo-users + (when (contains? @cf/flags :demo-users) [:div.links.demo [:div.link-entry [:span (tr "auth.create-demo-profile") " "] diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 5ee5ebd1f9..e690b57d8b 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -116,7 +116,7 @@ [:h1 (tr "auth.register-title")] [:div.subtitle (tr "auth.register-subtitle")] - (when cf/demo-warning + (when (contains? @cf/flags :demo-warning) [:& demo-warning]) [:& register-form {:params params}] @@ -135,7 +135,7 @@ :tab-index "4"} (tr "auth.login-here")]] - (when cf/allow-demo-users + (when (contains? @cf/flags :demo-users) [:div.link-entry [:span (tr "auth.create-demo-profile") " "] [:a {:on-click #(st/emit! (du/create-demo-profile)) @@ -216,7 +216,7 @@ :label (tr "auth.terms-privacy-agreement") :type "checkbox"}]] - (when (contains? @cf/flags :show-newsletter-check-on-register-validation) + (when (contains? @cf/flags :newsletter-registration-check) [:div.fields-row [:& fm/input {:name :accept-newsletter-subscription :class "check-primary" diff --git a/frontend/src/app/main/ui/components/copy_button.cljs b/frontend/src/app/main/ui/components/copy_button.cljs index 6911ba5ae4..a23c350eb4 100644 --- a/frontend/src/app/main/ui/components/copy_button.cljs +++ b/frontend/src/app/main/ui/components/copy_button.cljs @@ -12,12 +12,14 @@ [beicon.core :as rx] [rumext.alpha :as mf])) -(mf/defc copy-button [{:keys [data]}] +(mf/defc copy-button [{:keys [data on-copied]}] (let [just-copied (mf/use-state false)] (mf/use-effect (mf/deps @just-copied) (fn [] (when @just-copied + (when (fn? on-copied) + (on-copied)) (let [sub (timers/schedule 1000 #(reset! just-copied false))] ;; On unmount we dispose the timer #(rx/-dispose sub))))) diff --git a/frontend/src/app/main/ui/components/editable_select.cljs b/frontend/src/app/main/ui/components/editable_select.cljs index 127e0e403e..d1a2e666f4 100644 --- a/frontend/src/app/main/ui/components/editable_select.cljs +++ b/frontend/src/app/main/ui/components/editable_select.cljs @@ -7,14 +7,16 @@ (ns app.main.ui.components.editable-select (:require [app.common.data :as d] + [app.common.math :as math] [app.common.uuid :as uuid] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.icons :as i] [app.util.dom :as dom] + [app.util.keyboard :as kbd] [app.util.timers :as timers] [rumext.alpha :as mf])) -(mf/defc editable-select [{:keys [value type options class on-change placeholder on-blur]}] +(mf/defc editable-select [{:keys [value type options class on-change placeholder on-blur] :as params}] (let [state (mf/use-state {:id (uuid/next) :is-open? false :current-value value @@ -22,6 +24,13 @@ :left nil :bottom nil}) + min-val (get params :min) + max-val (get params :max) + + num? (fn [val] (and (number? val) + (not (math/nan? val)) + (math/finite? val))) + emit-blur? (mf/use-ref nil) open-dropdown #(swap! state assoc :is-open? true) @@ -38,11 +47,15 @@ value->label (fn [value] (get labels-map value value)) + set-value (fn [value] + (swap! state assoc :current-value value) + (when on-change (on-change value))) + handle-change-input (fn [event] (let [value (-> event dom/get-target dom/get-value) - value (or (d/parse-integer value) value)] - (swap! state assoc :current-value value) - (when on-change (on-change value)))) + value (-> (or (d/parse-double value) value) + (math/precision 2))] + (set-value value))) on-node-load (fn [node] @@ -61,6 +74,38 @@ :top top :bottom bottom)))))) + handle-key-down + (mf/use-callback + (fn [event] + (when (= type "number") + (let [up? (kbd/up-arrow? event) + down? (kbd/down-arrow? event)] + (when (or up? down?) + (dom/prevent-default event) + (let [value (-> event dom/get-target dom/get-value) + value (-> (or (d/parse-double value) value) + (math/precision 2)) + + increment (cond + (kbd/shift? event) + (if up? 10 -10) + + (kbd/alt? event) + (if up? 0.1 -0.1) + + :else + (if up? 1 -1)) + + new-value (-> (+ value increment) + (math/precision 2)) + + new-value (cond + (and (num? min-val) (< new-value min-val)) min-val + (and (num? max-val) (> new-value max-val)) max-val + :else new-value)] + + (set-value new-value))))))) + handle-focus (mf/use-callback (fn [] @@ -89,6 +134,7 @@ :ref on-node-load} [:input.input-text {:value (or (-> @state :current-value value->label) "") :on-change handle-change-input + :on-key-down handle-key-down :on-focus handle-focus :on-blur handle-blur :placeholder placeholder diff --git a/frontend/src/app/main/ui/components/numeric_input.cljs b/frontend/src/app/main/ui/components/numeric_input.cljs index a6bbb02e3c..54154c5479 100644 --- a/frontend/src/app/main/ui/components/numeric_input.cljs +++ b/frontend/src/app/main/ui/components/numeric_input.cljs @@ -31,7 +31,7 @@ local-ref (mf/use-ref) ref (or external-ref local-ref) - value (d/parse-integer value-str) + value (d/parse-integer value-str 0) min-val (when (string? min-val-str) (d/parse-integer min-val-str)) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index ce2f8b4f53..46d50d1a80 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.comments (:require [app.main.data.comments :as dcm] + [app.main.data.events :as ev] [app.main.data.workspace.comments :as dwcm] [app.main.refs :as refs] [app.main.store :as st] @@ -15,13 +16,18 @@ [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [potok.core :as ptk] [rumext.alpha :as mf])) (mf/defc comments-section [{:keys [profile team]}] + (mf/use-effect (mf/deps team) - (st/emitf (dcm/retrieve-unread-comment-threads (:id team)))) + (fn [] + (st/emit! (dcm/retrieve-unread-comment-threads (:id team)) + (ptk/event ::ev/event {::ev/name "open-comment-notifications" + ::ev/origin "dashboard"})))) (let [show-dropdown? (mf/use-state false) show-dropdown (mf/use-fn #(reset! show-dropdown? true)) @@ -38,7 +44,8 @@ on-navigate (mf/use-callback (fn [thread] - (st/emit! (dwcm/navigate thread))))] + (st/emit! (-> (dwcm/navigate thread) + (with-meta {::ev/origin "dashboard"})))))] [:div.dashboard-comments-section [:div.button diff --git a/frontend/src/app/main/ui/dashboard/export.cljs b/frontend/src/app/main/ui/dashboard/export.cljs index e89405d3ee..68240a79ca 100644 --- a/frontend/src/app/main/ui/dashboard/export.cljs +++ b/frontend/src/app/main/ui/dashboard/export.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.export (:require [app.common.data :as d] + [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.icons :as i] @@ -14,6 +15,7 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [beicon.core :as rx] + [potok.core :as ptk] [rumext.alpha :as mf])) (def ^:const options [:all :merge :detach]) @@ -58,6 +60,10 @@ start-export (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "export-files" + :num-files (count (:files @state)) + :option @selected-option})) + (swap! state assoc :status :exporting) (->> (uw/ask-many! {:cmd :export-file @@ -117,7 +123,7 @@ (let [selected? (= @selected-option type)] [:div.export-option {:class (when selected? "selected")} [:label.option-container - [:h3 (tr (str "dashboard.export.options." (d/name type) ".title"))] + [:h3 (tr (str "dashboard.export.options." (d/name type) ".title"))] [:p (tr (str "dashboard.export.options." (d/name type) ".message"))] [:input {:type "radio" :checked selected? diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 31207a0b93..e0c03033ba 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.main.data.dashboard :as dd] + [app.main.data.events :as ev] [app.main.data.messages :as dm] [app.main.data.modal :as modal] [app.main.repo :as rp] @@ -18,6 +19,7 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] [beicon.core :as rx] + [potok.core :as ptk] [rumext.alpha :as mf])) (defn get-project-name @@ -159,6 +161,8 @@ (mf/use-callback (mf/deps files current-team-id) (fn [_] + (st/emit! (ptk/event ::ev/event {::ev/name "export-files" + :num-files (count files)})) (->> (rx/from files) (rx/flat-map (fn [file] @@ -172,13 +176,19 @@ {:type :export :team-id current-team-id :has-libraries? (->> files (some :has-libraries?)) - :files files})))))))] + :files files}))))))) + + ;; NOTE: this is used for detect if component is still mounted + mounted-ref (mf/use-ref true)] (mf/use-effect + (mf/deps show?) (fn [] - (->> (rp/query! :all-projects) - (rx/map group-by-team) - (rx/subs #(reset! teams %))))) + (when show? + (->> (rp/query! :all-projects) + (rx/map group-by-team) + (rx/subs #(when (mf/ref-val mounted-ref) + (reset! teams %))))))) (when current-team (let [sub-options (conj (vec (for [project current-projects] diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index b982d8ceda..4ce733033c 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.files (:require [app.main.data.dashboard :as dd] + [app.main.data.events :as ev] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid]] @@ -62,30 +63,34 @@ (if (:edition @local) [:& inline-edition {:content (:name project) :on-end (fn [name] - (st/emit! (dd/rename-project (assoc project :name name))) + (st/emit! (-> (dd/rename-project (assoc project :name name)) + (with-meta {::ev/origin "project"}))) (swap! local assoc :edition false))}] [:div.dashboard-title [:h1 {:on-double-click on-edit} - (:name project)] - [:& project-menu {:project project - :show? (:menu-open @local) - :left (- (:x (:menu-pos @local)) 180) - :top (:y (:menu-pos @local)) - :on-edit on-edit - :on-menu-close on-menu-close - :on-import on-import}]])) + (:name project)]])) + + [:& project-menu {:project project + :show? (:menu-open @local) + :left (- (:x (:menu-pos @local)) 180) + :top (:y (:menu-pos @local)) + :on-edit on-edit + :on-menu-close on-menu-close + :on-import on-import}] + [:div.dashboard-header-actions [:a.btn-secondary.btn-small {:on-click on-create-clicked} (tr "dashboard.new-file")] - [:div.icon.pin-icon.tooltip.tooltip-bottom - {:class (when (:is-pinned project) "active") - :on-click toggle-pin :alt (tr "dashboard.pin-unpin")} - (if (:is-pinned project) - i/pin-fill - i/pin)] + (when-not (:is-default project) + [:div.icon.pin-icon.tooltip.tooltip-bottom + {:class (when (:is-pinned project) "active") + :on-click toggle-pin :alt (tr "dashboard.pin-unpin")} + (if (:is-pinned project) + i/pin-fill + i/pin)]) - [:div.icon.tooltip.tooltip-bottom + [:div.icon.tooltip.tooltip-bottom-left {:on-click on-menu-click :alt (tr "dashboard.options")} i/actions]]])) @@ -98,12 +103,17 @@ (reverse))] (mf/use-effect - (mf/deps (:id project)) + (mf/deps project) + (fn [] + (when project + (let [pname (if (:is-default project) + (tr "labels.drafts") + (:name project))] + (dom/set-html-title (tr "title.dashboard.files" pname)))))) + + (mf/use-effect + (mf/deps project) (fn [] - (dom/set-html-title (tr "title.dashboard.files" - (if (:is-default project) - (tr "labels.drafts") - (:name project)))) (st/emit! (dd/fetch-files {:project-id (:id project)}) (dd/clear-selected-files)))) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 72d65eb27f..1b12fc298e 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -19,7 +19,6 @@ [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] [app.util.logging :as log] - ;; [app.util.router :as rt] [beicon.core :as rx] [cuerdas.core :as str] [rumext.alpha :as mf])) diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index b8b083e04b..dcc0663b5d 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.import (:require [app.common.data :as d] + [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.components.file-uploader :refer [file-uploader]] @@ -17,6 +18,7 @@ [app.util.keyboard :as kbd] [app.util.logging :as log] [beicon.core :as rx] + [potok.core :as ptk] [rumext.alpha :as mf])) (log/set-level! :debug) @@ -214,6 +216,9 @@ import-files (mf/use-callback (fn [project-id files] + (st/emit! (ptk/event ::ev/event {::ev/name "import-files" + :num-files (count files)})) + (->> (uw/ask-many! {:cmd :import-files :project-id project-id diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index 7130c97216..98ff52fae2 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -23,10 +23,11 @@ (mf/use-effect (mf/deps team) (fn [] - (dom/set-html-title (tr "title.dashboard.shared-libraries" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team)))))) + (when team + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (dom/set-html-title (tr "title.dashboard.shared-libraries" tname)))))) (mf/use-effect (st/emitf (dd/fetch-shared-files) diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index f6923cc783..b3654ff591 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -99,14 +99,18 @@ :left left :options [(when-not (:is-default project) [(tr "labels.rename") on-edit]) - [(tr "dashboard.duplicate") on-duplicate] - [(tr "dashboard.pin-unpin") toggle-pin] - (when (seq teams) + (when-not (:is-default project) + [(tr "dashboard.duplicate") on-duplicate]) + (when-not (:is-default project) + [(tr "dashboard.pin-unpin") toggle-pin]) + (when (and (seq teams) (not (:is-default project))) [(tr "dashboard.move-to") nil (for [team teams] [(:name team) (on-move (:id team))])]) (when (some? on-import) [(tr "dashboard.import") on-import-files]) - [:separator] - [(tr "labels.delete") on-delete]]}]])) + (when-not (:is-default project) + [:separator]) + (when-not (:is-default project) + [(tr "labels.delete") on-delete])]}]])) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 768599da77..7089ba7211 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.dashboard.projects (:require [app.main.data.dashboard :as dd] + [app.main.data.events :as ev] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [line-grid]] @@ -74,7 +75,8 @@ (mf/use-callback (mf/deps project) (fn [name] - (st/emit! (dd/rename-project (assoc project :name name))) + (st/emit! (-> (dd/rename-project (assoc project :name name)) + (with-meta {::ev/origin "dashboard"}))) (swap! local assoc :edition? false))) on-file-created @@ -163,14 +165,16 @@ (mf/use-effect (mf/deps team) (fn [] - (dom/set-html-title (tr "title.dashboard.projects" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team)))))) + (when team + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (dom/set-html-title (tr "title.dashboard.projects" tname)))))) (mf/use-effect - (st/emitf (dd/fetch-recent-files) - (dd/clear-selected-files))) + (fn [] + (st/emit! (dd/fetch-recent-files) + (dd/clear-selected-files)))) (when (seq projects) [:* diff --git a/frontend/src/app/main/ui/dashboard/search.cljs b/frontend/src/app/main/ui/dashboard/search.cljs index 56bc7439f0..12b6e628da 100644 --- a/frontend/src/app/main/ui/dashboard/search.cljs +++ b/frontend/src/app/main/ui/dashboard/search.cljs @@ -17,20 +17,23 @@ (mf/defc search-page [{:keys [team search-term] :as props}] - (let [result (mf/deref refs/dashboard-search-result)] - (mf/use-effect - (mf/deps team) - (fn [] - (dom/set-html-title (tr "title.dashboard.search" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team)))))) - (mf/use-effect - (mf/deps search-term) - (fn [] - (st/emit! (dd/search {:search-term search-term}) - (dd/clear-selected-files)))) + (mf/use-effect + (mf/deps team) + (fn [] + (when team + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (dom/set-html-title (tr "title.dashboard.search" tname)))))) + + (mf/use-effect + (mf/deps search-term) + (fn [] + (st/emit! (dd/search {:search-term search-term}) + (dd/clear-selected-files)))) + + (let [result (mf/deref refs/dashboard-search-result)] [:* [:header.dashboard-header [:div.dashboard-title diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 07416cb045..3f1e7cbd94 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -8,7 +8,7 @@ (:require [app.common.data :as d] [app.common.spec :as us] - [app.config :as cfg] + [app.config :as cf] [app.main.data.dashboard :as dd] [app.main.data.messages :as dm] [app.main.data.modal :as modal] @@ -214,7 +214,7 @@ [:* {:key (:id team)} [:li.team-name {:on-click (partial team-selected (:id team))} [:span.team-icon - [:img {:src (cfg/resolve-team-photo-url team)}]] + [:img {:src (cf/resolve-team-photo-url team)}]] [:span.team-text {:title (:name team)} (:name team)]]]) [:hr] @@ -358,7 +358,7 @@ [:span.team-text (tr "dashboard.default-team-name")]] [:div.team-name [:span.team-icon - [:img {:src (cfg/resolve-team-photo-url team)}]] + [:img {:src (cf/resolve-team-photo-url team)}]] [:span.team-text {:title (:name team)} (:name team)]]) [:span.switch-icon @@ -468,7 +468,7 @@ (mf/defc profile-section [{:keys [profile team] :as props}] (let [show (mf/use-state false) - photo (cfg/resolve-profile-photo-url profile) + photo (cf/resolve-profile-photo-url profile) on-click (mf/use-callback @@ -496,7 +496,7 @@ [:span.icon i/exit] [:span.text (tr "labels.logout")]] - (when cfg/feedback-enabled + (when (contains? @cf/flags :user-feedback) [:li.feedback {:on-click (partial on-click :settings-feedback)} [:span.icon i/msg-info] [:span.text (tr "labels.give-feedback")] diff --git a/frontend/src/app/main/ui/handoff.cljs b/frontend/src/app/main/ui/handoff.cljs deleted file mode 100644 index bee408f97b..0000000000 --- a/frontend/src/app/main/ui/handoff.cljs +++ /dev/null @@ -1,133 +0,0 @@ -;; 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) UXBOX Labs SL - -(ns app.main.ui.handoff - (:require - [app.main.data.viewer :as dv] - [app.main.data.viewer.shortcuts :as sc] - [app.main.refs :as refs] - [app.main.store :as st] - [app.main.ui.handoff.left-sidebar :refer [left-sidebar]] - [app.main.ui.handoff.render :refer [render-frame-svg]] - [app.main.ui.handoff.right-sidebar :refer [right-sidebar]] - [app.main.ui.hooks :as hooks] - [app.main.ui.viewer.header :refer [header]] - [app.main.ui.viewer.thumbnails :refer [thumbnails-panel]] - [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [t tr]] - [app.util.keyboard :as kbd] - [goog.events :as events] - [rumext.alpha :as mf]) - (:import goog.events.EventType)) - -(defn handle-select-frame [frame] - #(do (dom/prevent-default %) - (dom/stop-propagation %) - (st/emit! (dv/select-shape (:id frame))))) - -(mf/defc render-panel - [{:keys [data state index page-id file-id]}] - (let [locale (mf/deref i18n/locale) - frames (:frames data []) - objects (:objects data) - frame (get frames index)] - - (mf/use-effect - (mf/deps index) - (fn [] - (st/emit! (dv/set-current-frame (:id frame)) - (dv/select-shape (:id frame))))) - - [:section.viewer-preview - (cond - (empty? frames) - [:section.empty-state - [:span (t locale "viewer.empty-state")]] - - (nil? frame) - [:section.empty-state - [:span (t locale "viewer.frame-not-found")]] - - :else - [:* - [:& left-sidebar {:frame frame}] - [:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)} - [:div.handoff-svg-container - [:& render-frame-svg {:frame-id (:id frame) - :zoom (:zoom state) - :objects objects}]]] - [:& right-sidebar {:frame frame - :page-id page-id - :file-id file-id}]])])) - -(mf/defc handoff-content - [{:keys [data state index page-id file-id] :as props}] - (let [on-mouse-wheel - (mf/use-callback - (fn [event] - (when (or (kbd/ctrl? event) (kbd/meta? event)) - (dom/prevent-default event) - (let [event (.getBrowserEvent ^js event) - delta (+ (.-deltaY ^js event) - (.-deltaX ^js event))] - (if (pos? delta) - (st/emit! dv/decrease-zoom) - (st/emit! dv/increase-zoom)))))) - - on-mount - (fn [] - ;; bind with passive=false to allow the event to be cancelled - ;; https://stackoverflow.com/a/57582286/3219895 - (let [key1 (events/listen goog/global EventType.WHEEL - on-mouse-wheel #js {"passive" false})] - (fn [] - (events/unlistenByKey key1))))] - - (mf/use-effect on-mount) - (hooks/use-shortcuts ::handoff sc/shortcuts) - - [:div.handoff-layout {:class (dom/classnames :force-visible - (:show-thumbnails state))} - [:& header - {:data data - :state state - :index index - :section :handoff}] - [:div.viewer-content - (when (:show-thumbnails state) - [:& thumbnails-panel {:index index - :data data - :screen :handoff}]) - [:& render-panel {:data data - :state state - :index index - :page-id page-id - :file-id file-id}]]])) - -(mf/defc handoff - [{:keys [file-id page-id index token] :as props}] - - (mf/use-effect - (mf/deps file-id page-id token) - (fn [] - (st/emit! (dv/initialize props)))) - - (let [data (mf/deref refs/viewer-data) - state (mf/deref refs/viewer-local)] - - (mf/use-effect - (mf/deps (:file data)) - #(when (:file data) - (dom/set-html-title (tr "title.viewer" - (get-in data [:file :name]))))) - - (when (and data state) - [:& handoff-content - {:file-id file-id - :page-id page-id - :index index - :state state - :data data}]))) diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index b4b4cd7126..47738e45a2 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -124,6 +124,7 @@ (def sort-descending (icon-xref :sort-descending)) (def strikethrough (icon-xref :strikethrough)) (def stroke (icon-xref :stroke)) +(def switch (icon-xref :switch)) (def text (icon-xref :text)) (def text-align-center (icon-xref :text-align-center)) (def text-align-justify (icon-xref :text-align-justify)) diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index 92c970bd64..d25c4b0bff 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -14,37 +14,35 @@ [app.main.data.users :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.util.dom :as dom] + [app.main.ui.releases.common :as rc] + [app.main.ui.releases.v1-4] + [app.main.ui.releases.v1-5] + [app.main.ui.releases.v1-6] + [app.main.ui.releases.v1-7] + [app.main.ui.releases.v1-8] + [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] [app.util.router :as rt] [app.util.timers :as tm] [cljs.spec.alpha :as s] [rumext.alpha :as mf])) - ;; --- ONBOARDING LIGHTBOX -(mf/defc navigation-bullets - [{:keys [slide navigate total]}] - [:ul.step-dots - (for [i (range total)] - [:li {:class (dom/classnames :current (= slide i)) - :on-click #(navigate i)}])]) - (mf/defc onboarding-start [{:keys [next] :as props}] [:div.modal-container.onboarding [:div.modal-left.welcome - [:img {:src "images/login-on.jpg" :border "0" :alt "Penpot"}]] + [:img {:src "images/login-on.jpg" :border "0" :alt (tr "onboarding.welcome.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "Welcome to Penpot!"]] + [:h2 (tr "onboarding.welcome.title")]] [:span.release "Alpha version " (:main @cf/version)] [:div.modal-content - [:p "We are very happy to introduce you to the very first Alpha release."] - [:p "Penpot is still at development stage and there will be constant updates. We hope you enjoy the first stable version."]] + [:p (tr "onboarding.welcome.desc1")] + [:p (tr "onboarding.welcome.desc2")]] [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]] + [:button.btn-secondary {:on-click next} (tr "labels.continue")]]] [:img.deco {:src "images/deco-left.png" :border "0"}] [:img.deco.right {:src "images/deco-right.png" :border "0"}]]) @@ -52,15 +50,20 @@ [{:keys [next] :as props}] [:div.modal-container.onboarding.black [:div.modal-left - [:img {:src "images/open-source.svg" :border "0" :alt "Open Source"}]] + [:img {:src "images/open-source.svg" :border "0" :alt (tr "onboarding.contrib.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "Open Source Contributor?"]] + [:h2 (tr "onboarding.contrib.title")]] [:div.modal-content - [:p "Penpot is Open Source, made by and for the community. If you want to collaborate, you are more than welcome!"] - [:p "You can access the " [:a {:href "https://github.com/penpot" :target "_blank"} "project on github"] " and follow the contribution instructions :)"]] + [:p (tr "onboarding.contrib.desc1")] + [:p + (tr "onboarding.contrib.desc2.1") + "\u00A0" + [:a {:href "https://github.com/penpot" :target "_blank"} (tr "onboarding.contrib.link")] + "\u00A0" + (tr "onboarding.contrib.desc2.2")]] [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]]]) + [:button.btn-secondary {:on-click next} (tr "labels.continue")]]]]) (defmulti render-slide :slide) @@ -69,17 +72,17 @@ (mf/html [:div.modal-container.onboarding.feature [:div.modal-left - [:img {:src "images/on-design.gif" :border "0" :alt "Create designs"}]] + [:img {:src "images/on-design.gif" :border "0" :alt (tr "onboarding.slide.0.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "Design libraries, styles and components"]] + [:h2 (tr "onboarding.slide.0.title")]] [:div.modal-content - [:p "Create beautiful user interfaces in collaboration with all team members."] - [:p "Maintain consistency at scale with components, libraries and design systems."]] + [:p (tr "onboarding.slide.0.desc1")] + [:p (tr "onboarding.slide.0.desc2")]] [:div.modal-navigation - [:button.btn-secondary {:on-click #(navigate 1)} "Continue"] - [:span.skip {:on-click skip} "Skip"] - [:& navigation-bullets + [:button.btn-secondary {:on-click #(navigate 1)} (tr "labels.continue")] + [:span.skip {:on-click skip} (tr "labels.skip")] + [:& rc/navigation-bullets {:slide slide :navigate navigate :total 4}]]]])) @@ -89,17 +92,17 @@ (mf/html [:div.modal-container.onboarding.feature [:div.modal-left - [:img {:src "images/on-proto.gif" :border "0" :alt "Interactive prototypes"}]] + [:img {:src "images/on-proto.gif" :border "0" :alt (tr "onboarding.slide.1.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "Bring your designs to life with interactions"]] + [:h2 (tr "onboarding.slide.1.title")]] [:div.modal-content - [:p "Create rich interactions to mimic the product behaviour."] - [:p "Share to stakeholders, present proposals to your team and start user testing with your designs, all in one place."]] + [:p (tr "onboarding.slide.1.desc1")] + [:p (tr "onboarding.slide.1.desc2")]] [:div.modal-navigation - [:button.btn-secondary {:on-click #(navigate 2)} "Continue"] - [:span.skip {:on-click skip} "Skip"] - [:& navigation-bullets + [:button.btn-secondary {:on-click #(navigate 2)} (tr "labels.continue")] + [:span.skip {:on-click skip} (tr "labels.skip")] + [:& rc/navigation-bullets {:slide slide :navigate navigate :total 4}]]]])) @@ -109,16 +112,16 @@ (mf/html [:div.modal-container.onboarding.feature [:div.modal-left - [:img {:src "images/on-feed.gif" :border "0" :alt "Get feedback"}]] + [:img {:src "images/on-feed.gif" :border "0" :alt (tr "onboarding.slide.2.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "Get feedback, present and share your work"]] + [:h2 (tr "onboarding.slide.2.title")]] [:div.modal-content - [:p "All team members working simultaneously with real time design multiplayer and centralised comments, ideas and feedback right over the designs."]] + [:p (tr "onboarding.slide.2.desc1")]] [:div.modal-navigation - [:button.btn-secondary {:on-click #(navigate 3)} "Continue"] - [:span.skip {:on-click skip} "Skip"] - [:& navigation-bullets + [:button.btn-secondary {:on-click #(navigate 3)} (tr "labels.continue")] + [:span.skip {:on-click skip} (tr "labels.skip")] + [:& rc/navigation-bullets {:slide slide :navigate navigate :total 4}]]]])) @@ -128,16 +131,16 @@ (mf/html [:div.modal-container.onboarding.feature [:div.modal-left - [:img {:src "images/on-handoff.gif" :border "0" :alt "Handoff and lowcode"}]] + [:img {:src "images/on-handoff.gif" :border "0" :alt (tr "onboarding.slide.3.alt")}]] [:div.modal-right [:div.modal-title - [:h2 "One shared source of truth"]] + [:h2 (tr "onboarding.slide.3.title")]] [:div.modal-content - [:p "Sync the design and code of all your components and styles and get code snippets."] - [:p "Get and provide code specifications like markup (SVG, HTML) or styles (CSS, Less, Stylus…)."]] + [:p (tr "onboarding.slide.3.desc1")] + [:p (tr "onboarding.slide.3.desc2")]] [:div.modal-navigation - [:button.btn-secondary {:on-click skip} "Start"] - [:& navigation-bullets + [:button.btn-secondary {:on-click skip} (tr "labels.start")] + [:& rc/navigation-bullets {:slide slide :navigate navigate :total 4}]]]])) @@ -212,23 +215,23 @@ [:div.modal-overlay [:div.modal-container.onboarding.final.animated.fadeInUp [:div.modal-left - [:img {:src "images/onboarding-team.jpg" :border "0" :alt "Create a team"}] - [:h2 "Create a team"] - [:p "Are you working with someone? Create a team to work together on projects and share design assets."] + [:img {:src "images/onboarding-team.jpg" :border "0" :alt (tr "onboarding.team.create.title")}] + [:h2 (tr "onboarding.team.create.title")] + [:p (tr "onboarding.team.create.desc1")] [:& fm/form {:form form :on-submit on-submit} [:& fm/input {:type "text" :name :name - :label "Enter new team name"}] + :label (tr "onboarding.team.create.input-placeholder")}] [:& fm/submit-button - {:label "Create team"}]]] + {:label (tr "onboarding.team.create.button")}]]] [:div.modal-right - [:img {:src "images/onboarding-start.jpg" :border "0" :alt "Start designing"}] - [:h2 "Start designing"] - [:p "Jump right away into Penpot and start designing by your own. You will still have the chance to create teams later."] - [:button.btn-primary.btn-large {:on-click close} "Start right away"]] + [:img {:src "images/onboarding-start.jpg" :border "0" :alt (tr "onboarding.team.start.title")}] + [:h2 (tr "onboarding.team.start.title")] + [:p (tr "onboarding.team.start.desc1")] + [:button.btn-primary.btn-large {:on-click close} (tr "onboarding.team.start.button")]] [:img.deco {:src "images/deco-left.png" :border "0"}] @@ -237,8 +240,6 @@ ;;; --- RELEASE NOTES MODAL -(defmulti render-release-notes :version) - (mf/defc release-notes [{:keys [version] :as props}] (let [slide (mf/use-state :start) @@ -276,7 +277,7 @@ (reset! klass nil) (tm/dispose! sem))))) - (render-release-notes + (rc/render-release-notes {:next next :navigate navigate :finish finish @@ -289,392 +290,13 @@ ::mf/register modal/components ::mf/register-as :release-notes} [props] - (let [versions (methods render-release-notes) + (let [versions (methods rc/render-release-notes) version (obj/get props "version")] (when (contains? versions version) [:div.relnotes [:> release-notes props]]))) -(defmethod render-release-notes "0.0" +(defmethod rc/render-release-notes "0.0" [params] - (render-release-notes (assoc params :version "1.6"))) + (rc/render-release-notes (assoc params :version "1.8"))) -(defmethod render-release-notes "1.4" - [{:keys [slide klass next finish navigate version]}] - (mf/html - (case @slide - :start - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.4.0"}]] - [:div.modal-right - [:div.modal-title - [:h2 "What's new?"]] - [:span.release "Alpha version " version] - [:div.modal-content - [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] - [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.4.0 version brings."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]] - [:img.deco {:src "images/deco-left.png" :border "0"}] - [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] - - 0 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/select-files.gif" :border "0" :alt "New file selection"}]] - [:div.modal-right - [:div.modal-title - [:h2 "New file selection and open files"]] - [:div.modal-content - [:p "Now you can select files with left click and make multi-selections holding down the shift + left click."] - [:p "To open a file you just have to double click it. You can also open a file in a new tab with right click."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 1 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/manage-files.gif" :border "0" :alt "Manage files"}]] - [:div.modal-right - [:div.modal-title - [:h2 "New files/projects management"]] - [:div.modal-content - [:p "Penpot now allows to duplicate and move files and projects."] - [:p "Also, now you have an easy way to manage files and projects between teams."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 2 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/rtl.gif" :border "0" :alt "RTL support"}]] - [:div.modal-right - [:div.modal-title - [:h2 "RTL support is now available!"]] - [:div.modal-content - [:p "Diversity and inclusion is one major Penpot concern and that's why we love to give support to RTL languages, unlike in most of design tools."] - [:p "If you write in arabic, hebrew or other RTL language text direction will be automatically detected in text layers."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 3 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/blend-modes.gif" :border "0" :alt "Blend modes"}]] - [:div.modal-right - [:div.modal-title - [:h2 "New layer opacity and blend modes"]] - [:div.modal-content - [:p "Combining elements visually is an important part of the design process."] - [:p "This is why the standard blend modes and opacity level are now available for each element."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click finish} "Start!"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]]))) - -(defmethod render-release-notes "1.5" - [{:keys [slide klass next finish navigate version]}] - (mf/html - (case @slide - :start - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.5.0"}]] - [:div.modal-right - [:div.modal-title - [:h2 "What's new?"]] - [:span.release "Alpha version " version] - [:div.modal-content - [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] - [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.5.0 version brings."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]] - [:img.deco {:src "images/deco-left.png" :border "0"}] - [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] - - 0 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/path-tool.gif" :border "0" :alt "New path tool"}]] - [:div.modal-right - [:div.modal-title - [:h2 "New features for paths"]] - [:div.modal-content - [:p "Now you can select snap points on edition, add/remove nodes, merge/join/split nodes."] - [:p "The usability and performance of the paths tool has been improved too."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 3}]]]]]] - - 1 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/assets-organiz.gif" :border "0" :alt "Manage libraries"}]] - [:div.modal-right - [:div.modal-title - [:h2 "New libraries organization"]] - [:div.modal-content - [:p "Penpot now allows to group, multiselect and bulk edition of assets (components and graphics)."] - [:p "It is time to have all the libraries well organized and work more efficiently."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 3}]]]]]] - - 2 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/smart-inputs.gif" :border "0" :alt "Smart inputs"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Smart inputs"]] - [:div.modal-content - [:p "Now you can have more precision in your designs with basic math operations in inputs."] - [:p "It's easier to specify by how much you want to change a value and work with measures and distances."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click finish} "Start!"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 3}]]]]]]))) - -(defmethod render-release-notes "1.6" - [{:keys [slide klass next finish navigate version]}] - (mf/html - (case @slide - :start - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.6.0"}]] - [:div.modal-right - [:div.modal-title - [:h2 "What's new?"]] - [:span.release "Alpha version " version] - [:div.modal-content - [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] - [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.6.0 version brings."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]] - [:img.deco {:src "images/deco-left.png" :border "0"}] - [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] - - 0 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/custom-fonts.gif" :border "0" :alt "Upload/use custom fonts"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Upload/use custom fonts"]] - [:div.modal-content - [:p "From now on you can upload fonts to a Penpot team and use them across its files. This is one of the most requested features since our first release (we listen!)"] - [:p "We hope you enjoy having more typography options and our brand new font selector."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 1 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/scale-text.gif" :border "0" :alt "Interactively scale text"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Scale text layers at resizing"]] - [:div.modal-content - [:p "New main menu option “Scale text (K)” to enable scale text mode."] - [:p "Disabled by default, this tool is disabled back after being used."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 2 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/performance.gif" :border "0" :alt "Performance improvements"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Performance improvements"]] - [:div.modal-content - [:p "Penpot brings important improvements handling large files. The performance in managing files in the dashboard has also been improved."] - [:p "You should have the feeling that files and layers show up a bit faster :)"]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 3 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/shapes-to-path.gif" :border "0" :alt "Shapes to path"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Shapes to path"]] - [:div.modal-content - [:p "Now you can edit basic shapes like rectangles, circles and image containers by double clicking."] - [:p "An easy way to increase speed by working with vectors!"]] - [:div.modal-navigation - [:button.btn-secondary {:on-click finish} "Start!"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]]))) - - -(defmethod render-release-notes "1.7" - [{:keys [slide klass next finish navigate version]}] - (mf/html - (case @slide - :start - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.7"}]] - [:div.modal-right - [:div.modal-title - [:h2 "What's new?"]] - [:span.release "Alpha version " version] - [:div.modal-content - [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] - [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.7 version brings."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"]]] - [:img.deco {:src "images/deco-left.png" :border "0"}] - [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] - - 0 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/export.gif" :border "0" :alt "Export & Import"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Export and import Penpot files"]] - [:div.modal-content - [:p [:strong "Export files from the dashboard to your computer and import them from your computer to your projects."] - " This means that Penpot users can freely save and share Penpot files."] - [:p "Exported files linked to shared libraries provide - different ways to export their assets. Choose the one that - suits you better!"]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 1 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/constraints.gif" :border "0" :alt "Resizing constraints"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Resizing constraints"]] - [:div.modal-content - [:p "Constraints allow you to " [:strong "decide how layers will behave when resizing its container"] " being a group or an artboard."] - [:p "You can manually set horizontal and vertical - constraints for every layer. This is especially useful to - control how your designs look when working with responsive - components."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 2 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/group-components.gif" :border "0" :alt "Library assets management improvements"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Library assets management"]] - [:div.modal-content - [:p [:strong "Collapse/expand groups"] " at any nesting level, so you don’t have to manage their visibility individually."] - [:p "Penpot " [:strong "remembers the last library state"] ", so you don’t have to collapse a group you want hidden every time."] - [:p "Easily " [:strong "rename and ungroup"] " asset groups."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click next} "Continue"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]] - - 3 - [:div.modal-overlay - [:div.animated {:class @klass} - [:div.modal-container.onboarding.feature - [:div.modal-left - [:img {:src "images/features/copy-paste.gif" :border "0" :alt "Paste components from file to file"}]] - [:div.modal-right - [:div.modal-title - [:h2 "Paste components from file to file"]] - [:div.modal-content - [:p "Do you sometimes copy and paste component copies that belong to a library already shared by the original and destination files? From now on, those component copies are aware of this and will retain their linkage to the library."]] - [:div.modal-navigation - [:button.btn-secondary {:on-click finish} "Start!"] - [:& navigation-bullets - {:slide @slide - :navigate navigate - :total 4}]]]]]]))) diff --git a/frontend/src/app/main/ui/releases/common.cljs b/frontend/src/app/main/ui/releases/common.cljs new file mode 100644 index 0000000000..ab675091d5 --- /dev/null +++ b/frontend/src/app/main/ui/releases/common.cljs @@ -0,0 +1,19 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.common + (:require + [app.util.dom :as dom] + [rumext.alpha :as mf])) + +(defmulti render-release-notes :version) + +(mf/defc navigation-bullets + [{:keys [slide navigate total]}] + [:ul.step-dots + (for [i (range total)] + [:li {:class (dom/classnames :current (= slide i)) + :on-click #(navigate i)}])]) diff --git a/frontend/src/app/main/ui/releases/v1_4.cljs b/frontend/src/app/main/ui/releases/v1_4.cljs new file mode 100644 index 0000000000..da5bb480b4 --- /dev/null +++ b/frontend/src/app/main/ui/releases/v1_4.cljs @@ -0,0 +1,109 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.v1-4 + (:require + [app.main.ui.releases.common :as c] + [rumext.alpha :as mf])) + +(defmethod c/render-release-notes "1.4" + [{:keys [slide klass next finish navigate version]}] + (mf/html + (case @slide + :start + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.4.0"}]] + [:div.modal-right + [:div.modal-title + [:h2 "What's new?"]] + [:span.release "Alpha version " version] + [:div.modal-content + [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.4.0 version brings."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] + + 0 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/select-files.gif" :border "0" :alt "New file selection"}]] + [:div.modal-right + [:div.modal-title + [:h2 "New file selection and open files"]] + [:div.modal-content + [:p "Now you can select files with left click and make multi-selections holding down the shift + left click."] + [:p "To open a file you just have to double click it. You can also open a file in a new tab with right click."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 1 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/manage-files.gif" :border "0" :alt "Manage files"}]] + [:div.modal-right + [:div.modal-title + [:h2 "New files/projects management"]] + [:div.modal-content + [:p "Penpot now allows to duplicate and move files and projects."] + [:p "Also, now you have an easy way to manage files and projects between teams."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 2 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/rtl.gif" :border "0" :alt "RTL support"}]] + [:div.modal-right + [:div.modal-title + [:h2 "RTL support is now available!"]] + [:div.modal-content + [:p "Diversity and inclusion is one major Penpot concern and that's why we love to give support to RTL languages, unlike in most of design tools."] + [:p "If you write in arabic, hebrew or other RTL language text direction will be automatically detected in text layers."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 3 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/blend-modes.gif" :border "0" :alt "Blend modes"}]] + [:div.modal-right + [:div.modal-title + [:h2 "New layer opacity and blend modes"]] + [:div.modal-content + [:p "Combining elements visually is an important part of the design process."] + [:p "This is why the standard blend modes and opacity level are now available for each element."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click finish} "Start!"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]]))) + diff --git a/frontend/src/app/main/ui/releases/v1_5.cljs b/frontend/src/app/main/ui/releases/v1_5.cljs new file mode 100644 index 0000000000..64f83de777 --- /dev/null +++ b/frontend/src/app/main/ui/releases/v1_5.cljs @@ -0,0 +1,90 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.v1-5 + (:require + [app.main.ui.releases.common :as c] + [rumext.alpha :as mf])) + +(defmethod c/render-release-notes "1.5" + [{:keys [slide klass next finish navigate version]}] + (mf/html + (case @slide + :start + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.5.0"}]] + [:div.modal-right + [:div.modal-title + [:h2 "What's new?"]] + [:span.release "Alpha version " version] + [:div.modal-content + [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.5.0 version brings."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] + + 0 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/path-tool.gif" :border "0" :alt "New path tool"}]] + [:div.modal-right + [:div.modal-title + [:h2 "New features for paths"]] + [:div.modal-content + [:p "Now you can select snap points on edition, add/remove nodes, merge/join/split nodes."] + [:p "The usability and performance of the paths tool has been improved too."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 3}]]]]]] + + 1 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/assets-organiz.gif" :border "0" :alt "Manage libraries"}]] + [:div.modal-right + [:div.modal-title + [:h2 "New libraries organization"]] + [:div.modal-content + [:p "Penpot now allows to group, multiselect and bulk edition of assets (components and graphics)."] + [:p "It is time to have all the libraries well organized and work more efficiently."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 3}]]]]]] + + 2 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/smart-inputs.gif" :border "0" :alt "Smart inputs"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Smart inputs"]] + [:div.modal-content + [:p "Now you can have more precision in your designs with basic math operations in inputs."] + [:p "It's easier to specify by how much you want to change a value and work with measures and distances."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click finish} "Start!"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 3}]]]]]]))) + diff --git a/frontend/src/app/main/ui/releases/v1_6.cljs b/frontend/src/app/main/ui/releases/v1_6.cljs new file mode 100644 index 0000000000..6402875d58 --- /dev/null +++ b/frontend/src/app/main/ui/releases/v1_6.cljs @@ -0,0 +1,108 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.v1-6 + (:require + [app.main.ui.releases.common :as c] + [rumext.alpha :as mf])) + +(defmethod c/render-release-notes "1.6" + [{:keys [slide klass next finish navigate version]}] + (mf/html + (case @slide + :start + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.6.0"}]] + [:div.modal-right + [:div.modal-title + [:h2 "What's new?"]] + [:span.release "Alpha version " version] + [:div.modal-content + [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.6.0 version brings."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] + + 0 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/custom-fonts.gif" :border "0" :alt "Upload/use custom fonts"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Upload/use custom fonts"]] + [:div.modal-content + [:p "From now on you can upload fonts to a Penpot team and use them across its files. This is one of the most requested features since our first release (we listen!)"] + [:p "We hope you enjoy having more typography options and our brand new font selector."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 1 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/scale-text.gif" :border "0" :alt "Interactively scale text"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Scale text layers at resizing"]] + [:div.modal-content + [:p "New main menu option “Scale text (K)” to enable scale text mode."] + [:p "Disabled by default, this tool is disabled back after being used."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 2 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/performance.gif" :border "0" :alt "Performance improvements"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Performance improvements"]] + [:div.modal-content + [:p "Penpot brings important improvements handling large files. The performance in managing files in the dashboard has also been improved."] + [:p "You should have the feeling that files and layers show up a bit faster :)"]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 3 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/shapes-to-path.gif" :border "0" :alt "Shapes to path"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Shapes to path"]] + [:div.modal-content + [:p "Now you can edit basic shapes like rectangles, circles and image containers by double clicking."] + [:p "An easy way to increase speed by working with vectors!"]] + [:div.modal-navigation + [:button.btn-secondary {:on-click finish} "Start!"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]]))) diff --git a/frontend/src/app/main/ui/releases/v1_7.cljs b/frontend/src/app/main/ui/releases/v1_7.cljs new file mode 100644 index 0000000000..4a61adc388 --- /dev/null +++ b/frontend/src/app/main/ui/releases/v1_7.cljs @@ -0,0 +1,114 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.v1-7 + (:require + [app.main.ui.releases.common :as c] + [rumext.alpha :as mf])) + +(defmethod c/render-release-notes "1.7" + [{:keys [slide klass next finish navigate version]}] + (mf/html + (case @slide + :start + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.7"}]] + [:div.modal-right + [:div.modal-title + [:h2 "What's new?"]] + [:span.release "Alpha version " version] + [:div.modal-content + [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.7 version brings."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] + + 0 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/export.gif" :border "0" :alt "Export & Import"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Export and import Penpot files"]] + [:div.modal-content + [:p [:strong "Export files from the dashboard to your computer and import them from your computer to your projects."] + " This means that Penpot users can freely save and share Penpot files."] + [:p "Exported files linked to shared libraries provide + different ways to export their assets. Choose the one that + suits you better!"]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 1 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/constraints.gif" :border "0" :alt "Resizing constraints"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Resizing constraints"]] + [:div.modal-content + [:p "Constraints allow you to " [:strong "decide how layers will behave when resizing its container"] " being a group or an artboard."] + [:p "You can manually set horizontal and vertical + constraints for every layer. This is especially useful to + control how your designs look when working with responsive + components."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 2 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/group-components.gif" :border "0" :alt "Library assets management improvements"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Library assets management"]] + [:div.modal-content + [:p [:strong "Collapse/expand groups"] " at any nesting level, so you don’t have to manage their visibility individually."] + [:p "Penpot " [:strong "remembers the last library state"] ", so you don’t have to collapse a group you want hidden every time."] + [:p "Easily " [:strong "rename and ungroup"] " asset groups."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 3 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/copy-paste.gif" :border "0" :alt "Paste components from file to file"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Paste components from file to file"]] + [:div.modal-content + [:p "Do you sometimes copy and paste component copies that belong to a library already shared by the original and destination files? From now on, those component copies are aware of this and will retain their linkage to the library."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click finish} "Start!"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]]))) diff --git a/frontend/src/app/main/ui/releases/v1_8.cljs b/frontend/src/app/main/ui/releases/v1_8.cljs new file mode 100644 index 0000000000..7d88f71c1c --- /dev/null +++ b/frontend/src/app/main/ui/releases/v1_8.cljs @@ -0,0 +1,108 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.releases.v1-8 + (:require + [app.main.ui.releases.common :as c] + [rumext.alpha :as mf])) + +(defmethod c/render-release-notes "1.8" + [{:keys [slide klass next finish navigate version]}] + (mf/html + (case @slide + :start + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/login-on.jpg" :border "0" :alt "What's new Alpha release 1.8"}]] + [:div.modal-right + [:div.modal-title + [:h2 "What's new?"]] + [:span.release "Alpha version " version] + [:div.modal-content + [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "We are happy to show you a sneak peak of the most important stuff that the Alpha 1.8 version brings."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]]] + + 0 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/share-viewer.gif" :border "0" :alt "Share options and pages at view mode"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Share options and pages at view mode"]] + [:div.modal-content + [:p "Now you can navigate through prototype pages of the same file at the view mode."] + [:p "You can also create a shareable link deciding which pages will be available for the visitors. Sharing is caring!"]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 1 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/stroke-caps.gif" :border "0" :alt "Path stroke caps"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Path stroke caps"]] + [:div.modal-content + [:p "Ever needed an arrow to point something? Style the ends of any open paths."] + [:p "You can select different styles for each end of an open path: arrows, square, circle, diamond or just a round ending are the available options."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 2 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/navigate-history.gif" :border "0" :alt "Navigable history"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Navigable history"]] + [:div.modal-content + [:p "Click on a change of the history of a file to get the file to this very point without ctrl+z all the way."] + [:p "Quick and easy :)"]] + [:div.modal-navigation + [:button.btn-secondary {:on-click next} "Continue"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]] + + 3 + [:div.modal-overlay + [:div.animated {:class @klass} + [:div.modal-container.onboarding.feature + [:div.modal-left + [:img {:src "images/features/export-artboards.gif" :border "0" :alt "Export artboards PDF"}]] + [:div.modal-right + [:div.modal-title + [:h2 "Export artboards PDF"]] + [:div.modal-content + [:p "If you have a presentation made at Penpot you might want to create a document that can be shared with anyone, regardless of having a Penpot account, or just to be able to use your presentation offline (essential for talks and classes)."] + [:p "Now you can easily export all the artboards of a page to a single pdf file."]] + [:div.modal-navigation + [:button.btn-secondary {:on-click finish} "Start!"] + [:& c/navigation-bullets + {:slide @slide + :navigate navigate + :total 4}]]]]]]))) diff --git a/frontend/src/app/main/ui/render.cljs b/frontend/src/app/main/ui/render.cljs index 1a7ca192fb..9cea9c60a6 100644 --- a/frontend/src/app/main/ui/render.cljs +++ b/frontend/src/app/main/ui/render.cljs @@ -17,6 +17,7 @@ [app.main.repo :as repo] [app.main.store :as st] [app.main.ui.shapes.embed :as embed] + [app.main.ui.shapes.export :as ed] [app.main.ui.shapes.filters :as filters] [app.main.ui.shapes.shape :refer [shape-container]] [app.util.dom :as dom] @@ -32,6 +33,8 @@ (:id object) (:frame-id object)) + include-metadata? (mf/use-ctx ed/include-metadata-ctx) + modifier (-> (gpt/point (:x object) (:y object)) (gpt/negate) (gmt/translate-matrix)) @@ -52,7 +55,12 @@ width (* width zoom) height (* height zoom) - vbox (str/join " " [x y width height]) + padding (* (filters/calculate-padding object) zoom) + + vbox (str/join " " [(- x padding) + (- y padding) + (+ width padding padding) + (+ height padding padding)]) frame-wrapper (mf/use-memo @@ -72,18 +80,21 @@ (mf/use-effect (mf/deps width height) - #(dom/set-page-style {:size (str (mth/round width) "px " - (mth/round height) "px")})) + #(dom/set-page-style {:size (str (mth/ceil (+ width padding padding)) "px " + (mth/ceil (+ height padding padding)) "px")})) [:& (mf/provider embed/context) {:value true} [:svg {:id "screenshot" :view-box vbox - :width width - :height height + :width (+ width padding padding) + :height (+ height padding padding) :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns "http://www.w3.org/2000/svg" - :xmlns:penpot "https://penpot.app/xmlns"} + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") + ;; Fix Chromium bug about color of html texts + ;; https://bugs.chromium.org/p/chromium/issues/detail?id=1244560#c5 + :style {:-webkit-print-color-adjust :exact}} (case (:type object) :frame [:& frame-wrapper {:shape object :view-box vbox}] diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 7c9cd788ff..6adc821c29 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -20,12 +20,9 @@ (mf/defc header {::mf/wrap [mf/memo]} [] - (let [logout (constantly nil)] - [:header.dashboard-header - [:div.dashboard-title - [:h1 (tr "dashboard.your-account-title")]] - [:a.btn-secondary.btn-small {:on-click logout} - (tr "labels.logout")]])) + [:header.dashboard-header + [:div.dashboard-title + [:h1 (tr "dashboard.your-account-title")]]]) (mf/defc settings [{:keys [route] :as props}] diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 5ea32034f3..7c538fbffa 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -95,16 +95,17 @@ {:type :info :content (tr "modals.change-email.info" (:email profile))}] - [:div.fields-row - [:& fm/input {:type "text" - :name :email-1 - :label (tr "modals.change-email.new-email") - :trim true}]] - [:div.fields-row - [:& fm/input {:type "text" - :name :email-2 - :label (tr "modals.change-email.confirm-email") - :trim true}]]] + [:div.fields-container + [:div.fields-row + [:& fm/input {:type "email" + :name :email-1 + :label (tr "modals.change-email.new-email") + :trim true}]] + [:div.fields-row + [:& fm/input {:type "email" + :name :email-2 + :label (tr "modals.change-email.confirm-email") + :trim true}]]]] [:div.modal-footer [:div.action-buttons diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index ed5347091a..aa3ec05ec9 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.settings.sidebar (:require [app.config :as cf] + [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.users :as du] [app.main.store :as st] @@ -14,6 +15,7 @@ [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] + [potok.core :as ptk] [rumext.alpha :as mf])) (mf/defc sidebar-content @@ -52,6 +54,7 @@ (mf/use-callback (fn [event] (let [version (:main @cf/version)] + (st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version})) (if (and (.-ctrlKey ^js event) (.-altKey ^js event)) (st/emit! (modal/show {:type :onboarding})) @@ -87,7 +90,7 @@ i/pencil [:span.element-title (tr "labels.release-notes")]] - (when cf/feedback-enabled + (when (contains? @cf/flags :user-feedback) [:li {:class (when feedback? "current") :on-click go-settings-feedback} i/msg-info diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index dcb26a3695..973a155a4b 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.shapes.attrs (:require + [app.common.pages.spec :as spec] [app.main.ui.context :as muc] [app.util.object :as obj] [app.util.svg :as usvg] @@ -117,7 +118,31 @@ (assoc :strokeOpacity (:stroke-opacity shape nil)) (not= stroke-style :svg) - (assoc :strokeDasharray (stroke-type->dasharray stroke-style)))] + (assoc :strokeDasharray (stroke-type->dasharray stroke-style)) + + ;; For simple line caps we use svg stroke-line-cap attribute. This + ;; only works if all caps are the same and we are not using the tricks + ;; for inner or outer strokes. + (and (spec/stroke-caps-line (:stroke-cap-start shape)) + (= (:stroke-cap-start shape) (:stroke-cap-end shape)) + (not (#{:inner :outer} (:stroke-alignment shape)))) + (assoc :strokeLinecap (:stroke-cap-start shape)) + + ;; For other cap types we use markers. + (and (or (spec/stroke-caps-marker (:stroke-cap-start shape)) + (and (spec/stroke-caps-line (:stroke-cap-start shape)) + (not= (:stroke-cap-start shape) (:stroke-cap-end shape)))) + (not (#{:inner :outer} (:stroke-alignment shape)))) + (assoc :markerStart + (str/format "url(#marker-%s-%s)" render-id (name (:stroke-cap-start shape)))) + + (and (or (spec/stroke-caps-marker (:stroke-cap-end shape)) + (and (spec/stroke-caps-line (:stroke-cap-end shape)) + (not= (:stroke-cap-start shape) (:stroke-cap-end shape)))) + (not (#{:inner :outer} (:stroke-alignment shape)))) + (assoc :markerEnd + (str/format "url(#marker-%s-%s)" render-id (name (:stroke-cap-end shape)))))] + (obj/merge! attrs (clj->js stroke-attrs))) attrs))) @@ -134,7 +159,8 @@ id)) svg-attrs (-> svg-attrs (usvg/clean-attrs) - (usvg/update-attr-ids replace-id)) + (usvg/update-attr-ids replace-id) + (dissoc :id)) attrs (-> svg-attrs (dissoc :style) (clj->js)) styles (-> svg-attrs (:style {}) (clj->js))] diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 741494946c..6a5f92dcbf 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -42,6 +42,107 @@ [:use {:xlinkHref (str "#" shape-id) :style #js {:fill "black"}}]])) +(mf/defc cap-markers + [{:keys [shape render-id]}] + (let [marker-id-prefix (str "marker-" render-id) + cap-start (:stroke-cap-start shape) + cap-end (:stroke-cap-end shape) + + stroke-color (if (:stroke-color-gradient shape) + (str/format "url(#%s)" (str "stroke-color-gradient_" render-id)) + (:stroke-color shape)) + + stroke-opacity (when-not (:stroke-color-gradient shape) + (:stroke-opacity shape))] + [:* + (when (or (= cap-start :line-arrow) (= cap-end :line-arrow)) + [:marker {:id (str marker-id-prefix "-line-arrow") + :viewBox "0 0 3 6" + :refX "2" + :refY "3" + :markerWidth "3" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:path {:d "M 0.5 0.5 L 3 3 L 0.5 5.5 L 0 5 L 2 3 L 0 1 z"}]]) + + (when (or (= cap-start :triangle-arrow) (= cap-end :triangle-arrow)) + [:marker {:id (str marker-id-prefix "-triangle-arrow") + :viewBox "0 0 3 6" + :refX "2" + :refY "3" + :markerWidth "3" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:path {:d "M 0 0 L 3 3 L 0 6 z"}]]) + + (when (or (= cap-start :square-marker) (= cap-end :square-marker)) + [:marker {:id (str marker-id-prefix "-square-marker") + :viewBox "0 0 6 6" + :refX "5" + :refY "3" + :markerWidth "6" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:rect {:x 0 :y 0 :width 6 :height 6}]]) + + (when (or (= cap-start :circle-marker) (= cap-end :circle-marker)) + [:marker {:id (str marker-id-prefix "-circle-marker") + :viewBox "0 0 6 6" + :refX "5" + :refY "3" + :markerWidth "6" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:circle {:cx "3" :cy "3" :r "3"}]]) + + (when (or (= cap-start :diamond-marker) (= cap-end :diamond-marker)) + [:marker {:id (str marker-id-prefix "-diamond-marker") + :viewBox "0 0 6 6" + :refX "5" + :refY "3" + :markerWidth "6" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:path {:d "M 3 0 L 6 3 L 3 6 L 0 3 z"}]]) + + ;; If the user wants line caps but different in each end, + ;; simulate it with markers. + (when (and (or (= cap-start :round) (= cap-end :round)) + (not= cap-start cap-end)) + [:marker {:id (str marker-id-prefix "-round") + :viewBox "0 0 6 6" + :refX "3" + :refY "3" + :markerWidth "6" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:path {:d "M 3 2.5 A 0.5 0.5 0 0 1 3 3.5 "}]]) + + (when (and (or (= cap-start :square) (= cap-end :square)) + (not= cap-start cap-end)) + [:marker {:id (str marker-id-prefix "-square") + :viewBox "0 0 6 6" + :refX "3" + :refY "3" + :markerWidth "6" + :markerHeight "6" + :orient "auto-start-reverse" + :fill stroke-color + :fillOpacity stroke-opacity} + [:rect {:x 3 :y 2.5 :width 0.5 :height 1}]])])) + (mf/defc stroke-defs [{:keys [shape render-id]}] (cond @@ -53,7 +154,13 @@ (and (= :outer (:stroke-alignment shape :center)) (> (:stroke-width shape 0) 0)) [:& outer-stroke-mask {:shape shape - :render-id render-id}])) + :render-id render-id}] + + (and (or (some? (:stroke-cap-start shape)) + (some? (:stroke-cap-end shape))) + (= (:stroke-alignment shape) :center)) + [:& cap-markers {:shape shape + :render-id render-id}])) ;; Outer alingmnent: display the shape in two layers. One ;; without stroke (only fill), and another one only with stroke diff --git a/frontend/src/app/main/ui/shapes/embed.cljs b/frontend/src/app/main/ui/shapes/embed.cljs index 25d00243d9..1f68376cb8 100644 --- a/frontend/src/app/main/ui/shapes/embed.cljs +++ b/frontend/src/app/main/ui/shapes/embed.cljs @@ -12,7 +12,7 @@ [rumext.alpha :as mf])) (def context (mf/create-context false)) - + (defn use-data-uris [urls] (let [embed? (mf/use-ctx context) urls (hooks/use-equal-memo urls) diff --git a/frontend/src/app/main/ui/shapes/export.cljs b/frontend/src/app/main/ui/shapes/export.cljs index b8fa606f32..7f1d3bcb93 100644 --- a/frontend/src/app/main/ui/shapes/export.cljs +++ b/frontend/src/app/main/ui/shapes/export.cljs @@ -14,6 +14,8 @@ [cuerdas.core :as str] [rumext.alpha :as mf])) +(def include-metadata-ctx (mf/create-context false)) + (mf/defc render-xml [{{:keys [tag attrs content] :as node} :xml}] @@ -60,6 +62,7 @@ group? (= :group (:type shape)) rect? (= :rect (:type shape)) text? (= :text (:type shape)) + path? (= :path (:type shape)) mask? (and group? (:masked-group? shape)) center (gsh/center-shape shape)] (-> props @@ -90,6 +93,10 @@ (add! :r3) (add! :r4))) + (cond-> path? + (-> (add! :stroke-cap-start) + (add! :stroke-cap-end))) + (cond-> text? (-> (add! :grow-type) (add! :content (comp json/encode uuid->string)))) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index c2664d695c..25af34796f 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -201,6 +201,19 @@ :width (- x2 x1) :height (- y2 y1)}))))) +(defn calculate-padding [shape] + (let [{:keys [stroke-style stroke-alignment stroke-width]} shape] + (cond + (and (not= stroke-style :none) + (= stroke-alignment :outer)) + stroke-width + + (and (not= stroke-style :none) + (= stroke-alignment :center)) + (mth/ceil (/ stroke-width 2)) + + :else 0))) + (mf/defc filters [{:keys [filter-id shape]}] @@ -209,15 +222,17 @@ ;; Adds the previous filter as `filter-in` parameter filters (map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters))) - bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))] + bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0)) + + padding (calculate-padding shape)] [:* (when (> (count filters) 2) [:filter {:id filter-id - :x (:x bounds) - :y (:y bounds) - :width (:width bounds) - :height (:height bounds) + :x (- (:x bounds) padding) + :y (- (:y bounds) padding) + :width (+ (:width bounds) (* 2 padding)) + :height (+ (:height bounds) (* 2 padding)) :filterUnits "userSpaceOnUse" :color-interpolation-filters "sRGB"} diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index 0b8d294774..eb26eb5a49 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -38,6 +38,8 @@ frame? (= :frame type) group? (= :group type) + include-metadata? (mf/use-ctx ed/include-metadata-ctx) + wrapper-props (-> (obj/clone props) (obj/without ["shape" "children"]) @@ -53,9 +55,11 @@ (obj/set! "y" y) (obj/set! "width" width) (obj/set! "height" height) - (obj/set! "xmlnsXlink" "http://www.w3.org/1999/xlink") (obj/set! "xmlns" "http://www.w3.org/2000/svg") - (obj/set! "xmlns:penpot" "https://penpot.app/xmlns"))) + (obj/set! "xmlnsXlink" "http://www.w3.org/1999/xlink") + (cond-> + include-metadata? + (obj/set! "xmlns:penpot" "https://penpot.app/xmlns")))) wrapper-props (cond-> wrapper-props @@ -66,7 +70,8 @@ [:& (mf/provider muc/render-ctx) {:value render-id} [:> wrapper-tag wrapper-props - [:& ed/export-data {:shape shape}] + (when include-metadata? + [:& ed/export-data {:shape shape}]) [:defs [:& defs/svg-defs {:shape shape :render-id render-id}] [:& filters/filters {:shape shape :filter-id filter-id}] diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index ec76c8f610..2dca984ef2 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -16,8 +16,9 @@ (defn generate-root-styles [shape node] (let [valign (:vertical-align node "top") + width (some-> (:width shape) (+ 1)) base #js {:height (or (:height shape) "100%") - :width (or (:width shape) "100%")}] + :width (or width "100%")}] (cond-> base (= valign "top") (obj/set! "justifyContent" "flex-start") (= valign "center") (obj/set! "justifyContent" "center") diff --git a/frontend/src/app/main/ui/share_link.cljs b/frontend/src/app/main/ui/share_link.cljs new file mode 100644 index 0000000000..b9a84565b9 --- /dev/null +++ b/frontend/src/app/main/ui/share_link.cljs @@ -0,0 +1,232 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.share-link + (:require + [app.common.data :as d] + [app.config :as cf] + [app.main.data.common :as dc] + [app.main.data.messages :as dm] + [app.main.data.modal :as modal] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.logging :as log] + [app.util.router :as rt] + [app.util.webapi :as wapi] + [rumext.alpha :as mf])) + +(log/set-level! :debug) + +(defn prepare-params + [{:keys [sections pages pages-mode]}] + {:pages pages + :flags (-> #{} + (into (map #(str "section-" %)) sections) + (into (map #(str "pages-" %)) [pages-mode]))}) + +(mf/defc share-link-dialog + {::mf/register modal/components + ::mf/register-as :share-link} + [{:keys [file page]}] + (let [slinks (mf/deref refs/share-links) + router (mf/deref refs/router) + route (mf/deref refs/route) + + link (mf/use-state nil) + confirm (mf/use-state false) + + opts (mf/use-state + {:sections #{"viewer"} + :pages-mode "current" + :pages #{(:id page)}}) + + close + (fn [event] + (dom/prevent-default event) + (st/emit! (modal/hide))) + + select-pages-mode + (fn [mode] + (reset! confirm false) + (swap! opts + (fn [state] + (-> state + (assoc :pages-mode mode) + (cond-> (= mode "current") (assoc :pages #{(:id page)})) + (cond-> (= mode "all") (assoc :pages (into #{} (get-in file [:data :pages])))))))) + + mark-checked-page + (fn [event id] + (let [target (dom/get-target event) + checked? (.-checked ^js target)] + (reset! confirm false) + (swap! opts update :pages + (fn [pages] + (if checked? + (conj pages id) + (disj pages id)))))) + + create-link + (fn [_] + (let [params (prepare-params @opts) + params (assoc params :file-id (:id file))] + (st/emit! (dc/create-share-link params)))) + + copy-link + (fn [_] + (wapi/write-to-clipboard @link) + (st/emit! (dm/show {:type :info + :content (tr "common.share-link.link-copied-success") + :timeout 3000}))) + + try-delete-link + (fn [_] + (reset! confirm true)) + + delete-link + (fn [_] + (let [params (prepare-params @opts) + slink (d/seek #(= (:flags %) (:flags params)) slinks)] + (reset! confirm false) + (st/emit! (dc/delete-share-link slink) + (dm/show {:type :info + :content (tr "common.share-link.link-deleted-success") + :timeout 3000})))) + ] + + (mf/use-effect + (mf/deps file slinks @opts) + (fn [] + (let [{:keys [flags pages] :as params} (prepare-params @opts) + slink (d/seek #(and (= (:flags %) flags) (= (:pages %) pages)) slinks) + href (when slink + (let [pparams (:path-params route) + qparams (-> (:query-params route) + (assoc :share-id (:id slink)) + (assoc :index "0")) + href (rt/resolve router :viewer pparams qparams)] + (assoc cf/public-uri :fragment href)))] + (reset! link (some-> href str))))) + + [:div.modal-overlay + [:div.modal-container.share-link-dialog + [:div.modal-content + [:div.title + [:h2 (tr "common.share-link.title")] + [:div.modal-close-button + {:on-click close + :title (tr "labels.close")} + i/close]] + + [:div.share-link-section + [:label (tr "labels.link")] + [:div.custom-input.with-icon + [:input {:type "text" :value (or @link "") :read-only true}] + [:div.help-icon {:title (tr "labels.copy") + :on-click copy-link} + i/copy]] + + [:div.hint (tr "common.share-link.permissions-hint")]]] + + [:div.modal-content + (let [sections (:sections @opts)] + [:div.access-mode + [:div.title (tr "common.share-link.permissions-can-access")] + [:div.items + [:div.input-checkbox.check-primary.disabled + [:input.check-primary.input-checkbox {:type "checkbox" :disabled true}] + [:label (tr "labels.workspace")]] + + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :default-checked (contains? sections "viewer")}] + [:label (tr "labels.viewer") + [:span.hint "(" (tr "labels.default") ")"]]] + + ;; [:div.input-checkbox.check-primary + ;; [:input.check-primary.input-checkbox {:type "checkbox"}] + ;; [:label "Handsoff" ]] + ]]) + + (let [mode (:pages-mode @opts)] + [:* + [:div.view-mode + [:div.title (tr "common.share-link.permissions-can-view")] + [:div.items + [:div.input-radio.radio-primary + [:input {:type "radio" + :id "view-all" + :checked (= "all" mode) + :name "pages-mode" + :on-change #(select-pages-mode "all")}] + [:label {:for "view-all"} (tr "common.share-link.view-all-pages")]] + + [:div.input-radio.radio-primary + [:input {:type "radio" + :id "view-current" + :name "pages-mode" + :checked (= "current" mode) + :on-change #(select-pages-mode "current")}] + [:label {:for "view-current"} (tr "common.share-link.view-current-page")]] + + [:div.input-radio.radio-primary + [:input {:type "radio" + :id "view-selected" + :name "pages-mode" + :checked (= "selected" mode) + :on-change #(select-pages-mode "selected")}] + [:label {:for "view-selected"} (tr "common.share-link.view-selected-pages")]]]] + + (when (= "selected" mode) + (let [pages (->> (get-in file [:data :pages]) + (map #(get-in file [:data :pages-index %]))) + selected (:pages @opts)] + [:ul.pages-selection + (for [page pages] + [:li.input-checkbox.check-primary {:key (str (:id page))} + [:input {:type "checkbox" + :id (str "page-" (:id page)) + :on-change #(mark-checked-page % (:id page)) + :checked (contains? selected (:id page))}] + [:label {:for (str "page-" (:id page))} (:name page)]])]))])] + + [:div.modal-footer + (cond + (true? @confirm) + [:div.confirm-dialog + [:div.description (tr "common.share-link.confirm-deletion-link-description")] + [:div.actions + [:input.btn-secondary + {:type "button" + :on-click #(reset! confirm false) + :value (tr "labels.cancel")}] + [:input.btn-warning + {:type "button" + :on-click delete-link + :value (tr "common.share-link.remove-link") + }]]] + + (some? @link) + [:input.btn-secondary + {:type "button" + :class "primary" + :on-click try-delete-link + :value (tr "common.share-link.remove-link")}] + + :else + [:input.btn-primary + {:type "button" + :class "primary" + :on-click create-link + :value (tr "common.share-link.get-link")}])] + + ]])) + + + diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index aec0d11bc1..6f630ce14c 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -12,82 +12,68 @@ [app.main.store :as st] [app.main.ui.icons :as i] [app.util.i18n :refer [tr]] + [app.util.object :as obj] [app.util.router :as rt] [rumext.alpha :as mf])) -(defn- go-to-dashboard - [profile] - (let [team-id (du/get-current-team-id profile)] - (st/emit! (rt/nav :dashboard-projects {:team-id team-id})))) +(mf/defc static-header + {::mf/wrap-props false} + [props] + (let [children (obj/get props "children") + on-click (mf/use-callback + (fn [] + (let [profile (deref refs/profile)] + (if (du/is-authenticated? profile) + (let [team-id (du/get-current-team-id profile)] + (st/emit! (rt/nav :dashboard-projects {:team-id team-id}))) + (st/emit! (rt/nav :auth-login {}))))))] + + [:section.exception-layout + [:div.exception-header + {:on-click on-click} + i/logo] + [:div.exception-content + [:div.container children]]])) (mf/defc not-found [] - (let [profile (mf/deref refs/profile)] - [:section.exception-layout - [:div.exception-header - {:on-click (partial go-to-dashboard profile)} - i/logo] - [:div.exception-content - [:div.container - [:div.image i/icon-empty] - [:div.main-message (tr "labels.not-found.main-message")] - [:div.desc-message (tr "labels.not-found.desc-message")] - [:div.sign-info - [:span (tr "labels.not-found.auth-info") " " [:b (:email profile)]] - [:a.btn-primary.btn-small - {:on-click (st/emitf (du/logout))} - (tr "labels.sign-out")]]]]])) + [:> static-header {} + [:div.image i/icon-empty] + [:div.main-message (tr "labels.not-found.main-message")] + [:div.desc-message (tr "labels.not-found.desc-message")]]) (mf/defc bad-gateway [] - (let [profile (mf/deref refs/profile)] - [:section.exception-layout - [:div.exception-header - {:on-click (partial go-to-dashboard profile)} - i/logo] - [:div.exception-content - [:div.container - [:div.image i/icon-empty] - [:div.main-message (tr "labels.bad-gateway.main-message")] - [:div.desc-message (tr "labels.bad-gateway.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small - {:on-click (st/emitf #(dissoc % :exception))} - (tr "labels.retry")]]]]])) + [:> static-header {} + [:div.image i/icon-empty] + [:div.main-message (tr "labels.bad-gateway.main-message")] + [:div.desc-message (tr "labels.bad-gateway.desc-message")] + [:div.sign-info + [:a.btn-primary.btn-small + {:on-click (st/emitf #(dissoc % :exception))} + (tr "labels.retry")]]]) (mf/defc service-unavailable [] - (let [profile (mf/deref refs/profile)] - [:section.exception-layout - [:div.exception-header - {:on-click (partial go-to-dashboard profile)} - i/logo] - [:div.exception-content - [:div.container - [:div.image i/icon-empty] - [:div.main-message (tr "labels.service-unavailable.main-message")] - [:div.desc-message (tr "labels.service-unavailable.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small - {:on-click (st/emitf #(dissoc % :exception))} - (tr "labels.retry")]]]]])) + [:> static-header {} + [:div.image i/icon-empty] + [:div.main-message (tr "labels.service-unavailable.main-message")] + [:div.desc-message (tr "labels.service-unavailable.desc-message")] + [:div.sign-info + [:a.btn-primary.btn-small + {:on-click (st/emitf #(dissoc % :exception))} + (tr "labels.retry")]]]) (mf/defc internal-error [] - (let [profile (mf/deref refs/profile)] - [:section.exception-layout - [:div.exception-header - {:on-click (partial go-to-dashboard profile)} - i/logo] - [:div.exception-content - [:div.container - [:div.image i/icon-empty] - [:div.main-message (tr "labels.internal-error.main-message")] - [:div.desc-message (tr "labels.internal-error.desc-message")] - [:div.sign-info - [:a.btn-primary.btn-small - {:on-click (st/emitf (dm/assign-exception nil))} - (tr "labels.retry")]]]]])) + [:> static-header {} + [:div.image i/icon-empty] + [:div.main-message (tr "labels.internal-error.main-message")] + [:div.desc-message (tr "labels.internal-error.desc-message")] + [:div.sign-info + [:a.btn-primary.btn-small + {:on-click (st/emitf (dm/assign-exception nil))} + (tr "labels.retry")]]]) (mf/defc exception-page [{:keys [data] :as props}] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 212301613e..c3b54d8bb3 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -6,276 +6,157 @@ (ns app.main.ui.viewer (:require - [app.common.data :as d] - [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt] - [app.common.geom.shapes :as geom] - [app.common.pages :as cp] [app.main.data.comments :as dcm] [app.main.data.viewer :as dv] [app.main.data.viewer.shortcuts :as sc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.comments :as cmt] [app.main.ui.hooks :as hooks] + [app.main.ui.icons :as i] + [app.main.ui.share-link] + [app.main.ui.static :as static] + [app.main.ui.viewer.comments :refer [comments-layer]] + [app.main.ui.viewer.handoff :as handoff] [app.main.ui.viewer.header :refer [header]] - [app.main.ui.viewer.shapes :as shapes] + [app.main.ui.viewer.interactions :as interactions] [app.main.ui.viewer.thumbnails :refer [thumbnails-panel]] [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [t tr]] - [app.util.keyboard :as kbd] + [app.util.i18n :as i18n :refer [tr]] [goog.events :as events] - [okulary.core :as l] [rumext.alpha :as mf])) -(defn- frame-contains? - [{:keys [x y width height]} {px :x py :y}] - (let [x2 (+ x width) - y2 (+ y height)] - (and (<= x px x2) - (<= y py y2)))) +(defn- calculate-size + [frame zoom] + {:width (* (:width frame) zoom) + :height (* (:height frame) zoom) + :vbox (str "0 0 " (:width frame 0) " " (:height frame 0))}) -(def threads-ref - (l/derived :comment-threads st/state)) +(mf/defc viewer + [{:keys [params data]}] -(def comments-local-ref - (l/derived :comments-local st/state)) + (let [{:keys [page-id section index]} params -(mf/defc comments-layer - [{:keys [zoom frame data] :as props}] - (let [profile (mf/deref refs/profile) + local (mf/deref refs/viewer-local) - modifier1 (-> (gpt/point (:x frame) (:y frame)) - (gpt/negate) - (gmt/translate-matrix)) + file (:file data) + users (:users data) + project (:project data) + perms (:permissions data) - modifier2 (-> (gpt/point (:x frame) (:y frame)) - (gmt/translate-matrix)) + page-id (or page-id (-> file :data :pages first)) - threads-map (->> (mf/deref threads-ref) - (d/mapm #(update %2 :position gpt/transform modifier1))) + page (mf/use-memo + (mf/deps data page-id) + (fn [] + (get-in data [:pages page-id]))) - cstate (mf/deref refs/comments-local) + zoom (:zoom local) + frames (:frames page) + frame (get frames index) - mframe (geom/transform-shape frame) - threads (->> (vals threads-map) - (dcm/apply-filters cstate profile) - (filter (fn [{:keys [position]}] - (frame-contains? mframe position)))) - - on-bubble-click - (mf/use-callback - (mf/deps cstate) - (fn [thread] - (if (= (:open cstate) (:id thread)) - (st/emit! (dcm/close-thread)) - (st/emit! (dcm/open-thread thread))))) + size (mf/use-memo + (mf/deps frame zoom) + (fn [] (calculate-size frame zoom))) on-click (mf/use-callback - (mf/deps cstate data frame) - (fn [event] - (dom/stop-propagation event) - (if (some? (:open cstate)) - (st/emit! (dcm/close-thread)) - (let [event (.-nativeEvent ^js event) - position (-> (dom/get-offset-position event) - (gpt/transform modifier2)) - params {:position position - :page-id (get-in data [:page :id]) - :file-id (get-in data [:file :id])}] - (st/emit! (dcm/create-draft params)))))) + (mf/deps section) + (fn [_] + (when (= section :comments) + (st/emit! (dcm/close-thread)))))] - on-draft-cancel - (mf/use-callback - (mf/deps cstate) - (st/emitf (dcm/close-thread))) - - on-draft-submit - (mf/use-callback - (mf/deps frame) - (fn [draft] - (let [params (update draft :position gpt/transform modifier2)] - (st/emit! (dcm/create-thread params) - (dcm/close-thread)))))] - - [:div.comments-section {:on-click on-click} - [:div.viewer-comments-container - [:div.threads - (for [item threads] - [:& cmt/thread-bubble {:thread item - :zoom zoom - :on-click on-bubble-click - :open? (= (:id item) (:open cstate)) - :key (:seqn item)}]) - - (when-let [id (:open cstate)] - (when-let [thread (get threads-map id)] - [:& cmt/thread-comments {:thread thread - :users (:users data) - :zoom zoom}])) - - (when-let [draft (:draft cstate)] - [:& cmt/draft-thread {:draft (update draft :position gpt/transform modifier1) - :on-cancel on-draft-cancel - :on-submit on-draft-submit - :zoom zoom}])]]])) - - - -(mf/defc viewport - {::mf/wrap [mf/memo]} - [{:keys [state data index section] :as props}] - (let [zoom (:zoom state) - objects (:objects data) - - frame (get-in data [:frames index]) - frame-id (:id frame) - - modifier (-> (gpt/point (:x frame) (:y frame)) - (gpt/negate) - (gmt/translate-matrix)) - - update-fn #(d/update-when %1 %2 assoc-in [:modifiers :displacement] modifier) - - objects (->> (d/concat [frame-id] (cp/get-children frame-id objects)) - (reduce update-fn objects)) - - interactions? (:interactions-show? state) - wrapper (mf/use-memo (mf/deps objects) #(shapes/frame-container-factory objects interactions?)) - - ;; Retrieve frame again with correct modifier - frame (get objects frame-id) - - width (* (:width frame) zoom) - height (* (:height frame) zoom) - vbox (str "0 0 " (:width frame 0) " " (:height frame 0))] - - [:div.viewport-container - {:style {:width width - :height height - :state state - :position "relative"}} - - (when (= section :comments) - [:& comments-layer {:width width - :height height - :frame frame - :data data - :zoom zoom}]) - - [:svg {:view-box vbox - :width width - :height height - :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg"} - [:& wrapper {:shape frame - :show-interactions? interactions? - :view-box vbox}]]])) - -(mf/defc main-panel - [{:keys [data state index section]}] - (let [locale (mf/deref i18n/locale) - frames (:frames data) - frame (get frames index)] - [:section.viewer-preview - (cond - (empty? frames) - [:section.empty-state - [:span (t locale "viewer.empty-state")]] - - (nil? frame) - [:section.empty-state - [:span (t locale "viewer.frame-not-found")]] - - (some? state) - [:& viewport - {:data data - :section section - :index index - :state state - }])])) - -(mf/defc viewer-content - [{:keys [data state index section] :as props}] - (let [on-click - (fn [event] - (dom/stop-propagation event) - (st/emit! (dcm/close-thread)) - (let [mode (get state :interactions-mode)] - (when (= mode :show-on-click) - (st/emit! dv/flash-interactions)))) - - on-mouse-wheel - (fn [event] - (when (or (kbd/ctrl? event) (kbd/meta? event)) - (dom/prevent-default event) - (let [event (.getBrowserEvent ^js event) - delta (+ (.-deltaY ^js event) (.-deltaX ^js event))] - (if (pos? delta) - (st/emit! dv/decrease-zoom) - (st/emit! dv/increase-zoom))))) - - on-key-down - (fn [event] - (when (kbd/esc? event) - (st/emit! (dcm/close-thread)))) - - on-mount - (fn [] - ;; bind with passive=false to allow the event to be cancelled - ;; https://stackoverflow.com/a/57582286/3219895 - (let [key1 (events/listen goog/global "wheel" on-mouse-wheel #js {"passive" false}) - key2 (events/listen js/window "keydown" on-key-down) - key3 (events/listen js/window "click" on-click)] - (fn [] - (events/unlistenByKey key1) - (events/unlistenByKey key2) - (events/unlistenByKey key3))))] - - (mf/use-effect on-mount) (hooks/use-shortcuts ::viewer sc/shortcuts) - [:div.viewer-layout {:class (dom/classnames :force-visible - (:show-thumbnails state))} - [:& header - {:data data - :state state - :section section - :index index}] + ;; Set the page title + (mf/use-effect + (mf/deps (:name file)) + (fn [] + (let [name (:name file)] + (dom/set-html-title (str "\u25b6 " (tr "title.viewer" name)))))) - [:div.viewer-content {:on-click on-click} - (when (:show-thumbnails state) - [:& thumbnails-panel {:screen :viewer - :index index - :data data}]) - [:& main-panel {:data data - :section section - :state state - :index index}]]])) + (mf/use-effect + (fn [] + (let [key1 (events/listen js/window "click" on-click)] + (fn [] + (events/unlistenByKey key1))))) + [:div {:class (dom/classnames + :force-visible (:show-thumbnails local) + :viewer-layout (not= section :handoff) + :handoff-layout (= section :handoff))} + + [:& header {:project project + :index index + :file file + :page page + :frame frame + :permissions perms + :zoom (:zoom local) + :section section}] + + [:div.viewer-content + [:& thumbnails-panel {:frames frames + :show? (:show-thumbnails local false) + :page page + :index index}] + [:section.viewer-preview + (cond + (empty? frames) + [:section.empty-state + [:span (tr "viewer.empty-state")]] + + (nil? frame) + [:section.empty-state + [:span (tr "viewer.frame-not-found")]] + + (some? frame) + (if (= :handoff section) + [:& handoff/viewport + {:frame frame + :page page + :file file + :section section + :local local}] + + + [:div.viewport-container + {:style {:width (:width size) + :height (:height size) + :position "relative"}} + + (when (= section :comments) + [:& comments-layer {:file file + :users users + :frame frame + :page page + :zoom zoom}]) + + [:& interactions/viewport + {:frame frame + :size size + :page page + :file file + :users users + :local local}]]))]]])) ;; --- Component: Viewer Page (mf/defc viewer-page - [{:keys [file-id page-id index token section] :as props}] - (let [data (mf/deref refs/viewer-data) - state (mf/deref refs/viewer-local)] - - (mf/use-effect - (mf/deps file-id page-id token) + [{:keys [file-id] :as props}] + (mf/use-effect + (mf/deps file-id) + (fn [] + (st/emit! (dv/initialize props)) (fn [] - (st/emit! (dv/initialize props)))) + (st/emit! (dv/finalize props))))) - (mf/use-effect - (mf/deps (:file data)) - #(when-let [name (get-in data [:file :name])] - (dom/set-html-title (str "\u25b6 " (tr "title.viewer" name))))) + (when-let [data (mf/deref refs/viewer-data)] + (let [key (str (get-in data [:file :id]))] + [:& viewer {:params props :data data :key key}]))) - (when (and data state) - [:& viewer-content - {:index index - :section section - :state state - :data data}]))) +(mf/defc breaking-change-notice + [] + [:> static/static-header {} + [:div.image i/unchain] + [:div.main-message (tr "viewer.breaking-change.message")] + [:div.desc-message (tr "viewer.breaking-change.description")]]) diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs new file mode 100644 index 0000000000..09ebfb384c --- /dev/null +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -0,0 +1,159 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.viewer.comments + (:require + [app.common.data :as d] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as geom] + [app.main.data.comments :as dcm] + [app.main.data.events :as ev] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.comments :as cmt] + [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [okulary.core :as l] + [rumext.alpha :as mf])) + +(mf/defc comments-menu + [] + (let [{cmode :mode cshow :show} (mf/deref refs/comments-local) + + show-dropdown? (mf/use-state false) + toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) + hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) + + update-mode + (mf/use-callback + (fn [mode] + (st/emit! (dcm/update-filters {:mode mode})))) + + update-show + (mf/use-callback + (fn [mode] + (st/emit! (dcm/update-filters {:show mode}))))] + + [:div.view-options {:on-click toggle-dropdown} + [:span.label (tr "labels.comments")] + [:span.icon i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + [:ul.dropdown.with-check + [:li {:class (dom/classnames :selected (= :all cmode)) + :on-click #(update-mode :all)} + [:span.icon i/tick] + [:span.label (tr "labels.show-all-comments")]] + + [:li {:class (dom/classnames :selected (= :yours cmode)) + :on-click #(update-mode :yours)} + [:span.icon i/tick] + [:span.label (tr "labels.show-your-comments")]] + + [:hr] + + [:li {:class (dom/classnames :selected (= :pending cshow)) + :on-click #(update-show (if (= :pending cshow) :all :pending))} + [:span.icon i/tick] + [:span.label (tr "labels.hide-resolved-comments")]]]]])) + + +(defn- frame-contains? + [{:keys [x y width height]} {px :x py :y}] + (let [x2 (+ x width) + y2 (+ y height)] + (and (<= x px x2) + (<= y py y2)))) + +(def threads-ref + (l/derived :comment-threads st/state)) + +(def comments-local-ref + (l/derived :comments-local st/state)) + +(mf/defc comments-layer + [{:keys [zoom file users frame page] :as props}] + (let [profile (mf/deref refs/profile) + + modifier1 (-> (gpt/point (:x frame) (:y frame)) + (gpt/negate) + (gmt/translate-matrix)) + + modifier2 (-> (gpt/point (:x frame) (:y frame)) + (gmt/translate-matrix)) + + threads-map (->> (mf/deref threads-ref) + (d/mapm #(update %2 :position gpt/transform modifier1))) + + cstate (mf/deref refs/comments-local) + + mframe (geom/transform-shape frame) + threads (->> (vals threads-map) + (dcm/apply-filters cstate profile) + (filter (fn [{:keys [position]}] + (frame-contains? mframe position)))) + + on-bubble-click + (mf/use-callback + (mf/deps cstate) + (fn [thread] + (if (= (:open cstate) (:id thread)) + (st/emit! (dcm/close-thread)) + (st/emit! (-> (dcm/open-thread thread) + (with-meta {::ev/origin "viewer"})))))) + + on-click + (mf/use-callback + (mf/deps cstate frame page file) + (fn [event] + (dom/stop-propagation event) + (if (some? (:open cstate)) + (st/emit! (dcm/close-thread)) + (let [event (.-nativeEvent ^js event) + position (-> (dom/get-offset-position event) + (gpt/transform modifier2)) + params {:position position + :page-id (:id page) + :file-id (:id file)}] + (st/emit! (dcm/create-draft params)))))) + + on-draft-cancel + (mf/use-callback + (mf/deps cstate) + (st/emitf (dcm/close-thread))) + + on-draft-submit + (mf/use-callback + (mf/deps frame) + (fn [draft] + (let [params (update draft :position gpt/transform modifier2)] + (st/emit! (dcm/create-thread params) + (dcm/close-thread)))))] + + [:div.comments-section {:on-click on-click} + [:div.viewer-comments-container + [:div.threads + (for [item threads] + [:& cmt/thread-bubble {:thread item + :zoom zoom + :on-click on-bubble-click + :open? (= (:id item) (:open cstate)) + :key (:seqn item)}]) + + (when-let [id (:open cstate)] + (when-let [thread (get threads-map id)] + [:& cmt/thread-comments {:thread thread + :users users + :zoom zoom}])) + + (when-let [draft (:draft cstate)] + [:& cmt/draft-thread {:draft (update draft :position gpt/transform modifier1) + :on-cancel on-draft-cancel + :on-submit on-draft-submit + :zoom zoom}])]]])) diff --git a/frontend/src/app/main/ui/viewer/handoff.cljs b/frontend/src/app/main/ui/viewer/handoff.cljs new file mode 100644 index 0000000000..f3cbeaa7be --- /dev/null +++ b/frontend/src/app/main/ui/viewer/handoff.cljs @@ -0,0 +1,67 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.viewer.handoff + (:require + [app.main.data.viewer :as dv] + [app.main.store :as st] + [app.main.ui.viewer.handoff.left-sidebar :refer [left-sidebar]] + [app.main.ui.viewer.handoff.render :refer [render-frame-svg]] + [app.main.ui.viewer.handoff.right-sidebar :refer [right-sidebar]] + [app.util.dom :as dom] + [app.util.keyboard :as kbd] + [goog.events :as events] + [rumext.alpha :as mf]) + (:import goog.events.EventType)) + +(defn handle-select-frame + [frame] + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dv/select-shape (:id frame))))) + +(mf/defc viewport + [{:keys [local file page frame]}] + (let [on-mouse-wheel + (fn [event] + (when (or (kbd/ctrl? event) (kbd/meta? event)) + (dom/prevent-default event) + (let [event (.getBrowserEvent ^js event) + delta (+ (.-deltaY ^js event) + (.-deltaX ^js event))] + (if (pos? delta) + (st/emit! dv/decrease-zoom) + (st/emit! dv/increase-zoom))))) + + on-mount + (fn [] + ;; bind with passive=false to allow the event to be cancelled + ;; https://stackoverflow.com/a/57582286/3219895 + (let [key1 (events/listen goog/global EventType.WHEEL + on-mouse-wheel #js {"passive" false})] + (fn [] + (events/unlistenByKey key1))))] + + (mf/use-effect on-mount) + + (mf/use-effect + (mf/deps (:id frame)) + (fn [] + (st/emit! (dv/select-shape (:id frame))))) + + [:* + [:& left-sidebar {:frame frame + :local local + :page page}] + [:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)} + [:div.handoff-svg-container + [:& render-frame-svg {:frame frame :page page :local local}]]] + + [:& right-sidebar {:frame frame + :selected (:selected local) + :page page + :file file}]])) diff --git a/frontend/src/app/main/ui/handoff/attributes.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs similarity index 70% rename from frontend/src/app/main/ui/handoff/attributes.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes.cljs index cdce9e76d1..2792ae1fef 100644 --- a/frontend/src/app/main/ui/handoff/attributes.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs @@ -4,18 +4,18 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes +(ns app.main.ui.viewer.handoff.attributes (:require [app.common.geom.shapes :as gsh] - [app.main.ui.handoff.attributes.blur :refer [blur-panel]] - [app.main.ui.handoff.attributes.fill :refer [fill-panel]] - [app.main.ui.handoff.attributes.image :refer [image-panel]] - [app.main.ui.handoff.attributes.layout :refer [layout-panel]] - [app.main.ui.handoff.attributes.shadow :refer [shadow-panel]] - [app.main.ui.handoff.attributes.stroke :refer [stroke-panel]] - [app.main.ui.handoff.attributes.svg :refer [svg-panel]] - [app.main.ui.handoff.attributes.text :refer [text-panel]] - [app.main.ui.handoff.exports :refer [exports]] + [app.main.ui.viewer.handoff.attributes.blur :refer [blur-panel]] + [app.main.ui.viewer.handoff.attributes.fill :refer [fill-panel]] + [app.main.ui.viewer.handoff.attributes.image :refer [image-panel]] + [app.main.ui.viewer.handoff.attributes.layout :refer [layout-panel]] + [app.main.ui.viewer.handoff.attributes.shadow :refer [shadow-panel]] + [app.main.ui.viewer.handoff.attributes.stroke :refer [stroke-panel]] + [app.main.ui.viewer.handoff.attributes.svg :refer [svg-panel]] + [app.main.ui.viewer.handoff.attributes.text :refer [text-panel]] + [app.main.ui.viewer.handoff.exports :refer [exports]] [app.util.i18n :as i18n] [rumext.alpha :as mf])) diff --git a/frontend/src/app/main/ui/handoff/attributes/blur.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs similarity index 96% rename from frontend/src/app/main/ui/handoff/attributes/blur.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs index 67f3b9194b..fc9f382eac 100644 --- a/frontend/src/app/main/ui/handoff/attributes/blur.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.blur +(ns app.main.ui.viewer.handoff.attributes.blur (:require [app.main.ui.components.copy-button :refer [copy-button]] [app.util.code-gen :as cg] diff --git a/frontend/src/app/main/ui/handoff/attributes/common.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs similarity index 95% rename from frontend/src/app/main/ui/handoff/attributes/common.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs index 2171979a50..c7fa443edc 100644 --- a/frontend/src/app/main/ui/handoff/attributes/common.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.common +(ns app.main.ui.viewer.handoff.attributes.common (:require [app.common.math :as mth] [app.main.store :as st] @@ -19,7 +19,7 @@ (def file-colors-ref - (l/derived (l/in [:viewer-data :file :colors]) st/state)) + (l/derived (l/in [:viewer :file :data :colors]) st/state)) (defn make-colors-library-ref [file-id] (let [get-library diff --git a/frontend/src/app/main/ui/handoff/attributes/fill.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs similarity index 93% rename from frontend/src/app/main/ui/handoff/attributes/fill.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs index a2b6301dc0..c1ee2269f0 100644 --- a/frontend/src/app/main/ui/handoff/attributes/fill.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs @@ -4,10 +4,10 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.fill +(ns app.main.ui.viewer.handoff.attributes.fill (:require [app.main.ui.components.copy-button :refer [copy-button]] - [app.main.ui.handoff.attributes.common :refer [color-row]] + [app.main.ui.viewer.handoff.attributes.common :refer [color-row]] [app.util.code-gen :as cg] [app.util.color :as uc] [app.util.i18n :refer [tr]] diff --git a/frontend/src/app/main/ui/handoff/attributes/image.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs similarity index 97% rename from frontend/src/app/main/ui/handoff/attributes/image.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs index e798188682..00691e236c 100644 --- a/frontend/src/app/main/ui/handoff/attributes/image.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.image +(ns app.main.ui.viewer.handoff.attributes.image (:require [app.config :as cfg] [app.main.ui.components.copy-button :refer [copy-button]] diff --git a/frontend/src/app/main/ui/handoff/attributes/layout.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs similarity index 98% rename from frontend/src/app/main/ui/handoff/attributes/layout.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs index 4704bbb0a9..ea7ca7b526 100644 --- a/frontend/src/app/main/ui/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.layout +(ns app.main.ui.viewer.handoff.attributes.layout (:require [app.common.math :as mth] [app.main.ui.components.copy-button :refer [copy-button]] diff --git a/frontend/src/app/main/ui/handoff/attributes/shadow.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs similarity index 95% rename from frontend/src/app/main/ui/handoff/attributes/shadow.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs index 892f466794..67a934f5c7 100644 --- a/frontend/src/app/main/ui/handoff/attributes/shadow.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs @@ -4,11 +4,11 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.shadow +(ns app.main.ui.viewer.handoff.attributes.shadow (:require [app.common.data :as d] [app.main.ui.components.copy-button :refer [copy-button]] - [app.main.ui.handoff.attributes.common :refer [color-row]] + [app.main.ui.viewer.handoff.attributes.common :refer [color-row]] [app.util.code-gen :as cg] [app.util.i18n :refer [tr]] [cuerdas.core :as str] diff --git a/frontend/src/app/main/ui/handoff/attributes/stroke.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs similarity index 96% rename from frontend/src/app/main/ui/handoff/attributes/stroke.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs index ddd5a3263c..a41d11ad5c 100644 --- a/frontend/src/app/main/ui/handoff/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs @@ -4,12 +4,12 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.stroke +(ns app.main.ui.viewer.handoff.attributes.stroke (:require [app.common.data :as d] [app.common.math :as mth] [app.main.ui.components.copy-button :refer [copy-button]] - [app.main.ui.handoff.attributes.common :refer [color-row]] + [app.main.ui.viewer.handoff.attributes.common :refer [color-row]] [app.util.code-gen :as cg] [app.util.color :as uc] [app.util.i18n :refer [t]] diff --git a/frontend/src/app/main/ui/handoff/attributes/svg.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/svg.cljs similarity index 97% rename from frontend/src/app/main/ui/handoff/attributes/svg.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/svg.cljs index a2308c5986..8e126e0149 100644 --- a/frontend/src/app/main/ui/handoff/attributes/svg.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/svg.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.svg +(ns app.main.ui.viewer.handoff.attributes.svg (:require #_[app.common.math :as mth] #_[app.main.ui.icons :as i] diff --git a/frontend/src/app/main/ui/handoff/attributes/text.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs similarity index 97% rename from frontend/src/app/main/ui/handoff/attributes/text.cljs rename to frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs index 694c3525b8..e760b668f4 100644 --- a/frontend/src/app/main/ui/handoff/attributes/text.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs @@ -4,13 +4,13 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.attributes.text +(ns app.main.ui.viewer.handoff.attributes.text (:require [app.common.text :as txt] [app.main.fonts :as fonts] [app.main.store :as st] [app.main.ui.components.copy-button :refer [copy-button]] - [app.main.ui.handoff.attributes.common :refer [color-row]] + [app.main.ui.viewer.handoff.attributes.common :refer [color-row]] [app.util.code-gen :as cg] [app.util.color :as uc] [app.util.i18n :refer [tr]] @@ -22,7 +22,7 @@ (:content shape)) (def file-typographies-ref - (l/derived (l/in [:viewer-data :file :typographies]) st/state)) + (l/derived (l/in [:viewer :file :data :typographies]) st/state)) (defn make-typographies-library-ref [file-id] (let [get-library diff --git a/frontend/src/app/main/ui/handoff/code.cljs b/frontend/src/app/main/ui/viewer/handoff/code.cljs similarity index 74% rename from frontend/src/app/main/ui/handoff/code.cljs rename to frontend/src/app/main/ui/viewer/handoff/code.cljs index b6eab07445..030a443b45 100644 --- a/frontend/src/app/main/ui/handoff/code.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/code.cljs @@ -4,16 +4,19 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.code +(ns app.main.ui.viewer.handoff.code (:require ["js-beautify" :as beautify] [app.common.geom.shapes :as gsh] + [app.main.data.events :as ev] + [app.main.store :as st] [app.main.ui.components.code-block :refer [code-block]] [app.main.ui.components.copy-button :refer [copy-button]] [app.main.ui.icons :as i] [app.util.code-gen :as cg] [app.util.dom :as dom] [cuerdas.core :as str] + [potok.core :as ptk] [rumext.alpha :as mf])) (defn generate-markup-code [_type shapes] @@ -48,7 +51,25 @@ (format-code "css")) markup-code (-> (mf/use-memo (mf/deps shapes) #(generate-markup-code @markup-type shapes)) - (format-code "svg"))] + (format-code "svg")) + + on-markup-copied + (mf/use-callback + (mf/deps @markup-type) + (fn [] + (st/emit! (ptk/event ::ev/event + {::ev/name "copy-handoff-code" + :type @markup-type})))) + + on-style-copied + (mf/use-callback + (mf/deps @style-type) + (fn [] + (st/emit! (ptk/event ::ev/event + {::ev/name "copy-handoff-style" + :type @style-type})))) + ] + [:div.element-options [:div.code-block [:div.code-row-lang @@ -62,7 +83,8 @@ {:on-click on-expand } i/full-screen] - [:& copy-button { :data style-code }]] + [:& copy-button {:data style-code + :on-copied on-style-copied}]] [:div.code-row-display [:& code-block {:type @style-type @@ -78,8 +100,8 @@ {:on-click on-expand} i/full-screen] - [:& copy-button { :data markup-code }]] - + [:& copy-button {:data markup-code + :on-copied on-markup-copied}]] [:div.code-row-display [:& code-block {:type @markup-type :code markup-code}]]] diff --git a/frontend/src/app/main/ui/handoff/exports.cljs b/frontend/src/app/main/ui/viewer/handoff/exports.cljs similarity index 99% rename from frontend/src/app/main/ui/handoff/exports.cljs rename to frontend/src/app/main/ui/viewer/handoff/exports.cljs index 670a8f312f..e866617f7f 100644 --- a/frontend/src/app/main/ui/handoff/exports.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/exports.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.exports +(ns app.main.ui.viewer.handoff.exports (:require [app.common.data :as d] [app.main.data.messages :as dm] diff --git a/frontend/src/app/main/ui/handoff/left_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs similarity index 89% rename from frontend/src/app/main/ui/handoff/left_sidebar.cljs rename to frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs index 12d4ea0318..48aa1a5aae 100644 --- a/frontend/src/app/main/ui/handoff/left_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/left_sidebar.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.left-sidebar +(ns app.main.ui.viewer.handoff.left-sidebar (:require [app.common.data :as d] [app.main.data.viewer :as dv] @@ -16,12 +16,6 @@ [okulary.core :as l] [rumext.alpha :as mf])) -(def selected-shapes - (l/derived (comp :selected :viewer-local) st/state)) - -(def page-ref - (l/derived (comp :page :viewer-data) st/state)) - (defn- make-collapsed-iref [id] #(-> (l/in [:viewer-local :collapsed id]) @@ -31,7 +25,9 @@ [{:keys [item selected objects disable-collapse?] :as props}] (let [id (:id item) selected? (contains? selected id) - item-ref (mf/use-ref nil) + item-ref (mf/use-ref nil) + + collapsed-iref (mf/use-memo (mf/deps id) (make-collapsed-iref id)) @@ -94,10 +90,10 @@ :objects objects :key (:id item)}]))])])) -(mf/defc left-sidebar [{:keys [frame]}] - (let [page (mf/deref page-ref) - selected (mf/deref selected-shapes) - objects (:objects page)] +(mf/defc left-sidebar + [{:keys [frame page local]}] + (let [selected (:selected local) + objects (:objects page)] [:aside.settings-bar.settings-bar-left [:div.settings-bar-inside diff --git a/frontend/src/app/main/ui/handoff/render.cljs b/frontend/src/app/main/ui/viewer/handoff/render.cljs similarity index 75% rename from frontend/src/app/main/ui/handoff/render.cljs rename to frontend/src/app/main/ui/viewer/handoff/render.cljs index d4d8a89e4e..093cfc8464 100644 --- a/frontend/src/app/main/ui/handoff/render.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/render.cljs @@ -4,17 +4,12 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.render +(ns app.main.ui.viewer.handoff.render "The main container for a frame in handoff mode" (:require - [app.common.data :as d] - [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] - [app.common.pages :as cp] [app.main.data.viewer :as dv] [app.main.store :as st] - [app.main.ui.handoff.selection-feedback :refer [selection-feedback]] [app.main.ui.shapes.circle :as circle] [app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.group :as group] @@ -24,17 +19,21 @@ [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.svg-raw :as svg-raw] [app.main.ui.shapes.text :as text] + [app.main.ui.viewer.handoff.selection-feedback :refer [selection-feedback]] + [app.main.ui.viewer.interactions :refer [prepare-objects]] [app.util.dom :as dom] [app.util.object :as obj] [rumext.alpha :as mf])) (declare shape-container-factory) -(defn handle-hover-shape [{:keys [type id]} hover?] - #(when-not (#{:group :frame} type) - (dom/prevent-default %) - (dom/stop-propagation %) - (st/emit! (dv/hover-shape id hover?)))) +(defn handle-hover-shape + [{:keys [type id]} hover?] + (fn [event] + (when-not (#{:group :frame} type) + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dv/hover-shape id hover?))))) (defn select-shape [{:keys [type id]}] (fn [event] @@ -42,7 +41,7 @@ (dom/stop-propagation event) (dom/prevent-default event) (cond - (.-shiftKey event) + (.-shiftKey ^js event) (st/emit! (dv/toggle-selection id)) :else @@ -154,42 +153,37 @@ :group [:> group-container opts] :svg-raw [:> svg-raw-container opts]))))))) -(defn adjust-frame-position [frame-id objects] - (let [frame (get objects frame-id) - modifier (-> (gpt/point (:x frame) (:y frame)) - (gpt/negate) - (gmt/translate-matrix)) - - update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) - modifier-ids (d/concat [frame-id] (cp/get-children frame-id objects))] - (reduce update-fn objects modifier-ids))) - -(defn make-vbox [frame] - (str "0 0 " (:width frame 0) " " (:height frame 0))) - (mf/defc render-frame-svg - {::mf/wrap [mf/memo]} - [{:keys [objects frame-id zoom] :or {zoom 1} :as props}] + [{:keys [page frame local]}] + (let [objects (mf/use-memo + (mf/deps page frame) + (prepare-objects page frame)) - (let [objects (adjust-frame-position frame-id objects) - frame (get objects frame-id) - width (* (:width frame) zoom) - height (* (:height frame) zoom) - vbox (make-vbox frame) - render-frame (mf/use-memo - (mf/deps objects) - #(frame-container-factory objects))] - [:svg {:id "svg-frame" - :view-box vbox - :width width - :height height - :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg"} + ;; Retrieve frame again with correct modifier + frame (get objects (:id frame)) - [:& render-frame {:shape frame - :view-box vbox}] + zoom (:zoom local 1) + width (* (:width frame) zoom) + height (* (:height frame) zoom) + vbox (str "0 0 " (:width frame 0) " " (:height frame 0)) - [:& selection-feedback {:frame frame}]])) + render (mf/use-memo + (mf/deps objects) + #(frame-container-factory objects))] + + [:svg + {:id "svg-frame" + :view-box vbox + :width width + :height height + :version "1.1" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns "http://www.w3.org/2000/svg"} + + [:& render {:shape frame :view-box vbox}] + [:& selection-feedback + {:frame frame + :objects objects + :local local}]])) diff --git a/frontend/src/app/main/ui/handoff/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs similarity index 53% rename from frontend/src/app/main/ui/handoff/right_sidebar.cljs rename to frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs index 390aeaf421..2169bab30b 100644 --- a/frontend/src/app/main/ui/handoff/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs @@ -4,37 +4,26 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.right-sidebar +(ns app.main.ui.viewer.handoff.right-sidebar (:require [app.common.data :as d] - [app.main.store :as st] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.handoff.attributes :refer [attributes]] - [app.main.ui.handoff.code :refer [code]] [app.main.ui.icons :as i] + [app.main.ui.viewer.handoff.attributes :refer [attributes]] + [app.main.ui.viewer.handoff.code :refer [code]] + [app.main.ui.viewer.handoff.selection-feedback :refer [resolve-shapes]] [app.main.ui.workspace.sidebar.layers :refer [element-icon]] - [app.util.i18n :refer [t] :as i18n] - [okulary.core :as l] + [app.util.i18n :refer [tr]] [rumext.alpha :as mf])) -(defn make-selected-shapes-iref - [] - (let [selected->shapes - (fn [state] - (let [selected (get-in state [:viewer-local :selected]) - objects (get-in state [:viewer-data :page :objects]) - resolve-shape #(get objects %)] - (mapv resolve-shape selected)))] - #(l/derived selected->shapes st/state))) - (mf/defc right-sidebar - [{:keys [frame page-id file-id]}] - (let [expanded (mf/use-state false) - locale (mf/deref i18n/locale) - section (mf/use-state :info #_:code) - selected-ref (mf/use-memo (make-selected-shapes-iref)) - shapes (mf/deref selected-ref) - selected-type (-> shapes first (:type :not-found))] + [{:keys [frame page file selected]}] + (let [expanded (mf/use-state false) + section (mf/use-state :info #_:code) + + shapes (resolve-shapes (:objects page) selected) + selected-type (or (-> shapes first :type) :not-found)] + [:aside.settings-bar.settings-bar-right {:class (when @expanded "expanded")} [:div.settings-bar-inside (when (seq shapes) @@ -43,24 +32,24 @@ (if (> (count shapes) 1) [:* [:span.tool-window-bar-icon i/layers] - [:span.tool-window-bar-title (t locale "handoff.tabs.code.selected.multiple" (count shapes))]] + [:span.tool-window-bar-title (tr "handoff.tabs.code.selected.multiple" (count shapes))]] [:* [:span.tool-window-bar-icon [:& element-icon {:shape (-> shapes first)}]] - [:span.tool-window-bar-title (->> selected-type d/name (str "handoff.tabs.code.selected.") (t locale))]]) + [:span.tool-window-bar-title (->> selected-type d/name (str "handoff.tabs.code.selected.") (tr))]]) ] [:div.tool-window-content [:& tab-container {:on-change-tab #(do (reset! expanded false) (reset! section %)) :selected @section} - [:& tab-element {:id :info :title (t locale "handoff.tabs.info")} - [:& attributes {:page-id page-id - :file-id file-id + [:& tab-element {:id :info :title (tr "handoff.tabs.info")} + [:& attributes {:page-id (:id page) + :file-id (:id file) :frame frame :shapes shapes}]] - [:& tab-element {:id :code :title (t locale "handoff.tabs.code")} + [:& tab-element {:id :code :title (tr "handoff.tabs.code")} [:& code {:frame frame :shapes shapes :on-expand #(swap! expanded not)}]]]]])]])) diff --git a/frontend/src/app/main/ui/handoff/selection_feedback.cljs b/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs similarity index 55% rename from frontend/src/app/main/ui/handoff/selection_feedback.cljs rename to frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs index 70df269238..51fb68ce4e 100644 --- a/frontend/src/app/main/ui/handoff/selection_feedback.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/selection_feedback.cljs @@ -4,12 +4,10 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.handoff.selection-feedback +(ns app.main.ui.viewer.handoff.selection-feedback (:require [app.common.geom.shapes :as gsh] - [app.main.store :as st] [app.main.ui.measurements :refer [selection-guides size-display measurement]] - [okulary.core :as l] [rumext.alpha :as mf])) ;; ------------------------------------------------ @@ -21,33 +19,12 @@ (def select-guide-width 1) (def select-guide-dasharray 5) -;; ------------------------------------------------ -;; LENSES -;; ------------------------------------------------ - -(defn make-selected-shapes-iref - "Creates a lens to the current selected shapes" - [] - (let [selected->shapes - (fn [state] - (let [selected (get-in state [:viewer-local :selected]) - objects (get-in state [:viewer-data :page :objects]) - resolve-shape #(get objects %)] - (->> selected (map resolve-shape) (filterv (comp not nil?)))))] - #(l/derived selected->shapes st/state))) - -(defn make-hover-shapes-iref - "Creates a lens to the shapes the user is making hover" - [] - (let [hover->shapes - (fn [state] - (let [hover (get-in state [:viewer-local :hover]) - objects (get-in state [:viewer-data :page :objects])] - (get objects hover)))] - #(l/derived hover->shapes st/state))) - -(def selected-zoom - (l/derived (l/in [:viewer-local :zoom]) st/state)) +(defn resolve-shapes + [objects ids] + (let [resolve-shape #(get objects %)] + (into [] (comp (map resolve-shape) + (filter some?)) + ids))) ;; ------------------------------------------------ ;; HELPERS @@ -75,19 +52,17 @@ :stroke select-color :stroke-width selection-rect-width}}]])) -(mf/defc selection-feedback [{:keys [frame]}] - (let [zoom (mf/deref selected-zoom) - - hover-shapes-ref (mf/use-memo (make-hover-shapes-iref)) - hover-shape (-> (or (mf/deref hover-shapes-ref) frame) - (gsh/translate-to-frame frame)) - - selected-shapes-ref (mf/use-memo (make-selected-shapes-iref)) - selected-shapes (->> (mf/deref selected-shapes-ref) +(mf/defc selection-feedback + [{:keys [frame local objects]}] + (let [{:keys [hover selected zoom]} local + hover-shape (-> (or (first (resolve-shapes objects [hover])) frame) + (gsh/translate-to-frame frame)) + selected-shapes (->> (resolve-shapes objects selected) (map #(gsh/translate-to-frame % frame))) - selrect (gsh/selection-rect selected-shapes) - bounds (frame->bounds frame)] + selrect (gsh/selection-rect selected-shapes) + bounds (frame->bounds frame)] + (when (seq selected-shapes) [:g.selection-feedback {:pointer-events "none"} diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 061afd0f17..b0c869f90a 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -6,304 +6,158 @@ (ns app.main.ui.viewer.header (:require - [app.common.math :as mth] - [app.common.uuid :as uuid] - [app.config :as cfg] - [app.main.data.comments :as dcm] - [app.main.data.messages :as dm] + [app.main.data.modal :as modal] [app.main.data.viewer :as dv] - [app.main.data.viewer.shortcuts :as sc] - [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.fullscreen :as fs] [app.main.ui.icons :as i] + [app.main.ui.viewer.comments :refer [comments-menu]] + [app.main.ui.viewer.interactions :refer [interactions-menu]] + [app.main.ui.workspace.header :refer [zoom-widget]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [app.util.router :as rt] - [app.util.webapi :as wapi] [rumext.alpha :as mf])) -(mf/defc zoom-widget - {:wrap [mf/memo]} - [{:keys [zoom - on-increase - on-decrease - on-zoom-to-50 - on-zoom-to-100 - on-zoom-to-200 - on-fullscreen] - :as props}] - (let [show-dropdown? (mf/use-state false)] - [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} - [:span {} (str (mth/round (* 100 zoom)) "%")] - [:span.dropdown-button i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close #(reset! show-dropdown? false)} - [:ul.dropdown.zoom-dropdown - [:li {:on-click on-increase} - "Zoom in" [:span (sc/get-tooltip :increase-zoom)]] - [:li {:on-click on-decrease} - "Zoom out" [:span (sc/get-tooltip :decrease-zoom)]] - [:li {:on-click on-zoom-to-50} - "Zoom to 50%" [:span (sc/get-tooltip :zoom-50)]] - [:li {:on-click on-zoom-to-100} - "Zoom to 100%" [:span (sc/get-tooltip :reset-zoom)]] - [:li {:on-click on-zoom-to-200} - "Zoom to 200%" [:span (sc/get-tooltip :zoom-200)]] - [:li {:on-click on-fullscreen} - "Full screen"]]]])) - ;; "Full screen" [:span (sc/get-tooltip :full-screen)]]]]])) - -(mf/defc share-link - [{:keys [token] :as props}] - (let [show-dropdown? (mf/use-state false) - dropdown-ref (mf/use-ref) - create (st/emitf (dv/create-share-link)) - delete (st/emitf (dv/delete-share-link)) - - router (mf/deref refs/router) - route (mf/deref refs/route) - link (rt/resolve router - :viewer - (:path-params route) - {:token token :index "0"}) - link (assoc cfg/public-uri :fragment link) - - copy-link - (fn [_] - (wapi/write-to-clipboard (str link)) - (st/emit! (dm/show {:type :info - :content "Link copied successfuly!" - :timeout 3000})))] - [:* - [:span.btn-primary.btn-small - {:alt (tr "viewer.header.share.title") - :on-click #(swap! show-dropdown? not)} - (tr "viewer.header.share.title")] - - [:& dropdown {:show @show-dropdown? - :on-close #(swap! show-dropdown? not) - :container dropdown-ref} - [:div.dropdown.share-link-dropdown {:ref dropdown-ref} - [:span.share-link-title (tr "viewer.header.share.title")] - [:div.share-link-input - (if (string? token) - [:* - [:span.link (str link)] - [:span.link-button {:on-click copy-link} - (tr "viewer.header.share.copy-link")]] - [:span.link-placeholder (tr "viewer.header.share.placeholder")])] - - [:span.share-link-subtitle (tr "viewer.header.share.subtitle")] - [:div.share-link-buttons - (if (string? token) - [:button.btn-warning {:on-click delete} - (tr "viewer.header.share.remove-link")] - [:button.btn-primary {:on-click create} - (tr "viewer.header.share.create-link")])]]]])) - -(mf/defc interactions-menu - [{:keys [state] :as props}] - (let [imode (:interactions-mode state) - - show-dropdown? (mf/use-state false) - toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) - hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) - - select-mode - (mf/use-callback - (fn [mode] - (st/emit! (dv/set-interactions-mode mode))))] - - [:div.view-options - [:div.view-options-dropdown {:on-click toggle-dropdown} - [:span (tr "viewer.header.interactions")] - i/arrow-down] - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} - [:ul.dropdown.with-check - [:li {:class (dom/classnames :selected (= imode :hide)) - :on-click #(select-mode :hide)} - [:span.icon i/tick] - [:span.label (tr "viewer.header.dont-show-interactions")]] - - [:li {:class (dom/classnames :selected (= imode :show)) - :on-click #(select-mode :show)} - [:span.icon i/tick] - [:span.label (tr "viewer.header.show-interactions")]] - - [:li {:class (dom/classnames :selected (= imode :show-on-click)) - :on-click #(select-mode :show-on-click)} - [:span.icon i/tick] - [:span.label (tr "viewer.header.show-interactions-on-click")]]]]])) - -(mf/defc comments-menu - [] - (let [{cmode :mode cshow :show} (mf/deref refs/comments-local) - - show-dropdown? (mf/use-state false) - toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) - hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) - - update-mode - (mf/use-callback - (fn [mode] - (st/emit! (dcm/update-filters {:mode mode})))) - - update-show - (mf/use-callback - (fn [mode] - (st/emit! (dcm/update-filters {:show mode}))))] - - [:div.view-options - [:div.icon {:on-click toggle-dropdown} i/eye] - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} - [:ul.dropdown.with-check - [:li {:class (dom/classnames :selected (= :all cmode)) - :on-click #(update-mode :all)} - [:span.icon i/tick] - [:span.label (tr "labels.show-all-comments")]] - - [:li {:class (dom/classnames :selected (= :yours cmode)) - :on-click #(update-mode :yours)} - [:span.icon i/tick] - [:span.label (tr "labels.show-your-comments")]] - - [:hr] - - [:li {:class (dom/classnames :selected (= :pending cshow)) - :on-click #(update-show (if (= :pending cshow) :all :pending))} - [:span.icon i/tick] - [:span.label (tr "labels.hide-resolved-comments")]]]]])) - -(mf/defc file-menu - [{:keys [project-id file-id page-id] :as props}] - (let [show-dropdown? (mf/use-state false) - toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) - hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) - - on-edit - (mf/use-callback - (mf/deps project-id file-id page-id) - (st/emitf (rt/nav :workspace - {:project-id project-id - :file-id file-id} - {:page-id page-id})))] - [:div.file-menu - [:span.btn-icon-dark.btn-small {:on-click toggle-dropdown} - i/actions - [:& dropdown {:show @show-dropdown? - :on-close hide-dropdown} - [:ul.dropdown - [:li {:on-click on-edit} - [:span.label (tr "viewer.header.edit-file")]]]]]])) - -(mf/defc header - [{:keys [data index section state] :as props}] - (let [{:keys [project file page frames]} data - - fullscreen (mf/use-ctx fs/fullscreen-context) - - total (count frames) - profile (mf/deref refs/profile) - teams (mf/deref refs/teams) - - team-id (get-in data [:project :team-id]) - - has-permission? (and (not= uuid/zero (:id profile)) - (contains? teams team-id)) - - project-id (get-in data [:project :id]) - file-id (get-in data [:file :id]) - page-id (get-in data [:page :id]) - - on-click - (mf/use-callback - (st/emitf dv/toggle-thumbnails-panel)) - - on-goback - (mf/use-callback - (mf/deps project) - (st/emitf (dv/go-to-dashboard project))) - - navigate - (mf/use-callback - (mf/deps file-id page-id) - (fn [section] - (st/emit! (dv/go-to-section section)))) +(mf/defc header-options + [{:keys [section zoom page file permissions]}] + (let [fullscreen (mf/use-ctx fs/fullscreen-context) toggle-fullscreen (mf/use-callback - (mf/deps fullscreen) - (fn [] - (if @fullscreen (fullscreen false) (fullscreen true))))] + (mf/deps fullscreen) + (fn [] + (if @fullscreen (fullscreen false) (fullscreen true)))) + + go-to-workspace + (mf/use-callback + (mf/deps page) + (fn [] + (st/emit! (dv/go-to-workspace (:id page))))) + + open-share-dialog + (mf/use-callback + (mf/deps page) + (fn [] + (modal/show! :share-link {:page page :file file})))] + + [:div.options-zone + (case section + :interactions [:& interactions-menu] + :comments [:& comments-menu] + + [:div.view-options]) + + [:& zoom-widget + {:zoom zoom + :on-increase (st/emitf dv/increase-zoom) + :on-decrease (st/emitf dv/decrease-zoom) + :on-zoom-to-50 (st/emitf dv/zoom-to-50) + :on-zoom-to-100 (st/emitf dv/reset-zoom) + :on-zoom-to-200 (st/emitf dv/zoom-to-200) + :on-fullscreen toggle-fullscreen}] + + [:span.btn-icon-dark.btn-small.tooltip.tooltip-bottom-left + {:alt (tr "viewer.header.fullscreen") + :on-click toggle-fullscreen} + (if @fullscreen + i/full-screen-off + i/full-screen)] + + (when (:edit permissions) + [:span.btn-primary {:on-click open-share-dialog} (tr "labels.share-prototype")]) + + (when (:edit permissions) + [:span.btn-text-dark {:on-click go-to-workspace} (tr "labels.edit-file")])])) + +(mf/defc header-sitemap + [{:keys [project file page frame index] :as props}] + (let [project-name (:name project) + file-name (:name file) + page-name (:name page) + frame-name (:name frame) + total (count (:frames page)) + + toggle-thumbnails + (fn [] + (st/emit! dv/toggle-thumbnails-panel)) + + show-dropdown? (mf/use-state false) + + navigate-to + (fn [page-id] + (st/emit! (dv/go-to-page page-id)) + (reset! show-dropdown? false)) + ] + + [:div.sitemap-zone {:alt (tr "viewer.header.sitemap")} + [:div.breadcrumb + {:on-click #(swap! show-dropdown? not)} + [:span.project-name project-name] + [:span "/"] + [:span.file-name file-name] + [:span "/"] + [:span.page-name page-name] + [:span.icon i/arrow-down] + + [:& dropdown {:show @show-dropdown? + :on-close #(swap! show-dropdown? not)} + [:ul.dropdown + (for [id (get-in file [:data :pages])] + [:li {:id (str id) + :on-click (partial navigate-to id)} + (get-in file [:data :pages-index id :name])])]]] + + [:div.current-frame + {:on-click toggle-thumbnails} + [:span.label "/"] + [:span.label frame-name] + [:span.icon i/arrow-down] + [:span.counters (str (inc index) " / " total)]]])) + + +(mf/defc header + [{:keys [project file page frame zoom section permissions index]}] + (let [go-to-dashboard + (st/emitf (dv/go-to-dashboard)) + + navigate + (fn [section] + (st/emit! (dv/go-to-section section)))] + [:header.viewer-header [:div.main-icon - [:a {:on-click on-goback + [:a {:on-click go-to-dashboard ;; If the user doesn't have permission we disable the link - :style {:pointer-events (when-not has-permission? "none")}} i/logo-icon]] + :style {:pointer-events (when-not permissions "none")}} i/logo-icon]] - [:div.sitemap-zone {:alt (tr "viewer.header.sitemap") - :on-click on-click} - [:span.project-name (:name project)] - [:span "/"] - [:span.file-name (:name file)] - [:span "/"] - [:span.page-name (:name page)] - [:span.show-thumbnails-button i/arrow-down] - [:span.counters (str (inc index) " / " total)]] + [:& header-sitemap {:project project :file file :page page :frame frame :index index}] [:div.mode-zone [:button.mode-zone-button.tooltip.tooltip-bottom {:on-click #(navigate :interactions) :class (dom/classnames :active (= section :interactions)) - :alt "View mode"} + :alt (tr "viewer.header.interactions-section")} i/play] - (when has-permission? + (when (:edit permissions) [:button.mode-zone-button.tooltip.tooltip-bottom {:on-click #(navigate :comments) :class (dom/classnames :active (= section :comments)) - :alt "Comments"} + :alt (tr "viewer.header.comments-section")} i/chat]) - [:button.mode-zone-button.tooltip.tooltip-bottom - {:on-click #(navigate :handoff) - :class (dom/classnames :active (= section :handoff)) - :alt "Code mode"} - i/code]] + (when (:read permissions) + [:button.mode-zone-button.tooltip.tooltip-bottom + {:on-click #(navigate :handoff) + :class (dom/classnames :active (= section :handoff)) + :alt (tr "viewer.header.handsoff-section")} + i/code])] - [:div.options-zone - (case section - :interactions [:& interactions-menu {:state state}] - :comments [:& comments-menu] - nil) - - (when has-permission? - [:& share-link {:token (:token data) - :page (:page data)}]) - - [:& zoom-widget - {:zoom (:zoom state) - :on-increase (st/emitf dv/increase-zoom) - :on-decrease (st/emitf dv/decrease-zoom) - :on-zoom-to-50 (st/emitf dv/zoom-to-50) - :on-zoom-to-100 (st/emitf dv/reset-zoom) - :on-zoom-to-200 (st/emitf dv/zoom-to-200) - :on-fullscreen toggle-fullscreen}] - - [:span.btn-icon-basic.btn-small.tooltip.tooltip-bottom-left - {:alt (tr "viewer.header.fullscreen") - :on-click toggle-fullscreen} - (if @fullscreen - i/full-screen-off - i/full-screen)] - - (when has-permission? - [:& file-menu {:project-id project-id - :file-id file-id - :page-id page-id}])]])) + [:& header-options {:section section + :permissions permissions + :page page + :file file + :zoom zoom}]])) diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs new file mode 100644 index 0000000000..15846eca95 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -0,0 +1,137 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.viewer.interactions + (:require + [app.common.data :as d] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.pages :as cp] + [app.main.data.comments :as dcm] + [app.main.data.viewer :as dv] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.icons :as i] + [app.main.ui.viewer.shapes :as shapes] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.keyboard :as kbd] + [goog.events :as events] + [rumext.alpha :as mf])) + +(defn prepare-objects + [page frame] + (fn [] + (let [objects (:objects page) + frame-id (:id frame) + modifier (-> (gpt/point (:x frame) (:y frame)) + (gpt/negate) + (gmt/translate-matrix)) + + update-fn #(d/update-when %1 %2 assoc-in [:modifiers :displacement] modifier)] + + (->> (cp/get-children frame-id objects) + (d/concat [frame-id]) + (reduce update-fn objects))))) + + +(mf/defc viewport + {::mf/wrap [mf/memo]} + [{:keys [local page frame size]}] + (let [interactions? (:interactions-show? local) + + objects (mf/use-memo + (mf/deps page frame) + (prepare-objects page frame)) + + wrapper (mf/use-memo + (mf/deps objects interactions?) + #(shapes/frame-container-factory objects interactions?)) + + ;; Retrieve frame again with correct modifier + frame (get objects (:id frame)) + + on-click + (fn [_] + (let [mode (:interactions-mode local)] + (when (= mode :show-on-click) + (st/emit! dv/flash-interactions)))) + + on-mouse-wheel + (fn [event] + (when (or (kbd/ctrl? event) (kbd/meta? event)) + (dom/prevent-default event) + (let [event (.getBrowserEvent ^js event) + delta (+ (.-deltaY ^js event) (.-deltaX ^js event))] + (if (pos? delta) + (st/emit! dv/decrease-zoom) + (st/emit! dv/increase-zoom))))) + + on-key-down + (fn [event] + (when (kbd/esc? event) + (st/emit! (dcm/close-thread))))] + + (mf/use-effect + (mf/deps local) ;; on-click event depends on local + (fn [] + ;; bind with passive=false to allow the event to be cancelled + ;; https://stackoverflow.com/a/57582286/3219895 + (let [key1 (events/listen goog/global "wheel" on-mouse-wheel #js {"passive" false}) + key2 (events/listen js/window "keydown" on-key-down) + key3 (events/listen js/window "click" on-click)] + (fn [] + (events/unlistenByKey key1) + (events/unlistenByKey key2) + (events/unlistenByKey key3))))) + + [:svg {:view-box (:vbox size) + :width (:width size) + :height (:height size) + :version "1.1" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns "http://www.w3.org/2000/svg"} + [:& wrapper {:shape frame + :show-interactions? interactions? + :view-box (:vbox size)}]])) + + +(mf/defc interactions-menu + [] + (let [local (mf/deref refs/viewer-local) + mode (:interactions-mode local) + + show-dropdown? (mf/use-state false) + toggle-dropdown (mf/use-fn #(swap! show-dropdown? not)) + hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) + + select-mode + (mf/use-callback + (fn [mode] + (st/emit! (dv/set-interactions-mode mode))))] + + [:div.view-options {:on-click toggle-dropdown} + [:span.label (tr "viewer.header.interactions")] + [:span.icon i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close hide-dropdown} + [:ul.dropdown.with-check + [:li {:class (dom/classnames :selected (= mode :hide)) + :on-click #(select-mode :hide)} + [:span.icon i/tick] + [:span.label (tr "viewer.header.dont-show-interactions")]] + + [:li {:class (dom/classnames :selected (= mode :show)) + :on-click #(select-mode :show)} + [:span.icon i/tick] + [:span.label (tr "viewer.header.show-interactions")]] + + [:li {:class (dom/classnames :selected (= mode :show-on-click)) + :on-click #(select-mode :show-on-click)} + [:span.icon i/tick] + [:span.label (tr "viewer.header.show-interactions-on-click")]]]]])) + diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index 26397302e3..52082e5163 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -10,11 +10,11 @@ [app.main.data.viewer :as dv] [app.main.exports :as exports] [app.main.store :as st] - [app.main.ui.components.dropdown :refer [dropdown']] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] - [goog.object :as gobj] + [app.util.object :as obj] + [app.util.timers :as ts] [rumext.alpha :as mf])) (mf/defc thumbnails-content @@ -50,7 +50,7 @@ on-mount (fn [] (let [dom (mf/ref-val container)] - (reset! width (gobj/get dom "clientWidth"))))] + (reset! width (obj/get dom "clientWidth"))))] (mf/use-effect on-mount) (if expanded? @@ -72,7 +72,8 @@ [:span.btn-close {:on-click on-close} i/close]]]) (mf/defc thumbnail-item - [{:keys [selected? frame on-click index objects] :as props}] + {::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} + [{:keys [selected? frame on-click index objects]}] [:div.thumbnail-item {:on-click #(on-click % index)} [:div.thumbnail-preview {:class (dom/classnames :selected selected?)} @@ -81,42 +82,39 @@ [:span.name {:title (:name frame)} (:name frame)]]]) (mf/defc thumbnails-panel - [{:keys [data index] :as props}] + [{:keys [frames page index show?] :as props}] (let [expanded? (mf/use-state false) container (mf/use-ref) - on-close #(st/emit! dv/toggle-thumbnails-panel) - selected (mf/use-var false) + objects (:objects page) - on-mouse-leave - (fn [_] - (when @selected - (on-close))) + on-close #(st/emit! dv/toggle-thumbnails-panel) + selected (mf/use-var false) on-item-click - (fn [_ index] - (compare-and-set! selected false true) - (st/emit! (dv/go-to-frame-by-index index)) - (when @expanded? - (on-close)))] + (mf/use-callback + (mf/deps @expanded?) + (fn [_ index] + (compare-and-set! selected false true) + (st/emit! (dv/go-to-frame-by-index index)) + (when @expanded? + (on-close))))] - [:& dropdown' {:on-close on-close - :container container - :show true} - [:section.viewer-thumbnails - {:class (dom/classnames :expanded @expanded?) - :ref container - :on-mouse-leave on-mouse-leave} + [:section.viewer-thumbnails + {:class (dom/classnames :expanded @expanded? + :invisible (not show?)) - [:& thumbnails-summary {:on-toggle-expand #(swap! expanded? not) - :on-close on-close - :total (count (:frames data))}] - [:& thumbnails-content {:expanded? @expanded? - :total (count (:frames data))} - (for [[i frame] (d/enumerate (:frames data))] - [:& thumbnail-item {:key i - :index i - :frame frame - :objects (:objects data) - :on-click on-item-click - :selected? (= i index)}])]]])) + :ref container + } + + [:& thumbnails-summary {:on-toggle-expand #(swap! expanded? not) + :on-close on-close + :total (count frames)}] + [:& thumbnails-content {:expanded? @expanded? + :total (count frames)} + (for [[i frame] (d/enumerate frames)] + [:& thumbnail-item {:index i + :frame frame + :objects objects + :on-click on-item-click + :selected? (= i index)}])]])) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index a4e3f03558..e2eaf61364 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -26,7 +26,6 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] - [app.util.timers :as ts] [okulary.core :as l] [rumext.alpha :as mf])) @@ -116,27 +115,31 @@ project (mf/deref refs/workspace-project) layout (mf/deref refs/workspace-layout)] + ;; Setting the layout preset by its name (mf/use-effect (mf/deps layout-name) - #(st/emit! (dw/initialize-layout layout-name))) + (fn [] + (st/emit! (dw/setup-layout layout-name)))) + (mf/use-effect (mf/deps project-id file-id) (fn [] (st/emit! (dw/initialize-file project-id file-id)) (fn [] - ;; Schedule to 100ms so we can do the update before the file is finalized - (st/emit! ::dwp/force-persist) - (ts/schedule 100 (st/emitf (dw/finalize-file project-id file-id)))))) + (st/emit! ::dwp/force-persist + (dw/finalize-file project-id file-id))))) + ;; Close any non-modal dialog that may be still open (mf/use-effect + (fn [] (st/emit! dm/hide))) + + ;; Set properly the page title + (mf/use-effect + (mf/deps (:name file)) (fn [] - ;; Close any non-modal dialog that may be still open - (st/emit! dm/hide))) - - (mf/use-effect - (mf/deps file) - #(dom/set-html-title (tr "title.workspace" (:name file)))) + (when (:name file) + (dom/set-html-title (tr "title.workspace" (:name file)))))) [:& (mf/provider ctx/current-file-id) {:value (:id file)} [:& (mf/provider ctx/current-team-id) {:value (:team-id project)} diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 96f338b59d..7e71d5c4ab 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -20,7 +20,7 @@ [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]] [app.util.color :as uc] [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [t]] + [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] [okulary.core :as l] [rumext.alpha :as mf])) @@ -113,7 +113,6 @@ [{:keys [data disable-gradient disable-opacity on-change on-accept]}] (let [state (mf/use-state (data->state data)) active-tab (mf/use-state :ramp #_:harmony #_:hsva) - locale (mf/deref i18n/locale) ref-picker (mf/use-ref) @@ -291,12 +290,18 @@ :on-select-stop handle-change-stop}] [:div.colorpicker-tabs - [:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active") - :on-click (change-tab :ramp)} i/picker-ramp] - [:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active") - :on-click (change-tab :harmony)} i/picker-harmony] - [:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active") - :on-click (change-tab :hsva)} i/picker-hsv]] + [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand + {:class (when (= @active-tab :ramp) "active") + :alt (tr "workspace.libraries.colors.rgba") + :on-click (change-tab :ramp)} i/picker-ramp] + [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand + {:class (when (= @active-tab :harmony) "active") + :alt (tr "workspace.libraries.colors.rgb-complementary") + :on-click (change-tab :harmony)} i/picker-harmony] + [:div.colorpicker-tab.tooltip.tooltip-bottom.tooltip-expand + {:class (when (= @active-tab :hsva) "active") + :alt (tr "workspace.libraries.colors.hsv") + :on-click (change-tab :hsva)} i/picker-hsv]] (if picking-color? [:div.picker-detail-wrapper @@ -331,7 +336,7 @@ {:on-click (fn [] (on-accept (state->data @state)) (modal/hide!))} - (t locale "workspace.libraries.colors.save-color")]])]])) + (tr "workspace.libraries.colors.save-color")]])]])) (defn calculate-position "Calculates the style properties for the given coordinates and position" diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index 7084739318..ea92fc79d2 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.comments (:require [app.main.data.comments :as dcm] + [app.main.data.events :as ev] [app.main.data.workspace :as dw] [app.main.data.workspace.comments :as dwcm] [app.main.refs :as refs] @@ -77,8 +78,10 @@ (when (not= page-id (:page-id thread)) (st/emit! (dw/go-to-page (:page-id thread)))) (tm/schedule - (st/emitf (dwcm/center-to-comment-thread thread) - (dcm/open-thread thread)))))] + (fn [] + (st/emit! (dwcm/center-to-comment-thread thread) + (-> (dcm/open-thread thread) + (with-meta {::ev/origin "workspace"})))))))] [:div.comments-section.comment-threads-section [:div.workspace-comment-threads-sidebar-header diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index e493bb5fc5..29180d9a12 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -70,7 +70,7 @@ do-unmask-group (st/emitf dw/unmask-group) do-flip-vertical (st/emitf (dw/flip-vertical-selected)) do-flip-horizontal (st/emitf (dw/flip-horizontal-selected)) - do-add-component (st/emitf dwl/add-component) + do-add-component (st/emitf (dwl/add-component)) do-detach-component (st/emitf (dwl/detach-component id)) do-reset-component (st/emitf (dwl/reset-component id)) do-start-editing (fn [] @@ -195,6 +195,7 @@ [:* [:& menu-separator] [:& menu-entry {:title (tr "workspace.shape.menu.detach-instance") + :shortcut (sc/get-tooltip :detach-component) :on-click do-detach-component}] [:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides") :on-click do-reset-component}] @@ -205,6 +206,7 @@ [:* [:& menu-separator] [:& menu-entry {:title (tr "workspace.shape.menu.detach-instance") + :shortcut (sc/get-tooltip :detach-component) :on-click do-detach-component}] [:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides") :on-click do-reset-component}] diff --git a/frontend/src/app/main/ui/workspace/header.cljs b/frontend/src/app/main/ui/workspace/header.cljs index e958221158..5b817509f7 100644 --- a/frontend/src/app/main/ui/workspace/header.cljs +++ b/frontend/src/app/main/ui/workspace/header.cljs @@ -8,7 +8,8 @@ (:require [app.common.data :as d] [app.common.math :as mth] - [app.config :as cfg] + [app.config :as cf] + [app.main.data.messages :as dm] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.shortcuts :as sc] @@ -64,15 +65,16 @@ on-decrease on-zoom-reset on-zoom-fit - on-zoom-selected] + on-zoom-selected + on-fullscreen] :as props}] (let [show-dropdown? (mf/use-state false)] [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} - [:span {} (str (mth/round (* 100 zoom)) "%")] - [:span.dropdown-button i/arrow-down] + [:span.label {} (str (mth/round (* 100 zoom)) "%")] + [:span.icon i/arrow-down] [:& dropdown {:show @show-dropdown? :on-close #(reset! show-dropdown? false)} - [:ul.zoom-dropdown + [:ul.dropdown [:li {:on-click on-increase} "Zoom in" [:span (sc/get-tooltip :increase-zoom)]] [:li {:on-click on-decrease} @@ -82,15 +84,21 @@ [:li {:on-click on-zoom-fit} "Zoom to fit all" [:span (sc/get-tooltip :fit-all)]] [:li {:on-click on-zoom-selected} - "Zoom to selected" [:span (sc/get-tooltip :zoom-selected)]]]]])) + "Zoom to selected" [:span (sc/get-tooltip :zoom-selected)]] + (when on-fullscreen + [:li {:on-click on-fullscreen} + "Full screen"])]]])) + ;; --- Header Users (mf/defc menu - [{:keys [layout project file team-id] :as props}] + [{:keys [layout project file team-id page-id] :as props}] (let [show-menu? (mf/use-state false) editing? (mf/use-state false) + frames (mf/deref refs/workspace-frames) + edit-input-ref (mf/use-ref nil) add-shared-fn @@ -137,7 +145,7 @@ (dom/prevent-default event) (reset! editing? true)) - on-export-files + on-export-file (mf/use-callback (mf/deps file team-id) (fn [_] @@ -154,7 +162,27 @@ {:type :export :team-id team-id :has-libraries? (->> files (some :has-libraries?)) - :files files})))))))] + :files files}))))))) + + on-export-frames + (mf/use-callback + (mf/deps file) + (fn [_] + (let [filename (str (:name file) ".pdf") + frame-ids (mapv :id frames)] + (st/emit! (dm/info (tr "workspace.options.exporting-object") + {:timeout nil})) + (->> (rp/query! :export-frames + {:name (:name file) + :file-id (:id file) + :page-id page-id + :frame-ids frame-ids}) + (rx/subs + (fn [body] + (dom/trigger-download filename body)) + (fn [_error] + (st/emit! (dm/error (tr "errors.unexpected-error")))) + (st/emitf dm/hide))))))] (mf/use-effect (mf/deps @editing?) @@ -251,10 +279,13 @@ [:li {:on-click on-add-shared} [:span (tr "dashboard.add-shared")]]) - [:li.export-file {:on-click on-export-files} + [:li.export-file {:on-click on-export-file} [:span (tr "dashboard.export-single")]] - (when cfg/feedback-enabled + [:li.export-file {:on-click on-export-frames} + [:span (tr "dashboard.export-frames")]] + + (when (contains? @cf/flags :user-feedback) [:li.feedback {:on-click (st/emitf (rt/nav :settings-feedback))} [:span (tr "labels.give-feedback")] [:span.primary-badge "ALPHA"]]) @@ -285,7 +316,8 @@ [:& menu {:layout layout :project project :file file - :team-id team-id}] + :team-id team-id + :page-id page-id}] [:div.users-section [:& active-sessions]] diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index b9aa0dbc65..cee01fae22 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -15,7 +15,7 @@ [app.main.ui.icons :as i] [app.util.data :refer [matches-search]] [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [t tr]] + [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] [okulary.core :as l] [rumext.alpha :as mf])) @@ -78,7 +78,7 @@ (mf/use-callback (mf/deps file) (fn [library-id] - (st/emit! (dw/unlink-file-from-library (:id file) library-id) + (st/emit! (dw/unlink-file-from-library (:id file) library-id) (dwl/sync-file (:id file) library-id))))] [:* [:div.section @@ -151,8 +151,6 @@ ::mf/register-as :libraries-dialog} [{:keys [] :as ctx}] (let [selected-tab (mf/use-state :libraries) - - locale (mf/deref i18n/locale) project (mf/deref refs/workspace-project) file (mf/deref workspace-file) libraries (->> (mf/deref refs/workspace-libraries) @@ -176,11 +174,11 @@ [:div.header-item {:class (dom/classnames :active (= @selected-tab :libraries)) :on-click #(change-tab :libraries)} - (t locale "workspace.libraries.libraries")] + (tr "workspace.libraries.libraries")] [:div.header-item {:class (dom/classnames :active (= @selected-tab :updates)) :on-click #(change-tab :updates)} - (t locale "workspace.libraries.updates")]] + (tr "workspace.libraries.updates")]] [:div.libraries-content (case @selected-tab :libraries diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 8149b52d61..c759464b9d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -12,6 +12,7 @@ [app.common.spec :as us] [app.common.text :as txt] [app.config :as cfg] + [app.main.data.events :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as dc] @@ -39,6 +40,7 @@ [cljs.spec.alpha :as s] [cuerdas.core :as str] [okulary.core :as l] + [potok.core :as ptk] [rumext.alpha :as mf])) ; TODO: refactor to remove duplicate code and less parameter passing. @@ -65,17 +67,21 @@ 'subgroup12': {'': [{asset12A}]}} 'group2': {'subgroup21': {'': [{asset21A}}}} " - [assets] - (when-not (empty? assets) - (reduce (fn [groups asset] - (let [path-vector (cp/split-path (or (:path asset) ""))] - (update-in groups (conj path-vector "") - (fn [group] - (if-not group - [asset] - (conj group asset)))))) - {} - assets))) + [assets reverse-sort?] + (letfn [(sort-key [key1 key2] + (if reverse-sort? + (compare (d/name key2) (d/name key1)) + (compare (d/name key1) (d/name key2))))] + (when-not (empty? assets) + (reduce (fn [groups asset] + (let [path-vector (cp/split-path (or (:path asset) ""))] + (update-in groups (conj path-vector "") + (fn [group] + (if-not group + [asset] + (conj group asset)))))) + (sorted-map-by sort-key) + assets)))) (defn add-group [asset group-name] @@ -337,7 +343,7 @@ :on-context-menu on-context-menu}]))])])) (mf/defc components-box - [{:keys [file-id local? components listing-thumbs? open? open-groups selected-assets + [{:keys [file-id local? components listing-thumbs? open? reverse-sort? open-groups selected-assets on-asset-click on-assets-delete on-clear-selection] :as props}] (let [state (mf/use-state {:renaming nil :component-id nil}) @@ -350,7 +356,7 @@ (seq (:colors selected-assets)) (seq (:typographies selected-assets))) - groups (group-assets components) + groups (group-assets components reverse-sort?) on-duplicate (mf/use-callback @@ -589,7 +595,7 @@ :on-context-menu on-context-menu}]))])])) (mf/defc graphics-box - [{:keys [file-id local? objects listing-thumbs? open? open-groups selected-assets + [{:keys [file-id local? objects listing-thumbs? open? open-groups selected-assets reverse-sort? on-asset-click on-assets-delete on-clear-selection] :as props}] (let [input-ref (mf/use-ref nil) state (mf/use-state {:renaming nil @@ -603,7 +609,7 @@ (seq (:colors selected-assets)) (seq (:typographies selected-assets))) - groups (group-assets objects) + groups (group-assets objects reverse-sort?) add-graphic (mf/use-callback @@ -617,7 +623,9 @@ (fn [blobs] (let [params {:file-id file-id :blobs (seq blobs)}] - (st/emit! (dw/upload-media-asset params))))) + (st/emit! (dw/upload-media-asset params) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "graphics"}))))) on-delete (mf/use-callback @@ -937,6 +945,7 @@ (when-not (empty? path-item) [:& colors-group {:file-id file-id :prefix (cp/merge-path-item prefix path-item) + :key (str "group-" path-item) :groups content :open-groups open-groups :local? local? @@ -952,7 +961,7 @@ :colors colors}]))])])) (mf/defc colors-box - [{:keys [file-id local? colors open? open-groups selected-assets + [{:keys [file-id local? colors open? open-groups selected-assets reverse-sort? on-asset-click on-assets-delete on-clear-selection] :as props}] (let [selected-colors (:colors selected-assets) multi-colors? (> (count selected-colors) 1) @@ -960,7 +969,7 @@ (seq (:graphics selected-assets)) (seq (:typographies selected-assets))) - groups (group-assets colors) + groups (group-assets colors reverse-sort?) add-color (mf/use-callback @@ -972,7 +981,9 @@ (mf/use-callback (mf/deps file-id) (fn [event] - (st/emitf (dwl/set-assets-box-open file-id :colors true)) + (st/emit! (dwl/set-assets-box-open file-id :colors true) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "color"})) (modal/show! :colorpicker {:x (.-clientX event) :y (.-clientY event) @@ -1124,7 +1135,7 @@ :on-context-menu on-context-menu}]))])])) (mf/defc typographies-box - [{:keys [file file-id local? typographies open? open-groups selected-assets + [{:keys [file file-id local? typographies open? open-groups selected-assets reverse-sort? on-asset-click on-assets-delete on-clear-selection] :as props}] (let [state (mf/use-state {:detail-open? false :id nil}) @@ -1133,7 +1144,7 @@ local (deref refs/workspace-local) - groups (group-assets typographies) + groups (group-assets typographies reverse-sort?) selected-typographies (:typographies selected-assets) multi-typographies? (> (count selected-typographies) 1) @@ -1145,7 +1156,9 @@ (mf/use-callback (mf/deps file-id) (fn [_] - (st/emit! (dwl/add-typography txt/default-typography)))) + (st/emit! (dwl/add-typography txt/default-typography) + (ptk/event ::ev/event {::ev/name "add-asset-to-library" + :asset-type "typography"})))) handle-change (mf/use-callback @@ -1560,8 +1573,8 @@ (tr "workspace.assets.selected-count" (i18n/c selected-count))]) [:div.listing-option-btn.first {:on-click toggle-sort} (if @reverse-sort? - i/sort-descending - i/sort-ascending)] + i/sort-ascending + i/sort-descending)] [:div.listing-option-btn {:on-click toggle-listing} (if @listing-thumbs? i/listing-enum @@ -1574,6 +1587,7 @@ :listing-thumbs? listing-thumbs? :open? (open-box? :components) :open-groups (open-groups :components) + :reverse-sort? @reverse-sort? :selected-assets @selected-assets :on-asset-click (partial on-asset-click :components) :on-assets-delete on-assets-delete @@ -1586,6 +1600,7 @@ :listing-thumbs? listing-thumbs? :open? (open-box? :graphics) :open-groups (open-groups :graphics) + :reverse-sort? @reverse-sort? :selected-assets @selected-assets :on-asset-click (partial on-asset-click :graphics) :on-assets-delete on-assets-delete @@ -1596,6 +1611,7 @@ :colors colors :open? (open-box? :colors) :open-groups (open-groups :colors) + :reverse-sort? @reverse-sort? :selected-assets @selected-assets :on-asset-click (partial on-asset-click :colors) :on-assets-delete on-assets-delete @@ -1608,6 +1624,7 @@ :typographies typographies :open? (open-box? :typographies) :open-groups (open-groups :typographies) + :reverse-sort? @reverse-sort? :selected-assets @selected-assets :on-asset-click (partial on-asset-click :typographies) :on-assets-delete on-assets-delete diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.cljs b/frontend/src/app/main/ui/workspace/sidebar/history.cljs index 86f294d5b2..84afab2d1b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/history.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.sidebar.history (:require [app.common.data :as d] + [app.main.data.workspace.common :as dwc] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.icons :as i] @@ -249,7 +250,7 @@ nil)])) -(mf/defc history-entry [{:keys [locale entry disabled? current?]}] +(mf/defc history-entry [{:keys [locale entry idx-entry disabled? current?]}] (let [hover? (mf/use-state false) show-detail? (mf/use-state false)] [:div.history-entry {:class (dom/classnames @@ -259,14 +260,14 @@ :show-detail @show-detail?) :on-mouse-enter #(reset! hover? true) :on-mouse-leave #(reset! hover? false) - :on-click #(when (:detail entry) - (swap! show-detail? not)) - } + :on-click (st/emitf (dwc/undo-to-index idx-entry))} [:div.history-entry-summary [:div.history-entry-summary-icon (entry->icon entry)] [:div.history-entry-summary-text (entry->message locale entry)] (when (:detail entry) - [:div.history-entry-summary-button i/arrow-slide])] + [:div.history-entry-summary-button {:on-click #(when (:detail entry) + (swap! show-detail? not))} + i/arrow-slide])] (when show-detail? [:& history-entry-details {:entry entry}])])) @@ -287,6 +288,7 @@ [:& history-entry {:key (str "entry-" idx-entry) :locale locale :entry entry + :idx-entry idx-entry :current? (= idx-entry index) :disabled? (> idx-entry index)}])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs index 99f43711ea..8aae413444 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs @@ -138,6 +138,7 @@ [:& editable-select {:value (:size params) :type (when (number? (:size params)) "number" ) :class "input-option" + :min 1 :options size-options :placeholder "Auto" :on-change handle-change-size}]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs index beee39a191..a5698e4c05 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs @@ -8,10 +8,12 @@ (:require [app.common.data :as d] [app.common.math :as math] + [app.common.pages.spec :as spec] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.colors :as dc] [app.main.data.workspace.undo :as dwu] [app.main.store :as st] + [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] [app.util.dom :as dom] @@ -27,7 +29,9 @@ :stroke-color-ref-id :stroke-color-ref-file :stroke-opacity - :stroke-color-gradient]) + :stroke-color-gradient + :stroke-cap-start + :stroke-cap-end]) (defn- width->string [width] (if (= width :multiple) @@ -41,15 +45,44 @@ "" (pr-str value))) +(defn- stroke-cap-names [] + [[nil (tr "workspace.options.stroke-cap.none") false] + [:line-arrow (tr "workspace.options.stroke-cap.line-arrow") true] + [:triangle-arrow (tr "workspace.options.stroke-cap.triangle-arrow") false] + [:square-marker (tr "workspace.options.stroke-cap.square-marker") false] + [:circle-marker (tr "workspace.options.stroke-cap.circle-marker") false] + [:diamond-marker (tr "workspace.options.stroke-cap.diamond-marker") false] + [:round (tr "workspace.options.stroke-cap.round") true] + [:square (tr "workspace.options.stroke-cap.square") false]]) + +(defn- value->name [value] + (if (= value :multiple) + "--" + (-> (d/seek #(= (first %) value) (stroke-cap-names)) + (second)))) + +(defn- value->img [value] + (when (and value (not= value :multiple)) + (str "images/cap-" (name value) ".svg"))) + (mf/defc stroke-menu - {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]} - [{:keys [ids type values] :as props}] + {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "show-caps"]))]} + [{:keys [ids type values show-caps] :as props}] (let [label (case type :multiple (tr "workspace.options.selection-stroke") :group (tr "workspace.options.group-stroke") (tr "workspace.options.stroke")) show-options (not= (:stroke-style values :none) :none) + show-caps (and show-caps + (not (#{:inner :outer} (:stroke-alignment values)))) + + start-caps-state (mf/use-state {:open? false + :top 0 + :left 0}) + end-caps-state (mf/use-state {:open? false + :top 0 + :left 0}) current-stroke-color {:color (:stroke-color values) :opacity (:stroke-opacity values) @@ -94,6 +127,51 @@ (when-not (str/empty? value) (st/emit! (dch/update-shapes ids #(assoc % :stroke-width value)))))) + update-cap-attr + (fn [& kvs] + #(if (spec/has-caps? %) + (apply (partial assoc %) kvs) + %)) + + open-caps-select + (fn [caps-state] + (fn [event] + (let [window-size (dom/get-window-size) + + target (dom/get-current-target event) + rect (dom/get-bounding-rect target) + + top (+ (:bottom rect) 5) + left (if (< (+ (:left rect) 200) (:width window-size)) + (:left rect) + (- (:width window-size) 205))] + (swap! caps-state assoc :open? true + :left left + :top top)))) + + close-caps-select + (fn [caps-state] + (fn [_] + (swap! caps-state assoc :open? false))) + + on-stroke-cap-start-change + (fn [value] + (st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-start value)))) + + on-stroke-cap-end-change + (fn [value] + (st/emit! (dch/update-shapes ids (update-cap-attr :stroke-cap-end value)))) + + on-stroke-cap-switch + (fn [_] + (let [stroke-cap-start (:stroke-cap-start values) + stroke-cap-end (:stroke-cap-end values)] + (when (and (not= stroke-cap-start :multiple) + (not= stroke-cap-end :multiple)) + (st/emit! (dch/update-shapes ids (update-cap-attr + :stroke-cap-start stroke-cap-end + :stroke-cap-end stroke-cap-start)))))) + on-add-stroke (fn [_] (st/emit! (dch/update-shapes ids #(assoc % @@ -157,7 +235,45 @@ [:option {:value ":solid"} (tr "workspace.options.stroke.solid")] [:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")] [:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")] - [:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]]]] + [:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]] + + ;; Stroke Caps + (when show-caps + [:div.row-flex + [:div.cap-select {:tab-index 0 ;; tab-index to make the element focusable + :on-click (open-caps-select start-caps-state)} + (value->name (:stroke-cap-start values)) + [:span.cap-select-button + i/arrow-down]] + [:& dropdown {:show (:open? @start-caps-state) + :on-close (close-caps-select start-caps-state)} + [:ul.dropdown.cap-select-dropdown {:style {:top (:top @start-caps-state) + :left (:left @start-caps-state)}} + (for [[value label separator] (stroke-cap-names)] + (let [img (value->img value)] + [:li {:class (dom/classnames :separator separator) + :on-click #(on-stroke-cap-start-change value)} + (when img [:img {:src (value->img value)}]) + label]))]] + + [:div.element-set-actions-button {:on-click on-stroke-cap-switch} + i/switch] + + [:div.cap-select {:tab-index 0 + :on-click (open-caps-select end-caps-state)} + (value->name (:stroke-cap-end values)) + [:span.cap-select-button + i/arrow-down]] + [:& dropdown {:show (:open? @end-caps-state) + :on-close (close-caps-select end-caps-state)} + [:ul.dropdown.cap-select-dropdown {:style {:top (:top @end-caps-state) + :left (:left @end-caps-state)}} + (for [[value label separator] (stroke-cap-names)] + (let [img (value->img value)] + [:li {:class (dom/classnames :separator separator) + :on-click #(on-stroke-cap-end-change value)} + (when img [:img {:src (value->img value)}]) + label]))]]])]] ;; NO STROKE [:div.element-set diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 6af685f846..45f313c735 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -322,6 +322,8 @@ :options size-options :type "number" :placeholder "--" + :min 3 + :max 1000 :on-change on-font-size-change :on-blur on-blur}]) @@ -435,6 +437,8 @@ hover-detach (mf/use-state false) name-input-ref (mf/use-ref) + name-ref (mf/use-ref (:name typography)) + on-name-blur (fn [event] (let [content (dom/get-target-val event)] @@ -445,7 +449,12 @@ (fn [] (let [pparams {:project-id (:project-id file) :file-id (:id file)}] - (st/emit! (rt/nav :workspace pparams))))] + (st/emit! (rt/nav :workspace pparams)))) + + on-name-change + (mf/use-callback + (fn [event] + (mf/set-ref-val! name-ref (dom/get-target-val event))))] (mf/use-effect (mf/deps editting?) @@ -462,6 +471,14 @@ (dom/focus! node) (dom/select-text! node)))))) + (mf/use-effect + (fn [] + (fn [] + (let [content (mf/ref-val name-ref)] + ;; On destroy we check if it changed + (when (and (some? content) (not= content (:name typography))) + (on-change {:name content})))))) + [:* [:div.element-set-options-group.typography-entry {:class (when selected? "selected") @@ -534,7 +551,8 @@ {:type "text" :ref name-input-ref :default-value (cp/merge-path-item (:path typography) (:name typography)) - :on-blur on-name-blur}] + :on-blur on-name-blur + :on-change on-name-change}] [:div.element-set-actions-button {:on-click #(reset! open? false)} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs index c44d93ad28..9ab071389b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/input_row.cljs @@ -28,6 +28,8 @@ :class "input-option" :options options :type (when (number? value) "number") + :min min + :max max :placeholder placeholder :on-change on-change}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 5f69d1ea9a..07dbf09fb1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -215,7 +215,7 @@ [:& blur-menu {:type type :ids blur-ids :values blur-values}]) (when-not (empty? stroke-ids) - [:& stroke-menu {:type type :ids stroke-ids :values stroke-values}]) + [:& stroke-menu {:type type :ids stroke-ids :show-caps true :values stroke-values}]) (when-not (empty? text-ids) [:& ot/text-menu {:type type :ids text-ids :values text-values}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs index 9d8727a028..cd911b1b12 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs @@ -38,6 +38,7 @@ :values (select-keys shape fill-attrs)}] [:& stroke-menu {:ids ids :type type + :show-caps true :values stroke-values}] [:& shadow-menu {:ids ids :values (select-keys shape [:shadow])}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 65cf1c0fef..9acacb9774 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -28,8 +28,7 @@ (let [local (mf/use-state {}) input-ref (mf/use-ref) id (:id page) - - state (mf/use-state {:menu-open false}) + state (mf/use-state {:menu-open false}) delete-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/delete-page id))) navigate-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/go-to-page id))) @@ -199,8 +198,9 @@ (let [file (mf/deref refs/workspace-file) create (mf/use-callback (mf/deps file) - (st/emitf (dw/create-page {:file-id (:id file) - :project-id (:project-id file)}))) + (fn [] + (st/emit! (dw/create-page {:file-id (:id file) + :project-id (:project-id file)})))) show-pages? (mf/use-state true) toggle-pages diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index a0fa7552c2..c989136b54 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -70,6 +70,7 @@ ;; STATE alt? (mf/use-state false) ctrl? (mf/use-state false) + space? (mf/use-state false) cursor (mf/use-state (utils/get-cursor :pointer-inner)) hover-ids (mf/use-state nil) hover (mf/use-state nil) @@ -113,7 +114,8 @@ on-drag-enter (actions/on-drag-enter) on-drag-over (actions/on-drag-over) on-drop (actions/on-drop file viewport-ref zoom) - on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing? drawing-path? create-comment?) + on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing? + drawing-path? create-comment? space? viewport-ref zoom) on-mouse-up (actions/on-mouse-up disable-paste) on-pointer-down (actions/on-pointer-down) on-pointer-enter (actions/on-pointer-enter in-viewport?) @@ -150,8 +152,8 @@ (hooks/setup-viewport-size viewport-ref) (hooks/setup-cursor cursor alt? panning drawing-tool drawing-path? node-editing?) (hooks/setup-resize layout viewport-ref) - (hooks/setup-keyboard alt? ctrl?) - (hooks/setup-hover-shapes page-id move-stream selected objects transform selected ctrl? hover hover-ids zoom) + (hooks/setup-keyboard alt? ctrl? space?) + (hooks/setup-hover-shapes page-id move-stream objects transform selected ctrl? hover hover-ids zoom) (hooks/setup-viewport-modifiers modifiers selected objects render-ref) (hooks/setup-shortcuts node-editing? drawing-path?) (hooks/setup-active-frames objects vbox hover active-frames) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 2fbfbac085..8ee77b05ab 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -27,9 +27,11 @@ (:import goog.events.WheelEvent)) (defn on-mouse-down - [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? node-editing? drawing-path? create-comment?] + [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? + node-editing? drawing-path? create-comment? space? viewport-ref zoom] (mf/use-callback - (mf/deps id blocked hidden type selected edition drawing-tool text-editing? node-editing? drawing-path? create-comment?) + (mf/deps id blocked hidden type selected edition drawing-tool text-editing? + node-editing? drawing-path? create-comment? space? viewport-ref zoom) (fn [bevent] (when (or (dom/class? (dom/get-target bevent) "viewport-controls") (dom/class? (dom/get-target bevent) "viewport-selrect")) @@ -48,7 +50,12 @@ (when middle-click? (dom/prevent-default bevent) - (st/emit! (dw/start-panning))) + (if ctrl? + (let [raw-pt (dom/get-client-position event) + viewport (mf/ref-val viewport-ref) + pt (utils/translate-point-to-viewport viewport zoom raw-pt)] + (st/emit! (dw/start-zooming pt))) + (st/emit! (dw/start-panning)))) (when left-click? (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?)) @@ -69,14 +76,16 @@ ;; Handle path node area selection (st/emit! (dwdp/handle-area-selection shift?)) + @space? + (st/emit! (dw/start-panning)) + (or (not id) (and frame? (not selected?))) (st/emit! (dw/handle-area-selection shift?)) (not drawing-tool) (st/emit! (when (or shift? (not selected?)) (dw/select-shape id shift?)) - (when (not shift?) - (dw/start-move-selected))))))))))) + (dw/start-move-selected)))))))))) (defn on-move-selected [hover hover-ids selected] @@ -209,7 +218,8 @@ middle-click? (= 2 (.-which event))] (when left-click? - (st/emit! (ms/->MouseEvent :up ctrl? shift? alt?))) + (st/emit! (dw/finish-panning) + (ms/->MouseEvent :up ctrl? shift? alt?))) (when middle-click? (dom/prevent-default event) @@ -217,7 +227,8 @@ ;; We store this so in Firefox the middle button won't do a paste of the content (reset! disable-paste true) (timers/schedule #(reset! disable-paste false)) - (st/emit! (dw/finish-panning))))))) + (st/emit! (dw/finish-panning) + (dw/finish-zooming))))))) (defn on-pointer-enter [in-viewport?] (mf/use-callback @@ -253,23 +264,20 @@ (defn on-key-down [] (mf/use-callback (fn [event] - (let [bevent (.getBrowserEvent ^js event) - key (.-key ^js event) - ctrl? (kbd/ctrl? event) - shift? (kbd/shift? event) - alt? (kbd/alt? event) - meta? (kbd/meta? event) - target (dom/get-target event) - editor? (some? (.closest ^js target ".public-DraftEditor-content"))] + (let [bevent (.getBrowserEvent ^js event) + key (.-key ^js event) + ctrl? (kbd/ctrl? event) + shift? (kbd/shift? event) + alt? (kbd/alt? event) + meta? (kbd/meta? event) + target (dom/get-target event) + editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) + (= "rich-text" (obj/get target "className")) + (= "INPUT" (obj/get target "tagName")) + (= "TEXTAREA" (obj/get target "tagName")))] (when-not (.-repeat bevent) - (st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta?)) - (when (and (kbd/space? event) - (not editor?) - (not= "rich-text" (obj/get target "className")) - (not= "INPUT" (obj/get target "tagName")) - (not= "TEXTAREA" (obj/get target "tagName"))) - (st/emit! (dw/start-panning)))))))) + (st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta? editing?))))))) (defn on-key-up [] (mf/use-callback @@ -278,10 +286,13 @@ ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) alt? (kbd/alt? event) - meta? (kbd/meta? event)] - (when (kbd/space? event) - (st/emit! (dw/finish-panning))) - (st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta?)))))) + meta? (kbd/meta? event) + target (dom/get-target event) + editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) + (= "rich-text" (obj/get target "className")) + (= "INPUT" (obj/get target "tagName")) + (= "TEXTAREA" (obj/get target "tagName")))] + (st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta? editing?)))))) (defn on-mouse-move [viewport-ref zoom] (let [last-position (mf/use-var nil)] diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 1d9c98b1a6..9bebb02bd7 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -84,17 +84,23 @@ (let [on-resize (actions/on-resize viewport-ref)] (mf/use-layout-effect (mf/deps layout) on-resize))) -(defn setup-keyboard [alt? ctrl?] +(defn setup-keyboard [alt? ctrl? space?] (hooks/use-stream ms/keyboard-alt #(reset! alt? %)) - (hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %))) + (hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %)) + (hooks/use-stream ms/keyboard-space #(reset! space? %))) -;; TODO: revisit the arguments, looks like `selected` is not necessary here -(defn setup-hover-shapes [page-id move-stream _selected objects transform selected ctrl? hover hover-ids zoom] - (let [query-point +(defn setup-hover-shapes [page-id move-stream objects transform selected ctrl? hover hover-ids zoom] + (let [;; We use ref so we don't recreate the stream on a change + zoom-ref (mf/use-ref zoom) + transform-ref (mf/use-ref nil) + selected-ref (mf/use-ref selected) + + query-point (mf/use-callback (mf/deps page-id) (fn [point] - (let [rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))] + (let [zoom (mf/ref-val zoom-ref) + rect (gsh/center->rect point (/ 5 zoom) (/ 5 zoom))] (uw/ask-buffered! {:cmd :selection/query :page-id page-id @@ -102,25 +108,35 @@ :include-frames? true :reverse? true})))) ;; we want the topmost shape to be selected first - ;; We use ref so we don't recreate the stream on a change - transform-ref (mf/use-ref nil) - over-shapes-stream - (->> move-stream - ;; When transforming shapes we stop querying the worker - (rx/filter #(not (some? (mf/ref-val transform-ref)))) - (rx/switch-map query-point)) + (mf/use-memo + (fn [] + (->> move-stream + ;; When transforming shapes we stop querying the worker + (rx/filter #(not (some? (mf/ref-val transform-ref)))) + (rx/switch-map query-point)))) ] + ;; Refresh the refs on a value change + (mf/use-effect (mf/deps transform) #(mf/set-ref-val! transform-ref transform)) + (mf/use-effect + (mf/deps zoom) + #(mf/set-ref-val! zoom-ref zoom)) + + (mf/use-effect + (mf/deps selected) + #(mf/set-ref-val! selected-ref selected)) + (hooks/use-stream over-shapes-stream - (mf/deps page-id objects selected @ctrl?) + (mf/deps page-id objects @ctrl?) (fn [ids] - (let [remove-id? (into #{} (mapcat #(cp/get-parents % objects)) selected) + (let [selected (mf/ref-val selected-ref) + remove-id? (into #{} (mapcat #(cp/get-parents % objects)) selected) remove-id? (if @ctrl? (d/concat remove-id? (->> ids diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 9cf48d13de..5c3074e14a 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -55,6 +55,10 @@ (.insertAdjacentHTML head "beforeend" (str "")))) (defn get-element-by-class @@ -86,6 +90,12 @@ [event] (.-target event)) +(defn get-current-target + "Extract the current target from event instance (different from target + when event triggered in a child of the suscribing element)." + [event] + (.-currentTarget event)) + (defn get-parent [dom] (.-parentElement ^js dom)) diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 1eaf407e32..b7e942f941 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -19,6 +19,7 @@ (def supported-locales [{:label "English" :value "en"} {:label "Español" :value "es"} + {:label "Català" :value "ca"} {:label "Français (community)" :value "fr"} {:label "Deutsch (community)" :value "de"} {:label "Русский (community)" :value "ru"} diff --git a/frontend/src/app/util/import/parser.cljs b/frontend/src/app/util/import/parser.cljs index 8de2abf379..f4628f4782 100644 --- a/frontend/src/app/util/import/parser.cljs +++ b/frontend/src/app/util/import/parser.cljs @@ -373,7 +373,9 @@ stroke-alignment (get-meta node :stroke-alignment keyword) stroke (:stroke svg-data) gradient (when (str/starts-with? stroke "url") - (parse-gradient node stroke))] + (parse-gradient node stroke)) + stroke-cap-start (get-meta node :stroke-cap-start keyword) + stroke-cap-end (get-meta node :stroke-cap-end keyword)] (cond-> props :always @@ -389,7 +391,13 @@ :stroke-opacity nil) (= stroke-alignment :inner) - (update :stroke-width / 2)))) + (update :stroke-width / 2) + + (some? stroke-cap-start) + (assoc :stroke-cap-start stroke-cap-start) + + (some? stroke-cap-end) + (assoc :stroke-cap-end stroke-cap-end)))) (defn add-rect-data [props node svg-data] diff --git a/frontend/src/app/util/keyboard.cljs b/frontend/src/app/util/keyboard.cljs index 2ab1e286fc..0fb84f09d4 100644 --- a/frontend/src/app/util/keyboard.cljs +++ b/frontend/src/app/util/keyboard.cljs @@ -35,3 +35,7 @@ (def altKey? (is-key? "Alt")) (def ctrlKey? (or (is-key? "Control") (is-key? "Meta"))) + +(defn editing? [e] + (.-editing ^js e)) + diff --git a/frontend/src/app/util/router.cljs b/frontend/src/app/util/router.cljs index dd3078c75e..2329af2b61 100644 --- a/frontend/src/app/util/router.cljs +++ b/frontend/src/app/util/router.cljs @@ -108,14 +108,30 @@ (let [router (:router state) path (resolve router id params qparams) uri (-> (u/uri cfg/public-uri) - (assoc :fragment path))] - (js/window.open (str uri) "_blank")))) + (assoc :fragment path)) + name (str (name id) "-" (:file-id params))] + (js/window.open (str uri) name)))) (defn nav-new-window ([id] (nav-new-window id nil nil)) ([id params] (nav-new-window id params nil)) ([id params qparams] (NavigateNewWindow. id params qparams))) + +(defn nav-new-window* + [{:keys [rname path-params query-params name]}] + (ptk/reify ::nav-new-window + ptk/EffectEvent + (effect [_ state _] + (let [router (:router state) + path (resolve router rname path-params query-params) + uri (-> (u/uri cfg/public-uri) + (assoc :fragment path))] + + + + (js/window.open (str uri) name))))) + ;; --- History API (defn initialize-history diff --git a/frontend/src/app/util/websockets.cljs b/frontend/src/app/util/websockets.cljs index 8bac581684..32346b555a 100644 --- a/frontend/src/app/util/websockets.cljs +++ b/frontend/src/app/util/websockets.cljs @@ -40,11 +40,13 @@ (when (.isOpen ^js ws) (.send ^js ws msg))) (-close [_] - (.close ws) (rx/end! sb) (ev/unlistenByKey lk1) (ev/unlistenByKey lk2) - (ev/unlistenByKey lk3))))) + (ev/unlistenByKey lk3) + (.close ^js ws) + (.dispose ^js ws))))) + (defn message? [msg] diff --git a/frontend/src/app/util/worker.cljs b/frontend/src/app/util/worker.cljs index 810d32e294..914756e961 100644 --- a/frontend/src/app/util/worker.cljs +++ b/frontend/src/app/util/worker.cljs @@ -62,7 +62,7 @@ [path on-error] (let [instance (js/Worker. path) bus (rx/subject) - worker (Worker. instance bus) + worker (Worker. instance (rx/to-observable bus)) handle-message (fn [event] diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs index d318f9d6a9..fc6c8a4c18 100644 --- a/frontend/src/app/worker/import.cljs +++ b/frontend/src/app/worker/import.cljs @@ -255,7 +255,7 @@ (let [name (cip/get-image-name node) data-uri (cip/get-image-data node)] (->> (upload-media-files file-id name data-uri) - (rx/catch #(do (.error js/console %) + (rx/catch #(do (.error js/console "Error uploading media: " name) (rx/of node))) (rx/map (fn [media] @@ -358,7 +358,7 @@ (let [resolve (:resolve context)] (->> (get-file context :media-list) (rx/flat-map (comp d/kebab-keys cip/string->uuid)) - (rx/flat-map + (rx/mapcat (fn [[id media]] (let [media (assoc media :id (resolve id))] (->> (get-file context :media id media) @@ -370,7 +370,9 @@ :content content :is-local false}))) (rx/flat-map #(rp/mutation! :upload-file-media-object %)) - (rx/map (constantly media)))))) + (rx/map (constantly media)) + (rx/catch #(do (.error js/console (str "Error uploading media: " (:name media)) ) + (rx/empty))))))) (rx/reduce fb/add-library-media file))) (rx/of file))) diff --git a/frontend/test/app/test_helpers/pages.cljs b/frontend/test/app/test_helpers/pages.cljs index d4f0ab03b1..9f3af5f33d 100644 --- a/frontend/test/app/test_helpers/pages.cljs +++ b/frontend/test/app/test_helpers/pages.cljs @@ -80,7 +80,7 @@ :obj shape}])))) (defn group-shapes - ([state label ids] (group-shapes state label ids "Group")) + ([state label ids] (group-shapes state label ids "Group-1")) ([state label ids prefix] (let [page (current-page state) shapes (dwg/shapes-for-grouping (:objects page) ids)] diff --git a/frontend/translations/ar.po b/frontend/translations/ar.po index 4ac165f467..ea4f2ff9e6 100644 --- a/frontend/translations/ar.po +++ b/frontend/translations/ar.po @@ -1,16 +1,16 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-06-18 09:19+0000\n" -"Last-Translator: Amine Gdoura \n" -"Language-Team: Arabic \n" +"PO-Revision-Date: 2021-08-10 23:33+0000\n" +"Last-Translator: Mahmoud A. Rabo \n" +"Language-Team: Arabic " +"\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" -"X-Generator: Weblate 4.7\n" +"X-Generator: Weblate 4.8-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -96,6 +96,14 @@ msgstr "رمز الاسترداد غير صالح." msgid "auth.notifications.password-changed-succesfully" msgstr "تم تغيير كلمة المرور بنجاح" +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.notifications.profile-not-verified" +msgstr "لم يتم التعرف على الحساب الشخصي ، يرجى التحقق قبل المتابعة." + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.notifications.recovery-token-sent" +msgstr "تم إرسال رابط استعادة كلمة المرور إلى صندوق البريد الخاص بك." + #: src/app/main/ui/auth/verify_token.cljs msgid "auth.notifications.team-invitation-accepted" msgstr "تم الانضمام إلى الفريق بنجاح" @@ -152,7 +160,58 @@ msgstr "عند إنشاء حساب جديد ، فإنك توافق على شرو msgid "auth.verification-email-sent" msgstr "لقد أرسلنا رسالة تحقق إلى بريدك الالكتروني" -#, fuzzy, markdown +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.add-shared" +msgstr "أضف كمكتبة مشتركة" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.change-email" +msgstr "تغيير البريد الإلكتروني" + +#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs +msgid "dashboard.copy-suffix" +msgstr "(نسخة)" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.create-new-team" +msgstr "+ إنشاء فريق جديد" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.default-team-name" +msgstr "Penpot الخاص بك" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.delete-team" +msgstr "حذف الفريق" + +msgid "dashboard.draft-title" +msgstr "مسودة" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate" +msgstr "تكرير" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "تكرير ٪s الملفات" + +#: src/app/main/ui/dashboard/grid.cljs +msgid "dashboard.empty-files" +msgstr "لا يزال لديك 0 ملفات هنا" + +msgid "dashboard.export-multi" +msgstr "تصدير %s الملفات" + +msgid "dashboard.export-single" +msgstr "تصدير الملف" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "الخط محذوف" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "لا يزال ليس لديك خطوط مخصصة مثبتة." + +#, markdown msgid "dashboard.fonts.hero-text1" msgstr "" "ستتم إضافة أي خط ويب تقوم بتحميله هنا إلى قائمة عائلة الخطوط المتوفرة في " @@ -168,441 +227,343 @@ msgstr "" "(https://penpot.app/terms.html). قد ترغب أيضًا في القراءة عن [ترخيص الخطوط] " "(2)." -msgid "labels.custom-fonts" -msgstr "خطوط مخصصة" +msgid "dashboard.import" +msgstr "استيراد ملفات" -msgid "labels.font-family" -msgstr "عائلة الخط" +#: src/app/main/ui/dashboard/team.cljs +msgid "dashboard.invite-profile" +msgstr "قم بدعوة فريق" -#, fuzzy -msgid "labels.font-providers" -msgstr "موفرو الخطوط" +#: src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.leave-team" +msgstr "ترك الفريق" -#, fuzzy -msgid "labels.font-variant" -msgstr "نمط" +#: src/app/main/ui/dashboard/libraries.cljs +msgid "dashboard.libraries-title" +msgstr "المكتبات المشتركة" -msgid "labels.fonts" -msgstr "الخطوط" +#: src/app/main/ui/dashboard/grid.cljs +msgid "dashboard.loading-files" +msgstr "تحميل ملفاتك …" -msgid "labels.go-back" -msgstr "الرجوع للخلف" +msgid "dashboard.loading-fonts" +msgstr "جاري تحميل الخطوط …" -msgid "labels.images" -msgstr "الصور" +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to" +msgstr "الانتقال إلى" -msgid "labels.installed-fonts" -msgstr "الخطوط المتوفرة" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-multi" +msgstr "أنقل ٪s الملفات إلى" -#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs -msgid "labels.members" -msgstr "الأعضاء" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-other-team" +msgstr "الانتقال إلى فريق آخر" -msgid "labels.search-font" -msgstr "البحث عن الخط" - -#, fuzzy -msgid "labels.upload" -msgstr "تحميل" - -msgid "labels.upload-custom-fonts" -msgstr "تحميل الخطوط المخصصة" - -msgid "labels.uploading" -msgstr "جارٍ التحميل ..." - -msgid "modals.delete-font.message" -msgstr "هل أنت متأكد أنك تريد حذف هذا الخط؟ لن يتم تحميله إذا تم استخدامه في ملف." - -msgid "modals.delete-font.title" -msgstr "حذف الخط" - -#: src/app/main/ui/dashboard/fonts.cljs -#, fuzzy -msgid "title.dashboard.font-providers" -msgstr "موفرو الخطوط - %s - Penpot" - -#: src/app/main/ui/dashboard/fonts.cljs -msgid "title.dashboard.fonts" -msgstr "الخطوط -٪ s - Penpot" - -msgid "workspace.viewport.click-to-close-path" -msgstr "انقر لإغلاق المسار" - -#: src/app/main/ui/settings/password.cljs -msgid "dashboard.notifications.password-saved" -msgstr "تم حفظ كلمة المرور بنجاح!" - -#: src/app/main/ui/auth/verify_token.cljs -msgid "dashboard.notifications.email-verified-successfully" -msgstr "تم التحقق من عنوان بريدك الإلكتروني بنجاح" - -#: src/app/main/ui/auth/verify_token.cljs -msgid "dashboard.notifications.email-changed-successfully" -msgstr "تم تحديث عنوان بريدك الإلكتروني بنجاح" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.no-projects-placeholder" -msgstr "ستظهر المشاريع المثبتة هنا" - -#: src/app/main/ui/dashboard/search.cljs -msgid "dashboard.no-matches-for" -msgstr "لم يتم العثور على مطابقات ل \"٪s\"" +#: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/files.cljs +msgid "dashboard.new-file" +msgstr "+ ملف جديد" #: src/app/main/data/dashboard.cljs -msgid "dashboard.new-project-prefix" -msgstr "مشروع جديد" +msgid "dashboard.new-file-prefix" +msgstr "ملف جديد" #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.new-project" msgstr "+ مشروع جديد" #: src/app/main/data/dashboard.cljs -msgid "dashboard.new-file-prefix" -msgstr "ملف جديد" +msgid "dashboard.new-project-prefix" +msgstr "مشروع جديد" -#: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/files.cljs -msgid "dashboard.new-file" -msgstr "+ ملف جديد" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to-other-team" -msgstr "الانتقال إلى فريق آخر" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to-multi" -msgstr "أنقل ٪s الملفات إلى" - -#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to" -msgstr "الانتقال إلى" - -#: src/app/main/ui/dashboard/grid.cljs -msgid "dashboard.loading-files" -msgstr "تحميل ملفاتك …" - -#: src/app/main/ui/dashboard/libraries.cljs -msgid "dashboard.libraries-title" -msgstr "المكتبات المشتركة" - -#: src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.leave-team" -msgstr "ترك الفريق" - -#: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.invite-profile" -msgstr "قم بدعوة فريق" - -#: src/app/main/ui/dashboard/grid.cljs -msgid "dashboard.empty-files" -msgstr "لا يزال لديك 0 ملفات هنا" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate-multi" -msgstr "تكرير ٪s الملفات" - -#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate" -msgstr "تكرير" - -msgid "dashboard.draft-title" -msgstr "مسودة" +#: src/app/main/ui/dashboard/search.cljs +msgid "dashboard.no-matches-for" +msgstr "لم يتم العثور على مطابقات ل \"٪s\"" #: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.delete-team" -msgstr "حذف الفريق" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.default-team-name" -msgstr "Penpot الخاص بك" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.create-new-team" -msgstr "+ إنشاء فريق جديد" - -#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs -msgid "dashboard.copy-suffix" -msgstr "(نسخة)" - -#: src/app/main/ui/settings/profile.cljs -msgid "dashboard.change-email" -msgstr "تغيير البريد الإلكتروني" - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.add-shared" -msgstr "أضف كمكتبة مشتركة" - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.notifications.recovery-token-sent" -msgstr "تم إرسال رابط استعادة كلمة المرور إلى صندوق البريد الخاص بك." - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.notifications.profile-not-verified" -msgstr "لم يتم التعرف على الحساب الشخصي ، يرجى التحقق قبل المتابعة." +msgid "dashboard.no-projects-placeholder" +msgstr "ستظهر المشاريع المثبتة هنا" #: src/app/main/ui/auth/verify_token.cljs -msgid "errors.email-already-validated" -msgstr "تم التحقق من صحة البريد الإلكتروني." +msgid "dashboard.notifications.email-changed-successfully" +msgstr "تم تحديث عنوان بريدك الإلكتروني بنجاح" -#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/settings/change_email.cljs -msgid "errors.email-already-exists" -msgstr "البريد الإلكتروني مستخدم بالفعل" +#: src/app/main/ui/auth/verify_token.cljs +msgid "dashboard.notifications.email-verified-successfully" +msgstr "تم التحقق من عنوان بريدك الإلكتروني بنجاح" -#: src/app/main/data/workspace.cljs -msgid "errors.clipboard-not-implemented" -msgstr "لا يمكن للمتصفح إجراء هذه العملية" +#: src/app/main/ui/settings/password.cljs +msgid "dashboard.notifications.password-saved" +msgstr "تم حفظ كلمة المرور بنجاح!" -#: src/app/main/ui/dashboard/grid.cljs -#, fuzzy -msgid "ds.updated-at" -msgstr "محدث: ٪s" +#: src/app/main/ui/dashboard/team.cljs +msgid "dashboard.num-of-members" +msgstr "٪s الأعضاء" -#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs -msgid "ds.confirm-title" -msgstr "هل أنت متأكد؟" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.open-in-new-tab" +msgstr "فتح ملف في علامة تبويب جديدة" -#: src/app/main/ui/confirm.cljs -msgid "ds.confirm-ok" -msgstr "حسنا" +msgid "dashboard.options" +msgstr "الخيارات" -#: src/app/main/ui/confirm.cljs -msgid "ds.confirm-cancel" -msgstr "إلغاء الأمر" +#: src/app/main/ui/settings/password.cljs +msgid "dashboard.password-change" +msgstr "تغيير كلمة المرور" -#: src/app/main/ui/dashboard/search.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/libraries.cljs, src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.your-penpot" -msgstr "Penpot الخاص بك" +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.pin-unpin" +msgstr "تثبيت / إلغاء التثبيت" + +#: src/app/main/ui/dashboard/projects.cljs +msgid "dashboard.projects-title" +msgstr "المشاريع" + +#: src/app/main/ui/dashboard/team.cljs +msgid "dashboard.promote-to-owner" +msgstr "الترقية إلى مالك" #: src/app/main/ui/settings/profile.cljs -msgid "dashboard.your-name" -msgstr "اسمك" +msgid "dashboard.remove-account" +msgstr "هل تريد إزالة حسابك؟" -#: src/app/main/ui/settings/profile.cljs -msgid "dashboard.your-email" -msgstr "البريد الالكتروني" - -#: src/app/main/ui/settings.cljs -#, fuzzy -msgid "dashboard.your-account-title" -msgstr "حسابك الخاص" - -#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs -msgid "dashboard.update-settings" -msgstr "تحديث الإعدادات" - -#: src/app/main/ui/dashboard/search.cljs -msgid "dashboard.type-something" -msgstr "اكتب لإظهار نتائج البحث" - -#: src/app/main/ui/dashboard/search.cljs -msgid "dashboard.title-search" -msgstr "نتائج البحث" - -#: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.team-projects" -msgstr "مشاريع الفريق" - -#: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.team-members" -msgstr "أعضاء الفريق" - -#: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.team-info" -msgstr "معلومات الفريق" +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.remove-shared" +msgstr "إزالة كمكتبة مشتركة" #: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.switch-team" -msgstr "تبديل الفريق" +msgid "dashboard.search-placeholder" +msgstr "بحث…" -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.success-move-project" -msgstr "تم نقل مشروعك بنجاح" +#: src/app/main/ui/dashboard/search.cljs +msgid "dashboard.searching-for" +msgstr "البحث عن \"٪s\"…" + +#: src/app/main/ui/settings/options.cljs +msgid "dashboard.select-ui-language" +msgstr "حدد لغة واجهة المستخدم" + +#: src/app/main/ui/settings/options.cljs +msgid "dashboard.select-ui-theme" +msgstr "اختر نمطا" + +#: src/app/main/ui/dashboard/grid.cljs +msgid "dashboard.show-all-files" +msgstr "إظهار كافة الملفات" #: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-move-files" -msgstr "تم نقل الملفات بنجاح" - -#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-move-file" -msgstr "تم نقل ملفك بنجاح" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.success-duplicate-project" -msgstr "تم نسخ مشروعك بنجاح" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-duplicate-file" -msgstr "تم تكرار ملفك بنجاح" +msgid "dashboard.success-delete-file" +msgstr "تم حذف ملفك بنجاح" #: src/app/main/ui/dashboard/project_menu.cljs msgid "dashboard.success-delete-project" msgstr "تم حذف مشروعك بنجاح" #: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-delete-file" -msgstr "تم حذف ملفك بنجاح" - -#: src/app/main/ui/dashboard/grid.cljs -msgid "dashboard.show-all-files" -msgstr "إظهار كافة الملفات" - -#: src/app/main/ui/settings/options.cljs -msgid "dashboard.select-ui-theme" -msgstr "اختر نمطا" - -#: src/app/main/ui/settings/options.cljs -msgid "dashboard.select-ui-language" -msgstr "حدد لغة واجهة المستخدم" - -#: src/app/main/ui/dashboard/search.cljs -msgid "dashboard.searching-for" -msgstr "البحث عن \"٪s\"…" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.search-placeholder" -msgstr "بحث…" - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.remove-shared" -msgstr "إزالة كمكتبة مشتركة" - -#: src/app/main/ui/settings/profile.cljs -msgid "dashboard.remove-account" -msgstr "هل تريد إزالة حسابك؟" - -#: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.promote-to-owner" -msgstr "الترقية إلى مالك" - -#: src/app/main/ui/dashboard/projects.cljs -msgid "dashboard.projects-title" -msgstr "المشاريع" +msgid "dashboard.success-duplicate-file" +msgstr "تم تكرار ملفك بنجاح" #: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.pin-unpin" -msgstr "تثبيت / إلغاء التثبيت" +msgid "dashboard.success-duplicate-project" +msgstr "تم نسخ مشروعك بنجاح" -#: src/app/main/ui/settings/password.cljs -msgid "dashboard.password-change" -msgstr "تغيير كلمة المرور" +#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-file" +msgstr "تم نقل ملفك بنجاح" #: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.open-in-new-tab" -msgstr "فتح ملف في علامة تبويب جديدة" +msgid "dashboard.success-move-files" +msgstr "تم نقل الملفات بنجاح" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-move-project" +msgstr "تم نقل مشروعك بنجاح" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.switch-team" +msgstr "تبديل الفريق" #: src/app/main/ui/dashboard/team.cljs -msgid "dashboard.num-of-members" -msgstr "٪s الأعضاء" +msgid "dashboard.team-info" +msgstr "معلومات الفريق" -#: src/app/main/ui/handoff/attributes/stroke.cljs -msgid "handoff.attributes.stroke.width" -msgstr "عرض" +#: src/app/main/ui/dashboard/team.cljs +msgid "dashboard.team-members" +msgstr "أعضاء الفريق" -msgid "handoff.attributes.stroke.style.solid" -msgstr "صلب" +#: src/app/main/ui/dashboard/team.cljs +msgid "dashboard.team-projects" +msgstr "مشاريع الفريق" -msgid "handoff.attributes.stroke.style.none" -msgstr "لا أحد" +#: src/app/main/ui/settings/options.cljs +msgid "dashboard.theme-change" +msgstr "ثيم واجهة الاستخدام" -msgid "handoff.attributes.stroke.style.mixed" -msgstr "مختلط" +#: src/app/main/ui/dashboard/search.cljs +msgid "dashboard.title-search" +msgstr "نتائج البحث" -msgid "handoff.attributes.stroke.style.dotted" -msgstr "منقط" +#: src/app/main/ui/dashboard/search.cljs +msgid "dashboard.type-something" +msgstr "اكتب لإظهار نتائج البحث" -#, permanent -msgid "handoff.attributes.stroke.alignment.outer" -msgstr "خارج" +#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/password.cljs, src/app/main/ui/settings/options.cljs +msgid "dashboard.update-settings" +msgstr "تحديث الإعدادات" -#, permanent -msgid "handoff.attributes.stroke.alignment.inner" -msgstr "داخل" +#: src/app/main/ui/settings.cljs +msgid "dashboard.your-account-title" +msgstr "حسابك" -#, permanent -msgid "handoff.attributes.stroke.alignment.center" -msgstr "مركز" +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.your-email" +msgstr "البريد الالكتروني" -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow" -msgstr "ظل" +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.your-name" +msgstr "اسمك" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.width" -msgstr "عرض" +#: src/app/main/ui/dashboard/search.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/libraries.cljs, src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.your-penpot" +msgstr "Penpot الخاص بك" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.top" -msgstr "أعلى" +#: src/app/main/ui/confirm.cljs +msgid "ds.confirm-cancel" +msgstr "إلغاء الأمر" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.rotation" -msgstr "دوران" +#: src/app/main/ui/confirm.cljs +msgid "ds.confirm-ok" +msgstr "حسنا" -#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.radius" -msgstr "نصف قطر" +#: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs +msgid "ds.confirm-title" +msgstr "هل أنت متأكد؟" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.left" -msgstr "يسار" +#: src/app/main/ui/dashboard/grid.cljs +msgid "ds.updated-at" +msgstr "محدث: ٪s" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.height" -msgstr "ارتفاع" +#: src/app/main/data/workspace.cljs +msgid "errors.clipboard-not-implemented" +msgstr "لا يمكن للمتصفح إجراء هذه العملية" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout" -msgstr "تخطيط" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/settings/change_email.cljs +msgid "errors.email-already-exists" +msgstr "البريد الإلكتروني مستخدم بالفعل" -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.width" -msgstr "عرض" +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.email-already-validated" +msgstr "تم التحقق من صحة البريد الإلكتروني." -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.height" -msgstr "ارتفاع" +#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs +msgid "errors.email-has-permanent-bounces" +msgstr "يحتوي البريد الإلكتروني «%s» على العديد من تقارير الارتداد الدائم." -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.download" -msgstr "تحميل صورة المصدر" +#: src/app/main/ui/settings/change_email.cljs +msgid "errors.email-invalid-confirmation" +msgstr "يجب أن يتطابق البريد الإلكتروني للتأكيد" -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.rgba" -msgstr "RGBA" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/settings/feedback.cljs, src/app/main/ui/dashboard/team.cljs +msgid "errors.generic" +msgstr "حدث خطأ ما." -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.hsla" -msgstr "HSLA" +#: src/app/main/ui/auth/login.cljs +msgid "errors.google-auth-not-enabled" +msgstr "المصادقة مع جوجل تعطلت في الخلفية" -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.hex" -msgstr "HEX" +#: src/app/main/ui/components/color_input.cljs +msgid "errors.invalid-color" +msgstr "لون غير صالح" -#: src/app/main/ui/handoff/attributes/blur.cljs -msgid "handoff.attributes.blur.value" -msgstr "قيمة" +#: src/app/main/ui/auth/login.cljs +msgid "errors.ldap-disabled" +msgstr "تم تعطيل مصادقة LDAP." + +msgid "errors.media-format-unsupported" +msgstr "تنسيق الصورة غير مدعوم (يجب أن يكون svg أو jpg أو png)." + +#: src/app/main/data/workspace/persistence.cljs +msgid "errors.media-too-large" +msgstr "الصورة كبيرة جدا بحيث لا يمكن إدراجها (يجب أن تكون أقل من 5mb)." + +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "errors.media-type-mismatch" +msgstr "يبدو أن محتويات الصورة لا تتطابق مع امتداد الملف." + +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "errors.media-type-not-allowed" +msgstr "يبدو أن هذه ليست صورة صالحة." + +#: src/app/main/ui/dashboard/team.cljs +msgid "errors.member-is-muted" +msgstr "" +"يحتوي الملف الشخصي الذي تدعوه على رسائل بريد إلكتروني مكتومة (تقارير البريد " +"المزعج أو الارتدادات العالية)." + +msgid "errors.network" +msgstr "تعذر الاتصال بخادم الواجهة الخلفية." #: src/app/main/ui/settings/password.cljs -msgid "generic.error" -msgstr "حدث خطأ" +msgid "errors.password-invalid-confirmation" +msgstr "يجب أن تتطابق كلمة مرور التأكيد" -#: src/app/main/ui/settings/feedback.cljs -msgid "feedback.title" -msgstr "البريد الإلكتروني" +#: src/app/main/ui/settings/password.cljs +msgid "errors.password-too-short" +msgstr "يجب ألا تقل كلمة المرور عن 8 أحرف" -#: src/app/main/ui/settings/feedback.cljs -msgid "feedback.subtitle" +#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs +msgid "errors.profile-is-muted" msgstr "" -"يرجى وصف سبب بريدك الإلكتروني ، وتحديد ما إذا كانت مشكلة أم فكرة أم شك. سيرد " -"أحد أعضاء فريقنا في أسرع وقت ممكن." +"يحتوي ملفك الشخصي على رسائل بريد إلكتروني مكتومة (تقارير البريد المزعجة أو " +"الارتدادات العالية)." + +#: src/app/main/ui/auth/register.cljs +msgid "errors.registration-disabled" +msgstr "التسجيل معطل حاليا." + +msgid "errors.terms-privacy-agreement-invalid" +msgstr "يجب أن تقبل شروط الخدمة وسياسة الخصوصية الخاصة بنا." + +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.token-expired" +msgstr "انتهت صلاحية الرمز" + +#: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "errors.unexpected-error" +msgstr "حدث خطأ غير متوقع." + +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.unexpected-token" +msgstr "رمز غير معروف" + +#: src/app/main/ui/auth/login.cljs +msgid "errors.wrong-credentials" +msgstr "يبدو أن اسم المستخدم أو كلمة المرور خاطئة." + +#: src/app/main/ui/settings/password.cljs +msgid "errors.wrong-old-password" +msgstr "كلمة المرور القديمة غير صحيحة" #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.subject" -msgstr "موضوع" +msgid "feedback.chat-start" +msgstr "انضم إلى الدردشة" #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.discussions-title" -msgstr "مناقشات الفريق" +msgid "feedback.chat-subtitle" +msgstr "ترغب في الكلام؟ تحدث معنا في Gitter" + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.description" +msgstr "وصف" + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-go-to" +msgstr "اذهب إلى المناقشات" + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-subtitle1" +msgstr "انضم إلى منتدى التواصل التعاوني لفريق Penpot." #: src/app/main/ui/settings/feedback.cljs msgid "feedback.discussions-subtitle2" @@ -611,146 +572,1519 @@ msgstr "" "التي تؤثر على المشروع." #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.discussions-subtitle1" -msgstr "انضم إلى منتدى التواصل التعاوني لفريق Penpot." +msgid "feedback.discussions-title" +msgstr "مناقشات الفريق" #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.discussions-go-to" -msgstr "اذهب إلى المناقشات" +msgid "feedback.subject" +msgstr "موضوع" #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.description" -msgstr "وصف" +msgid "feedback.subtitle" +msgstr "" +"يرجى وصف سبب بريدك الإلكتروني ، وتحديد ما إذا كانت مشكلة أم فكرة أم شك. " +"سيرد أحد أعضاء فريقنا في أسرع وقت ممكن." #: src/app/main/ui/settings/feedback.cljs -msgid "feedback.chat-subtitle" -msgstr "ترغب في الكلام؟ تحدث معنا في Gitter" - -#: src/app/main/ui/settings/feedback.cljs -msgid "feedback.chat-start" -msgstr "انضم إلى الدردشة" +msgid "feedback.title" +msgstr "البريد الإلكتروني" #: src/app/main/ui/settings/password.cljs -msgid "errors.wrong-old-password" -msgstr "كلمة المرور القديمة غير صحيحة" +msgid "generic.error" +msgstr "حدث خطأ" -#: src/app/main/ui/settings/password.cljs -msgid "errors.password-too-short" -msgstr "يجب ألا تقل كلمة المرور عن 8 أحرف" +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur" +msgstr "الضبابية" -#: src/app/main/ui/settings/password.cljs -msgid "errors.password-invalid-confirmation" -msgstr "يجب أن تتطابق كلمة مرور التأكيد" +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur.value" +msgstr "قيمة" -msgid "errors.network" -msgstr "تعذر الاتصال بخادم الواجهة الخلفية." +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hex" +msgstr "HEX" -#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs -msgid "errors.media-type-not-allowed" -msgstr "يبدو أن هذه ليست صورة صالحة." +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hsla" +msgstr "HSLA" -#: src/app/main/data/workspace/persistence.cljs -msgid "errors.media-too-large" -msgstr "الصورة كبيرة جدا بحيث لا يمكن إدراجها (يجب أن تكون أقل من 5mb)." +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.rgba" +msgstr "RGBA" -msgid "errors.media-format-unsupported" -msgstr "تنسيق الصورة غير مدعوم (يجب أن يكون svg أو jpg أو png)." +#: src/app/main/ui/handoff/attributes/fill.cljs +msgid "handoff.attributes.fill" +msgstr "ملء" -#: src/app/main/ui/auth/login.cljs -msgid "errors.ldap-disabled" -msgstr "تم تعطيل مصادقة LDAP." +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.download" +msgstr "تحميل صورة المصدر" -#: src/app/main/ui/components/color_input.cljs -msgid "errors.invalid-color" -msgstr "لون غير صالح" +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.height" +msgstr "ارتفاع" -#: src/app/main/ui/auth/login.cljs -msgid "errors.google-auth-not-enabled" -msgstr "المصادقة مع جوجل تعطلت في الخلفية" +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.width" +msgstr "عرض" -#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/settings/feedback.cljs, src/app/main/ui/dashboard/team.cljs -msgid "errors.generic" -msgstr "حدث خطأ ما." +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout" +msgstr "تخطيط" -#: src/app/main/ui/settings/change_email.cljs -msgid "errors.email-invalid-confirmation" -msgstr "يجب أن يتطابق البريد الإلكتروني للتأكيد" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.height" +msgstr "ارتفاع" -#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs -msgid "labels.admin" -msgstr "مشرف" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.left" +msgstr "يسار" -msgid "labels.accept" -msgstr "إقبل" +#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.radius" +msgstr "نصف قطر" -msgid "history.alert-message" -msgstr "أنت ترى الإصدار٪ s" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.rotation" +msgstr "دوران" -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.info" -msgstr "معلومات" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.top" +msgstr "أعلى" -msgid "handoff.tabs.code.selected.text" -msgstr "نص" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.width" +msgstr "عرض" -msgid "handoff.tabs.code.selected.svg-raw" -msgstr "SVG" +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow" +msgstr "ظل" -msgid "handoff.tabs.code.selected.rect" -msgstr "رباعي" +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.blur" +msgstr "B" -msgid "handoff.tabs.code.selected.path" -msgstr "مسار" +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-x" +msgstr "X" -msgid "handoff.tabs.code.selected.image" -msgstr "صورة" +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-y" +msgstr "Y" -msgid "handoff.tabs.code.selected.curve" -msgstr "منحنى" +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.spread" +msgstr "S" -msgid "handoff.tabs.code.selected.circle" -msgstr "دائرة" +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke" +msgstr "لون الحدّ" -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.code" -msgstr "شفرة" +#, permanent +msgid "handoff.attributes.stroke.alignment.center" +msgstr "مركز" -msgid "handoff.attributes.typography.text-transform.uppercase" -msgstr "الأحرف الكبيرة" +#, permanent +msgid "handoff.attributes.stroke.alignment.inner" +msgstr "داخل" -msgid "handoff.attributes.typography.text-transform.lowercase" -msgstr "أحرف صغيرة" +#, permanent +msgid "handoff.attributes.stroke.alignment.outer" +msgstr "خارج" + +msgid "handoff.attributes.stroke.style.dotted" +msgstr "منقط" + +msgid "handoff.attributes.stroke.style.mixed" +msgstr "مختلط" + +msgid "handoff.attributes.stroke.style.none" +msgstr "لا أحد" + +msgid "handoff.attributes.stroke.style.solid" +msgstr "صلب" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke.width" +msgstr "عرض" #: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-transform" -msgstr "تغيير النص" - -msgid "handoff.attributes.typography.text-decoration.underline" -msgstr "مسطر" - -msgid "handoff.attributes.typography.text-decoration.strikethrough" -msgstr "يتوسطه خط" +msgid "handoff.attributes.typography" +msgstr "صياغة الحروف" #: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-decoration" -msgstr "زخرفة النص" +msgid "handoff.attributes.typography.font-family" +msgstr "عائلة الخط" #: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.line-height" -msgstr "ارتفاع الخط" +msgid "handoff.attributes.typography.font-size" +msgstr "حجم الخط" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-style" +msgstr "نوع الخط" #: src/app/main/ui/handoff/attributes/text.cljs msgid "handoff.attributes.typography.letter-spacing" msgstr "تباعد الحروف" #: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-style" -msgstr "نوع الخط" +msgid "handoff.attributes.typography.line-height" +msgstr "ارتفاع الخط" -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.spread" -msgstr "S" +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-decoration" +msgstr "زخرفة النص" -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.offset-y" -msgstr "Y" +msgid "handoff.attributes.typography.text-decoration.none" +msgstr "لا شئ" + +msgid "handoff.attributes.typography.text-decoration.strikethrough" +msgstr "يتوسطه خط" + +msgid "handoff.attributes.typography.text-decoration.underline" +msgstr "مسطر" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-transform" +msgstr "تغيير النص" + +msgid "handoff.attributes.typography.text-transform.lowercase" +msgstr "أحرف صغيرة" + +msgid "handoff.attributes.typography.text-transform.none" +msgstr "لا شئ" + +msgid "handoff.attributes.typography.text-transform.titlecase" +msgstr "حالة العنوان" + +msgid "handoff.attributes.typography.text-transform.uppercase" +msgstr "الأحرف الكبيرة" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.code" +msgstr "شفرة" + +msgid "handoff.tabs.code.selected.circle" +msgstr "دائرة" + +msgid "handoff.tabs.code.selected.curve" +msgstr "منحنى" + +msgid "handoff.tabs.code.selected.frame" +msgstr "لوح الرسم" + +msgid "handoff.tabs.code.selected.group" +msgstr "مجموعة" + +msgid "handoff.tabs.code.selected.image" +msgstr "صورة" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.code.selected.multiple" +msgstr "%s محدد" + +msgid "handoff.tabs.code.selected.path" +msgstr "مسار" + +msgid "handoff.tabs.code.selected.rect" +msgstr "رباعي" + +msgid "handoff.tabs.code.selected.svg-raw" +msgstr "SVG" + +msgid "handoff.tabs.code.selected.text" +msgstr "نص" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.info" +msgstr "معلومات" + +msgid "history.alert-message" +msgstr "أنت ترى الإصدار٪ s" + +msgid "labels.accept" +msgstr "إقبل" + +msgid "labels.add-custom-font" +msgstr "إضافة خط مخصص" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.admin" +msgstr "مشرف" + +#: src/app/main/ui/workspace/comments.cljs +msgid "labels.all" +msgstr "الكل" + +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.desc-message" +msgstr "" +"يبدو أنك بحاجة إلى الانتظار قليلا وإعادة المحاولة. نحن نقوم بصيانة صغيرة " +"لخوادمنا." + +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.main-message" +msgstr "مدخل خاطأ" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.cancel" +msgstr "الغاء" + +msgid "labels.centered" +msgstr "توسيط" + +#: src/app/main/ui/dashboard/comments.cljs +msgid "labels.comments" +msgstr "تعليقات" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.confirm-password" +msgstr "تأكيد كلمة المرور" + +msgid "labels.content" +msgstr "محتوى" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "labels.create" +msgstr "انشاء" + +#: src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team" +msgstr "إنشاء فريق جديد" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team.placeholder" +msgstr "أدخل اسم الفريق الجديد" + +msgid "labels.custom-fonts" +msgstr "خطوط مخصصة" + +#: src/app/main/ui/settings/sidebar.cljs +msgid "labels.dashboard" +msgstr "لوحة التحكم" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete" +msgstr "حذف" + +#: src/app/main/ui/comments.cljs +msgid "labels.delete-comment" +msgstr "حذف التعليق" + +#: src/app/main/ui/comments.cljs +msgid "labels.delete-comment-thread" +msgstr "حذف موضوع" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete-multi-files" +msgstr "حذف %s ملفات" + +#: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.drafts" +msgstr "المسودات" + +#: src/app/main/ui/comments.cljs +msgid "labels.edit" +msgstr "تعديل" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.editor" +msgstr "محرر" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.email" +msgstr "البريد الإلكتروني" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-disabled" +msgstr "تعطيل الملاحظات" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-sent" +msgstr "تم إرسال الملاحظات" + +msgid "labels.font-family" +msgstr "عائلة الخط" + +msgid "labels.font-providers" +msgstr "موفري الخط" + +#, fuzzy +msgid "labels.font-variant" +msgstr "نمط" + +msgid "labels.font-variants" +msgstr "الأنماط" + +msgid "labels.fonts" +msgstr "الخطوط" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.give-feedback" +msgstr "إعطاء ملاحظات" + +msgid "labels.go-back" +msgstr "الرجوع للخلف" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.hide-resolved-comments" +msgstr "إخفاء التعليقات التي تم حلها" + +msgid "labels.icons" +msgstr "الأيقونات" + +msgid "labels.images" +msgstr "الصور" + +msgid "labels.installed-fonts" +msgstr "الخطوط المتوفرة" + +#: src/app/main/ui/static.cljs +msgid "labels.internal-error.desc-message" +msgstr "شيء سيء حدث الرجاء إعادة محاولة العملية وإذا استمرت المشكلة، اتصل بالدعم." + +#: src/app/main/ui/static.cljs +msgid "labels.internal-error.main-message" +msgstr "خطأ داخلي" + +#: src/app/main/ui/settings/options.cljs +msgid "labels.language" +msgstr "اللغة" + +#: src/app/main/ui/settings.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.logout" +msgstr "تسجيل خروج" + +msgid "labels.manage-fonts" +msgstr "إدارة الخطوط" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.members" +msgstr "الأعضاء" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.name" +msgstr "الاسم" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.new-password" +msgstr "كلمة مرور جديدة" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/dashboard/comments.cljs +msgid "labels.no-comments-available" +msgstr "ليس لديك أي إشعارات تعليق معلقة" + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.auth-info" +msgstr "لقد سجلت الدخول باعتبارك" + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.desc-message" +msgstr "قد لا تكون هذه الصفحة موجودة أو ليس لديك أذونات للوصول إليها." + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.main-message" +msgstr "عفواً!" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.num-of-files" +msgid_plural "labels.num-of-files" +msgstr[0] "0" +msgstr[1] "1" +msgstr[2] "2" +msgstr[3] "قليل" +msgstr[4] "كثير" +msgstr[5] "غير ذلك" + +msgid "labels.num-of-frames" +msgid_plural "labels.num-of-frames" +msgstr[0] "0" +msgstr[1] "1" +msgstr[2] "2" +msgstr[3] "بعض" +msgstr[4] "العديد" +msgstr[5] "غير ذلك" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.num-of-projects" +msgid_plural "labels.num-of-projects" +msgstr[0] "0" +msgstr[1] "1" +msgstr[2] "2" +msgstr[3] "بعض" +msgstr[4] "الكثير" +msgstr[5] "غير ذلك" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.old-password" +msgstr "كلمة المرور القديمة" + +#: src/app/main/ui/workspace/comments.cljs +msgid "labels.only-yours" +msgstr "خاصة بك" + +msgid "labels.or" +msgstr "أو" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.owner" +msgstr "مالك" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.password" +msgstr "كلمة المرور" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.permissions" +msgstr "اذونات" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.profile" +msgstr "الملف الشخصي" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.projects" +msgstr "المشاريع" + +msgid "labels.recent" +msgstr "الأخيرة" + +#: src/app/main/ui/settings/sidebar.cljs +msgid "labels.release-notes" +msgstr "ملاحظات الإصدار" + +#: src/app/main/ui/workspace/libraries.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.remove" +msgstr "إزالة" + +#: src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.rename" +msgstr "اعاده تسميه" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.rename-team" +msgstr "إعادة تسمية الفريق" + +#: src/app/main/ui/static.cljs, src/app/main/ui/static.cljs, src/app/main/ui/static.cljs +msgid "labels.retry" +msgstr "أعد المحاولة" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.role" +msgstr "دور" + +msgid "labels.save" +msgstr "حفظ" + +msgid "labels.search-font" +msgstr "البحث عن الخط" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.send" +msgstr "ارسل" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.sending" +msgstr "ارسال…" + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.desc-message" +msgstr "نحن في صيانة مبرمجة لأنظمتنا." + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.main-message" +msgstr "الخدمة غير متوفرة" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.settings" +msgstr "إعدادات" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.shared-libraries" +msgstr "المكتبات المشتركة" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-all-comments" +msgstr "إظهار كافة التعليقات" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-your-comments" +msgstr "إظهار تعليقاتك فقط" + +#: src/app/main/ui/static.cljs +msgid "labels.sign-out" +msgstr "خروج" + +#: src/app/main/ui/settings/profile.cljs +msgid "labels.update" +msgstr "تحديث" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.update-team" +msgstr "تحديث الفريق" + +msgid "labels.upload" +msgstr "رفع" + +msgid "labels.upload-custom-fonts" +msgstr "تحميل الخطوط المخصصة" + +msgid "labels.uploading" +msgstr "جارٍ الرفع …" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.viewer" +msgstr "مشاهد" + +#: src/app/main/ui/comments.cljs +msgid "labels.write-new-comment" +msgstr "كتابة تعليق جديد" + +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "media.loading" +msgstr "جاري تحميل الصورة…" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.accept" +msgstr "إضافة كمكتبة مشتركة" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.hint" +msgstr "" +"بمجرد إضافتها كمكتبة مشتركة، ستكون أصول مكتبة الملفات هذه متاحة للاستخدام " +"بين باقي ملفاتك." + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.message" +msgstr "إضافة “%s” كمكتبة مشتركة" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.confirm-email" +msgstr "تحقق من البريد الإلكتروني الجديد" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.info" +msgstr "سنرسل لك رسالة إلى بريدك الإلكتروني الحالي “%s” للتحقق من هويتك." + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.new-email" +msgstr "بريد إلكتروني جديد" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.submit" +msgstr "تغيير البريد الإلكتروني" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.title" +msgstr "تغيير بريدك الإلكتروني" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.cancel" +msgstr "إلغاء والاحتفاظ بحسابي" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.confirm" +msgstr "نعم، احذف حسابي" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.info" +msgstr "بحذف حسابك، ستفقد جميع مشاريعك وأرشيفاتك الحالية." + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.title" +msgstr "هل انت متأكد انك تريد حذف حسابك؟" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.accept" +msgstr "حذف المحادثة" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.message" +msgstr "" +"هل أنت متأكد أنك تريد حذف هذه المحادثة؟ سيتم حذف جميع التعليقات في هذا " +"الموضوع." + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.title" +msgstr "حذف المحادثة" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.accept" +msgstr "حذف الملف" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.message" +msgstr "هل أنت متأكد أنك تريد حذف هذا الملف؟" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.title" +msgstr "حذف الملف" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "حذف الملفات" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "هل تريد بالتأكيد حذف ٪s من الملفات؟" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "حذف %s الملفات" + +msgid "modals.delete-font-variant.message" +msgstr "" +"هل أنت متأكد أنك تريد حذف نمط هذا الخط؟ لن يتم تحميله إذا تم استخدامه في " +"ملف." + +msgid "modals.delete-font-variant.title" +msgstr "حذف نمط الخط" + +msgid "modals.delete-font.message" +msgstr "هل أنت متأكد أنك تريد حذف هذا الخط؟ لن يتم تحميله إذا تم استخدامه في ملف." + +msgid "modals.delete-font.title" +msgstr "حذف الخط" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.body" +msgstr "هل أنت متأكد أنك تريد حذف هذه الصفحة؟" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.title" +msgstr "حذف الصفحة" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.accept" +msgstr "حذف المشروع" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.message" +msgstr "هل أنت متأكد أنك تريد حذف هذا المشروع؟" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.title" +msgstr "حذف المشروع" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.accept" +msgstr "حذف الفريق" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.message" +msgstr "" +"هل أنت متأكد أنك تريد حذف هذا الفريق؟ سيتم حذف جميع المشاريع والملفات " +"المرتبطة بالفريق نهائيًا." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.title" +msgstr "حذف الفريق" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.accept" +msgstr "حذف عضو" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.message" +msgstr "هل أنت متأكد أنك تريد حذف هذا العضو من الفريق؟" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.title" +msgstr "حذف العضو" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member-confirm.accept" +msgstr "إرسال دعوة" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member.title" +msgstr "دعوة للانضمام إلى الفريق" + +msgid "modals.leave-and-reassign.forbiden" +msgstr "" +"لا يمكنك مغادرة الفريق إذا لم يكن هناك عضو آخر للترقية إلى المالك. قد ترغب " +"في حذف الفريق." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint1" +msgstr "أنت %s المالك." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint2" +msgstr "حدد عضوًا آخر للترقية قبل المغادرة" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.promote-and-leave" +msgstr "قم بالترقية والمغادرة" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.select-memeber-to-promote" +msgstr "حدد عضوا للترقية" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.title" +msgstr "حدد عضوا للترقية" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.accept" +msgstr "ترك الفريق" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.message" +msgstr "هل أنت متأكد أنك تريد مغادرة هذا الفريق؟" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.title" +msgstr "مغادرة الفريق" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.accept" +msgstr "رقى" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.message" +msgstr "هل أنت متأكد أنك تريد ترقية هذا المستخدم إلى مالك؟" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.title" +msgstr "الترقية إلى مالك" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.accept" +msgstr "إزالة كمكتبة مشتركة" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.hint" +msgstr "" +"بمجرد إزالتها كمكتبة مشتركة ، ستتوقف مكتبة الملفات لهذا الملف عن كونها " +"متاحة للاستخدام بين بقية ملفاتك." + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.message" +msgstr "إزالة “%s” كمكتبة مشتركة" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.accept" +msgstr "تحديث المكون" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.cancel" +msgstr "إلغاﺀ" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.hint" +msgstr "" +"أنت على وشك تحديث مكون في مكتبة مشتركة. قد يؤثر هذا على الملفات الأخرى التي " +"تستخدمها." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.message" +msgstr "تحديث المكون في المكتبة المشتركة" + +#: src/app/main/ui/dashboard/team.cljs +msgid "notifications.invitation-email-sent" +msgstr "تم إرسال الدعوة بنجاح" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "notifications.profile-deletion-not-allowed" +msgstr "لا يمكنك حذف ملفّك الشخصي، قم بإحالة فريقك قبل المتابعة." + +#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs +msgid "notifications.profile-saved" +msgstr "تم حفظ الملف الشخصي بنجاح!" + +#: src/app/main/ui/settings/change_email.cljs +msgid "notifications.validation-email-sent" +msgstr "تم إرسال رسالة التحقق إلى %s. راجع بريدك الالكتروني!" + +#: src/app/main/ui/auth/recovery.cljs +msgid "profile.recovery.go-to-login" +msgstr "اذهب إلى تسجيل الدخول" + +#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "settings.multiple" +msgstr "مختلط" + +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.font-providers" +msgstr "موفرو الخطوط - %s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.fonts" +msgstr "الخطوط -٪ s - Penpot" + +#: src/app/main/ui/dashboard/projects.cljs +msgid "title.dashboard.projects" +msgstr "المشاريع - %s - Penpot" + +#: src/app/main/ui/dashboard/search.cljs +msgid "title.dashboard.search" +msgstr "بحث - ٪s - بينبوت" + +#: src/app/main/ui/dashboard/libraries.cljs +msgid "title.dashboard.shared-libraries" +msgstr "المكتبات المشتركة - ٪s - Penpot" + +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot - حرية التصميم لفرق العمل" + +#: src/app/main/ui/settings/feedback.cljs +msgid "title.settings.feedback" +msgstr "تقديم ملاحظات - Penpot" + +#: src/app/main/ui/settings/options.cljs +msgid "title.settings.options" +msgstr "الإعدادات - Penpot" + +#: src/app/main/ui/settings/password.cljs +msgid "title.settings.password" +msgstr "كلمة المرور - Penpot" + +#: src/app/main/ui/settings/profile.cljs +msgid "title.settings.profile" +msgstr "الملف الشخصي - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-members" +msgstr "الأعضاء - ٪s - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-settings" +msgstr "الإعدادات - ٪s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "title.viewer" +msgstr "٪s - وضع العرض - Penpot" + +#: src/app/main/ui/workspace.cljs +msgid "title.workspace" +msgstr "%s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.empty-state" +msgstr "لم يتم العثور على لوحات الرسم على الصفحة." + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.frame-not-found" +msgstr "لوح الرسم غير موجود." + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.dont-show-interactions" +msgstr "لا تظهر التفاعلات" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "تعديل الملف" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.fullscreen" +msgstr "ملء الشاشة" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.interactions" +msgstr "التفاعلات" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.copy-link" +msgstr "نسخ الرابط" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.create-link" +msgstr "إنشاء رابط" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.placeholder" +msgstr "سيظهر رابط المشاركة هنا" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.remove-link" +msgstr "إزالة الرابط" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.subtitle" +msgstr "أي شخص لديه الرابط سيكون لديه حق الوصول" + +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.title" +msgstr "مشاركة النموذج الأولي" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions" +msgstr "إظهار التفاعلات" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions-on-click" +msgstr "إظهار التفاعلات عند النقر" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.sitemap" +msgstr "خريطة الموقع" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hcenter" +msgstr "محاذاة الوسط الأفقي" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hdistribute" +msgstr "توزيع التباعد الأفقي" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hleft" +msgstr "محاذاة لليسار" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hright" +msgstr "محاذاة لليمين" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vbottom" +msgstr "محاذاة للأسفل" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vcenter" +msgstr "محاذاة للوسط عموديًا" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vdistribute" +msgstr "توزيع التباعد عموديًا" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vtop" +msgstr "محاذاة للأعلى" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.assets" +msgstr "أصول رقمية" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.box-filter-all" +msgstr "كل الأصول الرقمية" + +msgid "workspace.assets.box-filter-graphics" +msgstr "الرسومات" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.colors" +msgstr "الألوان" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.components" +msgstr "المكونات" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "أنشئ مجموعة" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group-hint" +msgstr "ستتم تسمية عناصرك تلقائيًا باسم \"المجموعة / العنصر\"" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.delete" +msgstr "حذف" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.duplicate" +msgstr "تكرار" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.edit" +msgstr "تعديل" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.file-library" +msgstr "مكتبة الملف" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.graphics" +msgstr "الرسومات" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group" +msgstr "مجموعة" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group-name" +msgstr "اسم المجموعة" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.libraries" +msgstr "المكتبات" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.not-found" +msgstr "لم يتم العثور على أصول رقمية" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename" +msgstr "إعادة تسمية" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename-group" +msgstr "إعادة تسمية المجموعة" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.search" +msgstr "البحث عن الأصول الرقمية" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.selected-count" +msgid_plural "workspace.assets.selected-count" +msgstr[0] "0" +msgstr[1] "1" +msgstr[2] "2" +msgstr[3] "بضع" +msgstr[4] "الكثير" +msgstr[5] "غير ذلك" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.shared" +msgstr "متشاركة" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.typography" +msgstr "صياغة الحروف" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-id" +msgstr "الخط" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-size" +msgstr "الحجم" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-variant-id" +msgstr "متغير" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.go-to-edit" +msgstr "الذهاب إلى ملف مكتبة الأنماط لتعديله" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.letter-spacing" +msgstr "تباعد الحروف" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.line-height" +msgstr "ارتفاع الخط" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/handoff/attributes/text.cljs, src/app/main/ui/handoff/attributes/text.cljs +msgid "workspace.assets.typography.sample" +msgstr "مثال" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.text-transform" +msgstr "تحويل النص" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.ungroup" +msgstr "إلغاء التجميع" + +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.linear" +msgstr "تدرج خطي" + +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.radial" +msgstr "تدرج شعاعي" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-dynamic-alignment" +msgstr "تعطيل المحاذاة الديناميكية" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-scale-text" +msgstr "تعطيل مقياس النص" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-snap-grid" +msgstr "تعطيل الانطباق على الشبكة" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-dynamic-alignment" +msgstr "تفعيل المحاذاة الديناميكية" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-scale-text" +msgstr "تفعيل مقياس النص" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-snap-grid" +msgstr "الانطباق على الشبكة" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-assets" +msgstr "إخفاء الأصول الرقمية" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-grid" +msgstr "إخفاء الشبكات" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-layers" +msgstr "إخفاء الطبقات" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-palette" +msgstr "إخفاء لوحة الألوان" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-rules" +msgstr "إخفاء القواعد" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.select-all" +msgstr "اختر الكل" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-assets" +msgstr "إظهار الأصول الرقمية" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-grid" +msgstr "أظهر تخطيط الجدول" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-layers" +msgstr "إظهار الطبقات" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-palette" +msgstr "إظهار لوح الألوان" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-rules" +msgstr "إظهار القواعد" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.save-error" +msgstr "خطأ في الحفظ" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saved" +msgstr "حفظ" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saving" +msgstr "جاري الحفظ" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.unsaved" +msgstr "التغييرات غير المحفوظة" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.viewer" +msgstr "عرض الوضع (٪s)" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.add" +msgstr "أضف" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.colors" +msgstr "٪s الألوان" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.big-thumbnails" +msgstr "صور مصغرة كبيرة" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.file-library" +msgstr "مكتبة الملف" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.recent-colors" +msgstr "الألوان الحديثة" + +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.save-color" +msgstr "حفظ نمط اللون" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.small-thumbnails" +msgstr "صور مصغرة صغيرة" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.components" +msgstr "٪s المكونات" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.file-library" +msgstr "مكتبة الملف" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.graphics" +msgstr "٪s الرسومات" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.in-this-file" +msgstr "المكتبات في هذا الملف" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.libraries" +msgstr "مكتبات" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.library" +msgstr "مكتبة" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-libraries-need-sync" +msgstr "لا توجد مكتبات مشتركة تحتاج إلى تحديث" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-matches-for" +msgstr "لم يتم العثور على مطابقات ل “%s“" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-shared-libraries-available" +msgstr "لا توجد مكتبات مشتركة متاحة" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.search-shared-libraries" +msgstr "ابحث في المكتبات المشتركة" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.shared-libraries" +msgstr "المكتبات المشتركة" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography" +msgstr "طباعة متعددة" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography-tooltip" +msgstr "قم بفك ارتباط كافة الأنماط المطبعية" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.typography" +msgstr "٪s الطباعة" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.update" +msgstr "تحديث" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.updates" +msgstr "التحديثات" + +msgid "workspace.library.all" +msgstr "جميع المكتبات" + +msgid "workspace.library.libraries" +msgstr "المكتبات" + +msgid "workspace.library.own" +msgstr "مكتباتي" + +msgid "workspace.library.store" +msgstr "المكتبات المخزنة" + +msgid "workspace.options.blur-options.background-blur" +msgstr "خلفية" + +msgid "workspace.options.blur-options.layer-blur" +msgstr "طبقة" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title" +msgstr "الضبابية" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title.group" +msgstr "ضبابية المجموعة" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title.multiple" +msgstr "الضبابية المحددة" + +#: src/app/main/ui/workspace/sidebar/options/page.cljs +msgid "workspace.options.canvas-background" +msgstr "خلفية قماشية" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs +msgid "workspace.options.component" +msgstr "المكونات" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints" +msgstr "القيود" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.bottom" +msgstr "أسفل" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.center" +msgstr "توسيط" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.fix-when-scrolling" +msgstr "الإصلاح عند التمرير" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.left" +msgstr "يسار" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.leftright" +msgstr "اليسار واليمين" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.right" +msgstr "يمين" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.scale" +msgstr "مقياس" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.top" +msgstr "أعلى" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.topbottom" +msgstr "أعلى وأسفل" + +#: src/app/main/ui/workspace/sidebar/options.cljs +msgid "workspace.options.design" +msgstr "تصميم" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export" +msgstr "تصدير" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export-object" +msgstr "تصدير الشكل" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +msgid "workspace.options.export.suffix" +msgstr "لاحقة" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.exporting-object" +msgstr "جارٍ التصدير …" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.fill" +msgstr "ملء" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.auto" +msgstr "آلي" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.column" +msgstr "الأعمدة" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.columns" +msgstr "الأعمدة" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.gutter" +msgstr "مزراب" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.height" +msgstr "ارتفاع" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.margin" +msgstr "الهامش" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.rows" +msgstr "الصفوف" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.set-default" +msgstr "تعيين كافتراضي" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.size" +msgstr "حجم" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type" +msgstr "نوع" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.bottom" +msgstr "أسفل" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.center" +msgstr "توسيط" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.left" +msgstr "اليسار" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.right" +msgstr "يمين" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.stretch" +msgstr "التمدد" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.top" +msgstr "أعلى" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.use-default" +msgstr "استخدم الافتراضي" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.width" +msgstr "عرض" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.row" +msgstr "الصفوف" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.square" +msgstr "مربع" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.title" +msgstr "الشبكة والتخطيطات" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.group-fill" +msgstr "ملء المجموعة" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "لون" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-burn" +msgstr "حرق لوني" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-dodge" +msgstr "تمويه لوني" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.darken" +msgstr "قاتم" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.difference" +msgstr "اختلاف" + +msgid "workspace.viewport.click-to-close-path" +msgstr "انقر لإغلاق المسار" \ No newline at end of file diff --git a/frontend/translations/ca.po b/frontend/translations/ca.po index 44ac3aa5d3..5ab7d6d072 100644 --- a/frontend/translations/ca.po +++ b/frontend/translations/ca.po @@ -1,15 +1,15 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-06-01 00:38+0000\n" -"Last-Translator: Antonio \n" -"Language-Team: Catalan \n" +"PO-Revision-Date: 2021-09-04 15:33+0000\n" +"Last-Translator: Rubén \n" +"Language-Team: Catalan " +"\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8.1-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -18,8 +18,8 @@ msgstr "Ja teniu un compte?" #: src/app/main/ui/auth/register.cljs msgid "auth.check-your-email" msgstr "" -"Reviseu el correu i cliqueu l'enllaç per verificar i començar a utilitzar el " -"Penpot." +"Reviseu el correu i feu clic en l'enllaç per a verificar i començar a " +"utilitzar el Penpot." #: src/app/main/ui/auth/recovery.cljs msgid "auth.confirm-password" @@ -36,8 +36,8 @@ msgstr "Ho voleu provar?" #: src/app/main/ui/auth/register.cljs msgid "auth.demo-warning" msgstr "" -"Aquest és un servei de PROVA. NO L'UTILITZEU en feines reals, ja que els " -"projectes s'esborraran periòdicament." +"Aquest és un servei de PROVA. NO L'UTILITZEU en treballs reals, ja que els " +"projectes s'eliminaran periòdicament." #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs msgid "auth.email" @@ -65,7 +65,7 @@ msgstr "Introduïu les vostres dades a continuació" #: src/app/main/ui/auth/login.cljs msgid "auth.login-title" -msgstr "Ens agrada tornar-vos a veure!" +msgstr "Ens agrada tornar a veure-vos!" #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs msgid "auth.login-with-github-submit" @@ -75,9 +75,17 @@ msgstr "Entreu amb GitHub" msgid "auth.login-with-gitlab-submit" msgstr "Entreu amb GitLab" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-google-submit" +msgstr "Entreu amb Google" + #: src/app/main/ui/auth/login.cljs msgid "auth.login-with-ldap-submit" -msgstr "Identifiqueu-vos amb LDAP" +msgstr "Entreu amb LDAP" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-oidc-submit" +msgstr "Entreu amb OpenID (SSO)" #: src/app/main/ui/auth/recovery.cljs msgid "auth.new-password" @@ -93,8 +101,7 @@ msgstr "La contrasenya s'ha canviat correctament" #: src/app/main/ui/auth/recovery_request.cljs msgid "auth.notifications.profile-not-verified" -msgstr "" -"El perfil encara no ha estat verificat. Verifiqueu-lo abans de continuar." +msgstr "El perfil encara no s'ha verificat, feu-ho abans de continuar." #: src/app/main/ui/auth/recovery_request.cljs msgid "auth.notifications.recovery-token-sent" @@ -148,18 +155,28 @@ msgstr "Creeu un compte" msgid "auth.sidebar-tagline" msgstr "La solució de codi obert per dissenyar i prototipar." +#: src/app/main/ui/auth/register.cljs +msgid "auth.terms-privacy-agreement" +msgstr "" +"En crear un nou compte, accepteu les nostres condicions del servei i la " +"política de privacitat." + #: src/app/main/ui/auth/register.cljs msgid "auth.verification-email-sent" msgstr "S'ha enviat un correu de verificació a" #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.add-shared" -msgstr "Afegeix una biblioteca compartida" +msgstr "Afegeix com a biblioteca compartida" #: src/app/main/ui/settings/profile.cljs msgid "dashboard.change-email" msgstr "Canvia el correu" +#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs +msgid "dashboard.copy-suffix" +msgstr "(còpia)" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.create-new-team" msgstr "+ Crea un nou equip" @@ -170,22 +187,63 @@ msgstr "El vostre Penpot" #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.delete-team" -msgstr "Suprimeix l'equip" +msgstr "Elimina l'equip" msgid "dashboard.draft-title" msgstr "Esborrany" +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate" +msgstr "Duplica" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "Duplica %s fitxers" + #: src/app/main/ui/dashboard/grid.cljs msgid "dashboard.empty-files" msgstr "Encara no teniu cap arxiu aquí" +msgid "dashboard.export-multi" +msgstr "Exporta %s fitxers" + +msgid "dashboard.export-single" +msgstr "Exporta el fitxer" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "S'ha eliminat el tipus de lletra" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Encara no teniu cap tipus de lletra personalitzat instal·lat." + +#, markdown +msgid "dashboard.fonts.hero-text1" +msgstr "" +"Els tipus de lletra web que pengeu aquí s'afegiran a la llista de famílies " +"tipogràfiques disponibles a les propietats del text dels fitxers d'aquest " +"equip. Els tipus de lletra amb el mateix nom de família s'agruparan en " +"**una sola família tipogràfica**. Podeu pujar tipus de lletra amb aquests " +"formats: **TTF, OTF i WOFF** (només és necessari un)." + +#, markdown +msgid "dashboard.fonts.hero-text2" +msgstr "" +"Només podeu pujar tipus de lletra de la vostra propietat o dels que tingueu " +"una llicència que us permeti utilitzar-los al Penpot. Teniu més informació " +"a la secció de drets de contingut de les [Condicions del servei del " +"Penpot](https://penpot.app/terms.html). També podeu llegir sobre " +"[llicenciament de tipus de lletra](https://www.typography.com/faq)." + +msgid "dashboard.import" +msgstr "Importa fitxers" + #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" msgstr "Convida a l'equip" #: src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.leave-team" -msgstr "Deixa l'equip" +msgstr "Abandona l'equip" #: src/app/main/ui/dashboard/libraries.cljs msgid "dashboard.libraries-title" @@ -195,14 +253,37 @@ msgstr "Biblioteques compartides" msgid "dashboard.loading-files" msgstr "S'estan carregant els fitxers…" +msgid "dashboard.loading-fonts" +msgstr "carregant els tipus de lletra…" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to" +msgstr "Mou a" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-multi" +msgstr "Mou %s fitxers a" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-other-team" +msgstr "Mou a un altre equip" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/files.cljs msgid "dashboard.new-file" msgstr "+ Fitxer nou" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-file-prefix" +msgstr "Fitxer nou" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.new-project" msgstr "+ Projecte nou" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-project-prefix" +msgstr "Projecte nou" + #: src/app/main/ui/dashboard/search.cljs msgid "dashboard.no-matches-for" msgstr "No s'ha trobat cap coincidència amb “%s“" @@ -221,16 +302,27 @@ msgstr "S'ha verificat l'adreça de correu" #: src/app/main/ui/settings/password.cljs msgid "dashboard.notifications.password-saved" -msgstr "La contrasenya s'ha desat correctament" +msgstr "La contrasenya s'ha desat correctament!" #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.num-of-members" msgstr "%s membres" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.open-in-new-tab" +msgstr "Obre el fitxer en una pestanya nova" + +msgid "dashboard.options" +msgstr "Opcions" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "Canvia la contrasenya" +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.pin-unpin" +msgstr "Fixa/Deixa de fixar" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.projects-title" msgstr "Projectes" @@ -267,6 +359,34 @@ msgstr "Selecciona un tema" msgid "dashboard.show-all-files" msgstr "Mostra tots els fitxers" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-delete-file" +msgstr "S'ha eliminat el fitxer" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-delete-project" +msgstr "S'ha eliminat el projecte" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-duplicate-file" +msgstr "S'ha duplicat el fitxer" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-duplicate-project" +msgstr "S'ha eliminat el projecte" + +#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-file" +msgstr "S'ha mogut el fitxer" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-files" +msgstr "S'han mogut els fitxers" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-move-project" +msgstr "S'ha mogut el projecte" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.switch-team" msgstr "Canvia d'equip" @@ -353,12 +473,20 @@ msgstr "El correu de confirmació ha de coincidir" #: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/settings/feedback.cljs, src/app/main/ui/dashboard/team.cljs msgid "errors.generic" -msgstr "Alguna cosa ha anat malament" +msgstr "Alguna cosa ha anat malament." #: src/app/main/ui/auth/login.cljs msgid "errors.google-auth-not-enabled" msgstr "L'autenticació amb Google està desactivada en aquest servidor" +#: src/app/main/ui/components/color_input.cljs +msgid "errors.invalid-color" +msgstr "El color no és vàlid" + +#: src/app/main/ui/auth/login.cljs +msgid "errors.ldap-disabled" +msgstr "L'autenticació LDAP està inhabilitada." + msgid "errors.media-format-unsupported" msgstr "El format d'imatge no està suportat (ha de ser SVG, JPG o PNG)." @@ -369,7 +497,8 @@ msgstr "La imatge és massa gran (ha de ser inferior a 5 MB)." #: src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs msgid "errors.media-type-mismatch" msgstr "" -"Sembla que el contingut de la imatge no coincideix amb l'extensió del fitxer." +"Sembla que el contingut de la imatge no coincideix amb l'extensió del " +"fitxer." #: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs msgid "errors.media-type-not-allowed" @@ -400,7 +529,16 @@ msgstr "" #: src/app/main/ui/auth/register.cljs msgid "errors.registration-disabled" -msgstr "El registre està desactivat" +msgstr "El registre està desactivat." + +msgid "errors.terms-privacy-agreement-invalid" +msgstr "" +"Heu d'acceptar les nostres condicions del servei i la política de " +"privacitat." + +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.token-expired" +msgstr "El codi ha caducat" #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" @@ -412,7 +550,7 @@ msgstr "Token desconegut" #: src/app/main/ui/auth/login.cljs msgid "errors.wrong-credentials" -msgstr "El nom d'usuari o la contrasenya sembla incorrecte" +msgstr "El nom d'usuari o la contrasenya sembla incorrecte." #: src/app/main/ui/settings/password.cljs msgid "errors.wrong-old-password" @@ -420,7 +558,7 @@ msgstr "La contrasenya anterior no és correcte" #: src/app/main/ui/settings/feedback.cljs msgid "feedback.chat-start" -msgstr "Uneix-te al xat." +msgstr "Uneix-me al xat" #: src/app/main/ui/settings/feedback.cljs msgid "feedback.chat-subtitle" @@ -441,8 +579,8 @@ msgstr "Uneix-te al fòrum colaboratiu de Penpot." #: src/app/main/ui/settings/feedback.cljs msgid "feedback.discussions-subtitle2" msgstr "" -"Pots fer i respondre preguntes, tenir converses obertes i seguir les " -"decisións que afecten al projecte" +"Podeu fer i respondre preguntes, tenir converses obertes i seguir les " +"decisions que afecten el projecte." #: src/app/main/ui/settings/feedback.cljs msgid "feedback.discussions-title" @@ -467,18 +605,828 @@ msgstr "Correu electrònic" msgid "generic.error" msgstr "S'ha produït un error" +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur" +msgstr "Difuminat" + +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur.value" +msgstr "Valor" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hex" +msgstr "HEX" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hsla" +msgstr "HSLA" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.rgba" +msgstr "RGBA" + +#: src/app/main/ui/handoff/attributes/fill.cljs +msgid "handoff.attributes.fill" +msgstr "Emplenat" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.download" +msgstr "Baixa la imatge original" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.height" +msgstr "Alçada" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.width" +msgstr "Amplada" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout" +msgstr "Disposició" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.height" +msgstr "Alçada" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.left" +msgstr "Esquerra" + +#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.radius" +msgstr "Radi" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.rotation" +msgstr "Rotació" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.top" +msgstr "Superior" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.width" +msgstr "Amplada" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow" +msgstr "Ombra" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.blur" +msgstr "B" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-x" +msgstr "X" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-y" +msgstr "Y" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.spread" +msgstr "S" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke" +msgstr "Traç" + +#, permanent +msgid "handoff.attributes.stroke.alignment.center" +msgstr "Centre" + +#, permanent +msgid "handoff.attributes.stroke.alignment.inner" +msgstr "Interior" + +#, permanent +msgid "handoff.attributes.stroke.alignment.outer" +msgstr "Exterior" + +msgid "handoff.attributes.stroke.style.dotted" +msgstr "Puntejat" + +msgid "handoff.attributes.stroke.style.mixed" +msgstr "Mixte" + +msgid "handoff.attributes.stroke.style.none" +msgstr "Cap" + +msgid "handoff.attributes.stroke.style.solid" +msgstr "Sòlid" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke.width" +msgstr "Amplada" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography" +msgstr "Tipografia" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-family" +msgstr "Família tipogràfica" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-size" +msgstr "Mida de la lletra" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-style" +msgstr "Estil de la lletra" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.letter-spacing" +msgstr "Espaiat de la lletra" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.line-height" +msgstr "Alçada de la línia" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-decoration" +msgstr "Decoració del text" + +msgid "handoff.attributes.typography.text-decoration.none" +msgstr "Cap" + +msgid "handoff.attributes.typography.text-decoration.strikethrough" +msgstr "Barrat" + +msgid "handoff.attributes.typography.text-decoration.underline" +msgstr "Subratllat" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-transform" +msgstr "Transformació del text" + +msgid "handoff.attributes.typography.text-transform.lowercase" +msgstr "Minúscules" + +msgid "handoff.attributes.typography.text-transform.none" +msgstr "Cap" + +msgid "handoff.attributes.typography.text-transform.titlecase" +msgstr "Inicials en majúscules" + +msgid "handoff.attributes.typography.text-transform.uppercase" +msgstr "Majúscules" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.code" +msgstr "Codi" + +msgid "handoff.tabs.code.selected.circle" +msgstr "Cercle" + +msgid "handoff.tabs.code.selected.curve" +msgstr "Corba" + +msgid "handoff.tabs.code.selected.frame" +msgstr "Taula de treball" + +msgid "handoff.tabs.code.selected.group" +msgstr "Grup" + +msgid "handoff.tabs.code.selected.image" +msgstr "Imatge" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.code.selected.multiple" +msgstr "%s seleccionats" + +msgid "handoff.tabs.code.selected.path" +msgstr "Camí" + +msgid "handoff.tabs.code.selected.rect" +msgstr "Rectangle" + +msgid "handoff.tabs.code.selected.svg-raw" +msgstr "SVG" + +msgid "handoff.tabs.code.selected.text" +msgstr "Text" + +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.info" +msgstr "Informació" + +msgid "history.alert-message" +msgstr "Esteu veient la versió %s" + msgid "labels.accept" msgstr "Acceptar" +msgid "labels.add-custom-font" +msgstr "Afegeix tipus de lletra" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.admin" +msgstr "Administració" + +#: src/app/main/ui/workspace/comments.cljs +msgid "labels.all" +msgstr "Tot" + +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.desc-message" +msgstr "" +"Sembla que heu d'esperar una mica i tornar a provar; estem realitzant un " +"petit manteniment dels nostres servidors." + +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.main-message" +msgstr "Error del servidor (Bad Gateway)" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.cancel" +msgstr "Cancel·la" + +msgid "labels.centered" +msgstr "Centrat" + +#: src/app/main/ui/dashboard/comments.cljs +msgid "labels.comments" +msgstr "Comentaris" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.confirm-password" +msgstr "Confirma la contrasenya" + +msgid "labels.content" +msgstr "Contingut" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "labels.create" +msgstr "Crea" + +#: src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team" +msgstr "Crea un nou equip" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team.placeholder" +msgstr "Introduïu el nom de l'equip nou" + +msgid "labels.custom-fonts" +msgstr "Tipus de lletra personalitzats" + +#: src/app/main/ui/settings/sidebar.cljs +msgid "labels.dashboard" +msgstr "Panell" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete" +msgstr "Elimina" + +#: src/app/main/ui/comments.cljs +msgid "labels.delete-comment" +msgstr "Elimina el comentari" + +#: src/app/main/ui/comments.cljs +msgid "labels.delete-comment-thread" +msgstr "Elimina el fil" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete-multi-files" +msgstr "Elimina %s fitxers" + +#: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.drafts" +msgstr "Esborranys" + +#: src/app/main/ui/comments.cljs +msgid "labels.edit" +msgstr "Edita" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.editor" +msgstr "Editor" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.email" +msgstr "Correu electrònic" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-disabled" +msgstr "Opinions desactivades" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-sent" +msgstr "S'ha enviat l'opinió" + +msgid "labels.font-family" +msgstr "Família de tipus de lletra" + +msgid "labels.font-providers" +msgstr "Proveïdors de tipus de lletra" + +msgid "labels.font-variants" +msgstr "Estils" + +msgid "labels.fonts" +msgstr "Tipus de lletra" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.give-feedback" +msgstr "Envia opinions" + msgid "labels.go-back" msgstr "Enrere" +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.hide-resolved-comments" +msgstr "Amaga els comentaris resolts" + +msgid "labels.icons" +msgstr "Icones" + +msgid "labels.images" +msgstr "Imatges" + +msgid "labels.installed-fonts" +msgstr "Tipus de lletra instal·lats" + +#: src/app/main/ui/static.cljs +msgid "labels.internal-error.desc-message" +msgstr "" +"Alguna cosa ha fallat. Torneu a provar l'operació i, si el problema " +"continua, poseu-vos en contacte amb el suport tècnic." + +#: src/app/main/ui/static.cljs +msgid "labels.internal-error.main-message" +msgstr "Error intern" + +#: src/app/main/ui/settings/options.cljs +msgid "labels.language" +msgstr "Llengua" + +#: src/app/main/ui/settings.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.logout" +msgstr "Tanca la sessió" + +msgid "labels.manage-fonts" +msgstr "Gestiona els tipus de lletra" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.members" +msgstr "Membres" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.name" +msgstr "Nom" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.new-password" +msgstr "Nova contrasenya" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/dashboard/comments.cljs +msgid "labels.no-comments-available" +msgstr "No teniu notificacions de comentaris pendents" + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.auth-info" +msgstr "Sessió iniciada com a" + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.desc-message" +msgstr "" +"És possible que aquesta pàgina no existeixi o que no tingueu permisos per a " +"accedir-hi." + +#: src/app/main/ui/static.cljs +msgid "labels.not-found.main-message" +msgstr "Vaja!" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.num-of-files" +msgid_plural "labels.num-of-files" +msgstr[0] "1 fitxer" +msgstr[1] "%s fitxers" + +msgid "labels.num-of-frames" +msgid_plural "labels.num-of-frames" +msgstr[0] "1 taula de treball" +msgstr[1] "%s taules de treball" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.num-of-projects" +msgid_plural "labels.num-of-projects" +msgstr[0] "1 projecte" +msgstr[1] "%s projectes" + +#: src/app/main/ui/settings/password.cljs +msgid "labels.old-password" +msgstr "Contrasenya antiga" + +#: src/app/main/ui/workspace/comments.cljs +msgid "labels.only-yours" +msgstr "Només els vostres" + +msgid "labels.or" +msgstr "o" + +#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.owner" +msgstr "Propietari" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.password" +msgstr "Contrasenya" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.permissions" +msgstr "Permisos" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.profile" +msgstr "Perfil" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.projects" +msgstr "Projectes" + msgid "labels.recent" msgstr "Recent" +#: src/app/main/ui/settings/sidebar.cljs +msgid "labels.release-notes" +msgstr "Notes de la versió" + +#: src/app/main/ui/workspace/libraries.cljs, src/app/main/ui/dashboard/team.cljs +msgid "labels.remove" +msgstr "Elimina" + +#: src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.rename" +msgstr "Canvia el nom" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.rename-team" +msgstr "Canvia el nom de l’equip" + +#: src/app/main/ui/static.cljs, src/app/main/ui/static.cljs, src/app/main/ui/static.cljs +msgid "labels.retry" +msgstr "Tornar a intentar-ho" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.role" +msgstr "Rol" + msgid "labels.save" msgstr "Desa" +msgid "labels.search-font" +msgstr "Cerca tipus de lletra" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.send" +msgstr "Envia" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.sending" +msgstr "S'està enviant…" + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.desc-message" +msgstr "Estem de manteniment programat dels nostres sistemes." + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.main-message" +msgstr "Servei no disponible" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.settings" +msgstr "Configuració" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.shared-libraries" +msgstr "Biblioteques compartides" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-all-comments" +msgstr "Mostra tots els comentaris" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-your-comments" +msgstr "Mostra només els vostres comentaris" + +#: src/app/main/ui/static.cljs +msgid "labels.sign-out" +msgstr "Tanca la sessió" + +#: src/app/main/ui/settings/profile.cljs +msgid "labels.update" +msgstr "Actualitza" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.update-team" +msgstr "Actualitza l'equip" + +msgid "labels.upload" +msgstr "Puja" + +msgid "labels.upload-custom-fonts" +msgstr "Puja tipus de lletra personalitzats" + +msgid "labels.uploading" +msgstr "S'està pujant…" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.viewer" +msgstr "Espectador" + +#: src/app/main/ui/comments.cljs +msgid "labels.write-new-comment" +msgstr "Escriu un comentari nou" + +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "media.loading" +msgstr "S'està carregant la imatge…" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.accept" +msgstr "Afegeix com a biblioteca compartida" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.hint" +msgstr "" +"Una vegada afegit com a Biblioteca compartida, els recursos de la " +"biblioteca d'aquest fitxer estaran disponibles per a utilitzar-los entre la " +"resta de fitxers." + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.message" +msgstr "Afegeix \"%s\" com a biblioteca compartida" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.confirm-email" +msgstr "Verifica el correu electrònic nou" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.info" +msgstr "" +"Us enviarem un correu electrònic a la vostra adreça actual \"%s\" per a " +"verificar la vostra identitat." + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.new-email" +msgstr "Nou correu electrònic" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.submit" +msgstr "Canvia el correu" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.title" +msgstr "Canvieu el vostre correu electrònic" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.cancel" +msgstr "Cancel·la i conserva el meu compte" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.confirm" +msgstr "Sí, elimina el meu compte" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.info" +msgstr "Si suprimiu el compte, perdreu tots els vostres projectes i arxius actuals." + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.title" +msgstr "Segur que voleu eliminar el compte?" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.accept" +msgstr "Elimina la conversa" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.message" +msgstr "" +"Segur que voleu eliminar aquesta conversa? S'eliminaran tots els comentaris " +"del fil." + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.title" +msgstr "Elimina la conversa" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.accept" +msgstr "Elimina el fitxer" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.message" +msgstr "Segur que voleu eliminar el fitxer?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.title" +msgstr "S'està eliminant el fitxer" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "Elimina fitxers" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "Segur que voleu eliminar %s fitxers?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "S'estan eliminant %s fitxers" + +msgid "modals.delete-font-variant.message" +msgstr "" +"Segur que voleu eliminar aquest estil de lletra? No es carregarà si " +"s'utilitza en un fitxer." + +msgid "modals.delete-font-variant.title" +msgstr "S'està eliminant l'estil de tipus de lletra" + +msgid "modals.delete-font.message" +msgstr "" +"Segur que voleu eliminar aquest tipus de lletra? No es carregarà si " +"s'utilitza en un fitxer." + +msgid "modals.delete-font.title" +msgstr "S'està eliminant el tipus de lletra" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.body" +msgstr "Segur que voleu eliminar la pàgina?" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.title" +msgstr "Elimina la pàgina" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.accept" +msgstr "Elimina el projecte" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.message" +msgstr "Segur que voleu eliminar el projecte?" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.title" +msgstr "Elimina el projecte" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.accept" +msgstr "Elimina l'equip" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.message" +msgstr "" +"Segur que voleu eliminar l'equip? Tots els projectes i fitxers associats a " +"l'equip s'eliminaran permanentment." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.title" +msgstr "S'està eliminant l'equip" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.accept" +msgstr "Elimina el membre" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.message" +msgstr "Segur que voleu eliminar aquest membre de l'equip?" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.title" +msgstr "Elimina el membre de l'equip" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member-confirm.accept" +msgstr "Envia una invitació" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member.title" +msgstr "Convida a unir-se a l'equip" + +msgid "modals.leave-and-reassign.forbiden" +msgstr "" +"No es pot abandonar l'equip si no hi ha cap altre membre capaç d'ascendir a " +"propietari. És possible que vulgueu eliminar l'equip." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint1" +msgstr "Sou el propietari de %s." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint2" +msgstr "Seleccioneu un altre membre per a ascendir-lo abans d'abandonar" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.promote-and-leave" +msgstr "Ascendeix i abandona" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.select-memeber-to-promote" +msgstr "Seleccioneu un membre per a ascendir" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.title" +msgstr "Seleccioneu un membre per a ascendir" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.accept" +msgstr "Abandona l'equip" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.message" +msgstr "Segur que voleu deixar l'equip?" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.title" +msgstr "S'està abandonant l'equip" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.accept" +msgstr "Ascendeix" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.message" +msgstr "Segur que voleu ascendir aquest usuari a propietari?" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.title" +msgstr "Ascendeix a propietari" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.accept" +msgstr "Elimina com a biblioteca compartida" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.hint" +msgstr "" +"Una vegada suprimida com a Biblioteca compartida, la biblioteca d'aquest " +"fitxer deixarà d'estar disponible per a la resta de fitxers." + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.message" +msgstr "Elimina \"%s\" com a biblioteca compartida" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.accept" +msgstr "Actualitza el component" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.cancel" +msgstr "Cancel·la" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.hint" +msgstr "" +"Esteu a punt d'actualitzar un component d'una biblioteca compartida. Això " +"pot afectar altres fitxers que l'utilitzen." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.message" +msgstr "Actualitzar un component d'una biblioteca compartida" + +#: src/app/main/ui/dashboard/team.cljs +msgid "notifications.invitation-email-sent" +msgstr "La invitació s'ha enviat correctament" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "notifications.profile-deletion-not-allowed" +msgstr "" +"No podeu eliminar el vostre perfil. Reassigneu els vostres equips abans de " +"continuar." + +#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs +msgid "notifications.profile-saved" +msgstr "El perfil s'ha desat correctament!" + +#: src/app/main/ui/settings/change_email.cljs +msgid "notifications.validation-email-sent" +msgstr "S'ha enviat un correu electrònic de verificació a %s. Reviseu el correu!" + +#: src/app/main/ui/auth/recovery.cljs +msgid "profile.recovery.go-to-login" +msgstr "Vés a l'inici de sessió" + +#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +#, fuzzy +msgid "settings.multiple" +msgstr "Divers" + +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.font-providers" +msgstr "Proveïdors de tipus de lletra - %s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.fonts" +msgstr "Tipus de lletra - %s - Penpot" + #: src/app/main/ui/dashboard/projects.cljs msgid "title.dashboard.projects" msgstr "Projectes - %s - Penpot" @@ -487,333 +1435,1288 @@ msgstr "Projectes - %s - Penpot" msgid "title.dashboard.search" msgstr "Cerca - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "El codi ha caducat" +#: src/app/main/ui/dashboard/libraries.cljs +msgid "title.dashboard.shared-libraries" +msgstr "Biblioteques compartides - %s - Penpot" -msgid "errors.terms-privacy-agreement-invalid" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot - Llibertat de disseny per a equips" + +#: src/app/main/ui/settings/feedback.cljs +msgid "title.settings.feedback" +msgstr "Envia opinions - Penpot" + +#: src/app/main/ui/settings/options.cljs +msgid "title.settings.options" +msgstr "Configuració - Penpot" + +#: src/app/main/ui/settings/password.cljs +msgid "title.settings.password" +msgstr "Contrasenya - Penpot" + +#: src/app/main/ui/settings/profile.cljs +msgid "title.settings.profile" +msgstr "Perfil - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-members" +msgstr "Membres - %s - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-settings" +msgstr "Configuració - %s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "title.viewer" +msgstr "%s - Mode de visualització - Penpot" + +#: src/app/main/ui/workspace.cljs +msgid "title.workspace" +msgstr "%s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.empty-state" +msgstr "No s'ha trobat cap taula de treball a la pàgina." + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.frame-not-found" +msgstr "No s'ha trobat la taula de treball." + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.dont-show-interactions" +msgstr "No mostris les interaccions" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "Edita el fitxer" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.fullscreen" +msgstr "Pantalla completa" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.interactions" +msgstr "Interaccions" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.copy-link" +msgstr "Copia l'enllaç" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.create-link" +msgstr "Crea un enllaç" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.placeholder" +msgstr "L'enllaç compartit apareixerà aquí" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.remove-link" +msgstr "Elimina l'enllaç" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.subtitle" +msgstr "Qualsevol persona amb l'enllaç hi tindrà accés" + +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.title" +msgstr "Comparteix el prototip" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions" +msgstr "Mostra les interaccions" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions-on-click" +msgstr "Mostra les interaccions en fer clic" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.sitemap" +msgstr "Mapa del lloc" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hcenter" +msgstr "Alinea el centre horitzontal" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hdistribute" +msgstr "Distribueix l'espaiat horitzontal" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hleft" +msgstr "Alinea a l'esquerra" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hright" +msgstr "Alinea a la dreta" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vbottom" +msgstr "Alinea la part inferior" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vcenter" +msgstr "Alinea el centre vertical" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vdistribute" +msgstr "Distribueix l'espaiat vertical" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vtop" +msgstr "Alinea la part superior" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.assets" +msgstr "Recursos" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.box-filter-all" +msgstr "Tots els recursos" + +msgid "workspace.assets.box-filter-graphics" +msgstr "Gràfics" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.colors" +msgstr "Colors" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.components" +msgstr "Components" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "Crea un grup" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group-hint" msgstr "" -"Heu d'acceptar les nostres condicions del servei i la política de privacitat." +"Els elements s'anomenaran automàticament com a \"nom del grup / nom de " +"l'element\"" -#: src/app/main/ui/auth/login.cljs -msgid "errors.ldap-disabled" -msgstr "L'autenticació LDAP està inhabilitada." +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.delete" +msgstr "Elimina" -#: src/app/main/ui/components/color_input.cljs -msgid "errors.invalid-color" -msgstr "El color no és vàlid" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.success-move-project" -msgstr "S'ha mogut el projecte" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-move-files" -msgstr "S'han mogut els fitxers" - -#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-move-file" -msgstr "S'ha mogut el fitxer" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.success-duplicate-project" -msgstr "S'ha eliminat el projecte" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-duplicate-file" -msgstr "S'ha duplicat el fitxer" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.success-delete-project" -msgstr "S'ha eliminat el projecte" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.success-delete-file" -msgstr "S'ha eliminat el fitxer" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "dashboard.pin-unpin" -msgstr "Fixa/Deixa de fixar" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.open-in-new-tab" -msgstr "Obre el fitxer en una pestanya nova" - -#: src/app/main/data/dashboard.cljs -msgid "dashboard.new-project-prefix" -msgstr "Projecte nou" - -#: src/app/main/data/dashboard.cljs -msgid "dashboard.new-file-prefix" -msgstr "Fitxer nou" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to-other-team" -msgstr "Mou a un altre equip" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to-multi" -msgstr "Mou %s fitxers a" - -#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.move-to" -msgstr "Mou a" - -#, markdown -msgid "dashboard.fonts.hero-text2" -msgstr "" -"Només podeu pujar tipus de lletra de la vostra propietat o dels que tingueu " -"una llicència que us permeti utilitzar-los al Penpot. Teniu més informació a " -"la secció de drets de contingut de les [Condicions del servei del " -"Penpot](https://penpot.app/terms.html). També podeu llegir sobre [" -"llicenciament de tipus de lletra](https://www.typography.com/faq)." - -#, markdown -msgid "dashboard.fonts.hero-text1" -msgstr "" -"Els tipus de lletra web que pengeu aquí s'afegiran a la llista de famílies " -"tipogràfiques disponibles a les propietats del text dels fitxers d'aquest " -"equip. Els tipus de lletra amb el mateix nom de família s'agruparan en **una " -"sola família tipogràfica**. Podeu pujar tipus de lletra amb aquests formats: " -"**TTF, OTF i WOFF** (només és necessari un)." - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate-multi" -msgstr "Duplica %s fitxers" - -#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate" +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.duplicate" msgstr "Duplica" -#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs -msgid "dashboard.copy-suffix" -msgstr "(còpia)" +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.edit" +msgstr "Edita" -#: src/app/main/ui/auth/register.cljs -msgid "auth.terms-privacy-agreement" -msgstr "" -"En crear un nou compte, accepteu les nostres condicions del servei i la " -"política de privacitat." +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.file-library" +msgstr "Biblioteca del fitxer" -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-oidc-submit" -msgstr "Entreu amb OpenID (SSO)" +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.graphics" +msgstr "Gràfics" -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-google-submit" -msgstr "Entreu amb Google" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group" +msgstr "Agrupa" -#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs -msgid "labels.admin" -msgstr "Administració" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group-name" +msgstr "Nom del grup" -msgid "history.alert-message" -msgstr "Esteu veient la versió %s" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.libraries" +msgstr "Biblioteques" -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.info" -msgstr "Informació" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.not-found" +msgstr "No s'han trobat recursos" -msgid "handoff.tabs.code.selected.text" -msgstr "Text" +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename" +msgstr "Canvia el nom" -msgid "handoff.tabs.code.selected.svg-raw" -msgstr "SVG" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename-group" +msgstr "Canvia el nom del grup" -msgid "handoff.tabs.code.selected.rect" -msgstr "Rectangle" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.search" +msgstr "Cerca recursos" -msgid "handoff.tabs.code.selected.path" -msgstr "Camí" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.selected-count" +msgid_plural "workspace.assets.selected-count" +msgstr[0] "%s element seleccionat" +msgstr[1] "%s elements seleccionats" -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.code.selected.multiple" -msgstr "%s seleccionats" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.shared" +msgstr "COMPARTIT" -msgid "handoff.tabs.code.selected.image" -msgstr "Imatge" +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.typography" +msgstr "Tipografies" -msgid "handoff.tabs.code.selected.group" -msgstr "Grup" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-id" +msgstr "Tipus de lletra" -msgid "handoff.tabs.code.selected.frame" -msgstr "Taula de treball" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-size" +msgstr "Mida" -msgid "handoff.tabs.code.selected.curve" -msgstr "Corba" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-variant-id" +msgstr "Variant" -msgid "handoff.tabs.code.selected.circle" -msgstr "Cercle" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.go-to-edit" +msgstr "Vés al fitxer de la biblioteca d'estils per a editar-lo" -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.letter-spacing" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.letter-spacing" msgstr "Espaiat de la lletra" -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.code" -msgstr "Codi" - -msgid "handoff.attributes.typography.text-transform.uppercase" -msgstr "Majúscules" - -msgid "handoff.attributes.typography.text-transform.titlecase" -msgstr "Inicials en majúscules" - -msgid "handoff.attributes.typography.text-transform.none" -msgstr "Cap" - -msgid "handoff.attributes.typography.text-transform.lowercase" -msgstr "Minúscules" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-transform" -msgstr "Transformació del text" - -msgid "handoff.attributes.typography.text-decoration.underline" -msgstr "Subratllat" - -msgid "handoff.attributes.typography.text-decoration.strikethrough" -msgstr "Barrat" - -msgid "handoff.attributes.typography.text-decoration.none" -msgstr "Cap" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-decoration" -msgstr "Decoració del text" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.line-height" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.line-height" msgstr "Alçada de la línia" -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-style" -msgstr "Estil de la lletra" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/handoff/attributes/text.cljs, src/app/main/ui/handoff/attributes/text.cljs +msgid "workspace.assets.typography.sample" +msgstr "Ag" -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-size" -msgstr "Mida de la lletra" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.text-transform" +msgstr "Transforma el text" -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-family" -msgstr "Família tipogràfica" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.ungroup" +msgstr "Desagrupa" -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography" -msgstr "Tipografia" +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.linear" +msgstr "Degradat lineal" -#: src/app/main/ui/handoff/attributes/stroke.cljs -msgid "handoff.attributes.stroke.width" -msgstr "Amplada" +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.radial" +msgstr "Degradat radial" -msgid "handoff.attributes.stroke.style.solid" -msgstr "Sòlid" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-dynamic-alignment" +msgstr "Desactiva l'alineació dinàmica" -msgid "handoff.attributes.stroke.style.none" -msgstr "Cap" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-scale-text" +msgstr "Desactiva l'escalat del text" -msgid "handoff.attributes.stroke.style.mixed" -msgstr "Mesclat" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-snap-grid" +msgstr "Desactiva l'ajust a la quadrícula" -msgid "handoff.attributes.stroke.style.dotted" -msgstr "Puntejat" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-dynamic-alignment" +msgstr "Activa l'alineació dinàmica" -#, permanent -msgid "handoff.attributes.stroke.alignment.outer" -msgstr "Exterior" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-scale-text" +msgstr "Activa l'escalat del text" -#, permanent -msgid "handoff.attributes.stroke.alignment.inner" -msgstr "Interior" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-snap-grid" +msgstr "Ajusta a la quadrícula" -#, permanent -msgid "handoff.attributes.stroke.alignment.center" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-assets" +msgstr "Amaga els recursos" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-grid" +msgstr "Amaga la quadrícula" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-layers" +msgstr "Amaga les capes" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-palette" +msgstr "Amaga la paleta de colors" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-rules" +msgstr "Amaga les regles" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.select-all" +msgstr "Selecciona-ho tot" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-assets" +msgstr "Mostra els recursos" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-grid" +msgstr "Mostra la quadrícula" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-layers" +msgstr "Mostra les capes" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-palette" +msgstr "Mostra la paleta de colors" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-rules" +msgstr "Mostra les regles" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.save-error" +msgstr "S'ha produït un error en desar" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saved" +msgstr "Desat" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saving" +msgstr "S'està desant" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.unsaved" +msgstr "Canvis sense desar" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.viewer" +msgstr "Mode de visualització (%s)" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.add" +msgstr "Afegeix" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.colors" +msgstr "%s colors" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.big-thumbnails" +msgstr "Miniatures grans" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.file-library" +msgstr "Biblioteca del fitxer" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.recent-colors" +msgstr "Colors recents" + +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.save-color" +msgstr "Desa l'estil de color" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.small-thumbnails" +msgstr "Miniatures petites" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.components" +msgstr "%s components" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.file-library" +msgstr "Biblioteca del fitxer" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.graphics" +msgstr "%s gràfics" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.in-this-file" +msgstr "BIBLIOTEQUES EN AQUEST FITXER" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.libraries" +msgstr "BIBLIOTEQUES" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.library" +msgstr "BIBLIOTECA" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-libraries-need-sync" +msgstr "No hi ha biblioteques compartides pendents d'actualitzar" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-matches-for" +msgstr "No s'ha trobat cap coincidència per a “%s“" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-shared-libraries-available" +msgstr "No hi ha biblioteques compartides disponibles" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.search-shared-libraries" +msgstr "Cerca biblioteques compartides" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.shared-libraries" +msgstr "BIBLIOTEQUES COMPARTIDES" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography" +msgstr "Diverses tipografies" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography-tooltip" +msgstr "Desvincula totes les tipografies" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.typography" +msgstr "%s tipografies" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.update" +msgstr "Actualitza" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.updates" +msgstr "ACTUALITZACIONS" + +msgid "workspace.library.all" +msgstr "Totes les biblioteques" + +msgid "workspace.library.libraries" +msgstr "Biblioteques" + +msgid "workspace.library.own" +msgstr "Les meves biblioteques" + +msgid "workspace.library.store" +msgstr "Predeterminades" + +msgid "workspace.options.blur-options.background-blur" +msgstr "Fons" + +msgid "workspace.options.blur-options.layer-blur" +msgstr "Capa" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title" +msgstr "Difuminat" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title.group" +msgstr "Difuminat de grup" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title.multiple" +msgstr "Difuminat de selecció" + +#: src/app/main/ui/workspace/sidebar/options/page.cljs +msgid "workspace.options.canvas-background" +msgstr "Fons del llenç" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs +msgid "workspace.options.component" +msgstr "Component" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints" +msgstr "Restriccions" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.bottom" +msgstr "Baix" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.center" msgstr "Centre" -#: src/app/main/ui/handoff/attributes/stroke.cljs -msgid "handoff.attributes.stroke" -msgstr "Traç" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.fix-when-scrolling" +msgstr "Fixa en desplaçar" -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.spread" -msgstr "S" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.offset-y" -msgstr "Y" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.offset-x" -msgstr "X" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.blur" -msgstr "B" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow" -msgstr "Ombra" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.width" -msgstr "Amplada" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.top" -msgstr "Superior" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.rotation" -msgstr "Rotació" - -#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.radius" -msgstr "Radi" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.left" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.left" msgstr "Esquerra" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.height" -msgstr "Alçada" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.leftright" +msgstr "Esquerra i dreta" -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout" -msgstr "Disposició" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.right" +msgstr "Dreta" -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.width" -msgstr "Amplada" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.scale" +msgstr "Escala" -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.height" -msgstr "Alçada" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.top" +msgstr "Dalt" -#: src/app/main/ui/handoff/attributes/image.cljs -msgid "handoff.attributes.image.download" -msgstr "Baixa la imatge original" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.topbottom" +msgstr "Dalt i baix" -#: src/app/main/ui/handoff/attributes/fill.cljs -msgid "handoff.attributes.fill" +#: src/app/main/ui/workspace/sidebar/options.cljs +msgid "workspace.options.design" +msgstr "Disseny" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export" +msgstr "Exporta" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export-object" +msgstr "Exporta la forma" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +msgid "workspace.options.export.suffix" +msgstr "Sufix" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.exporting-object" +msgstr "S'està exportant…" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.fill" msgstr "Emplenat" -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.rgba" -msgstr "RGBA" +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.auto" +msgstr "Automàtic" -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.hsla" -msgstr "HSLA" +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.column" +msgstr "Columnes" -#: src/app/main/ui/handoff/attributes/common.cljs -msgid "handoff.attributes.color.hex" -msgstr "HEX" +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.columns" +msgstr "Columnes" -#: src/app/main/ui/handoff/attributes/blur.cljs -msgid "handoff.attributes.blur.value" -msgstr "Valor" +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.gutter" +msgstr "Espaiat" -#: src/app/main/ui/handoff/attributes/blur.cljs -msgid "handoff.attributes.blur" +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.height" +msgstr "Alçada" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.margin" +msgstr "Marge" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.rows" +msgstr "Files" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.set-default" +msgstr "Estableix com a predeterminat" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.size" +msgstr "Mida" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type" +msgstr "Tipus" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.bottom" +msgstr "Baix" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.center" +msgstr "Centre" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.left" +msgstr "Esquerra" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.right" +msgstr "Dreta" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.stretch" +msgstr "Estira" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.top" +msgstr "Dalt" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.use-default" +msgstr "Usa valors predeterminats" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.width" +msgstr "Ample" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.row" +msgstr "Files" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.square" +msgstr "Quadrat" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.title" +msgstr "Quadrícula i disposicions" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.group-fill" +msgstr "Emplenament de grup" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.group-stroke" +msgstr "Vora del grup" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "Color" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-burn" +msgstr "Crema el color" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-dodge" +msgstr "Aclareix el color" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.darken" +msgstr "Enfosqueix" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.difference" +msgstr "Resta" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.exclusion" +msgstr "Exclusió" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hard-light" +msgstr "Llum forta" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hue" +msgstr "To" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.lighten" +msgstr "Il·lumina" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.luminosity" +msgstr "Lluminositat" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.multiply" +msgstr "Multiplica" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.normal" +msgstr "Llum normal" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.overlay" +msgstr "Superposa" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.saturation" +msgstr "Saturació" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.screen" +msgstr "Pantalla" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.soft-light" +msgstr "Llum suau" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title" +msgstr "Capa" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.group" +msgstr "Agrupa les capes" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.multiple" +msgstr "Capes seleccionades" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.navigate-to" +msgstr "Vés a" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.none" +msgstr "Cap" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.position" +msgstr "Posició" + +#: src/app/main/ui/workspace/sidebar/options.cljs +msgid "workspace.options.prototype" +msgstr "Prototip" + +msgid "workspace.options.radius" +msgstr "Radi" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.all-corners" +msgstr "Tots els cantons" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.single-corners" +msgstr "Cantons individuals" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.rotation" +msgstr "Rotació" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-a-shape" +msgstr "" +"Seleccioneu una forma, una taula de treball o un grup per a arrossegar una " +"connexió a un altra taula de treball." + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-artboard" +msgstr "Selecciona la taula de treball" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.selection-fill" +msgstr "Emplenament de selecció" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.selection-stroke" +msgstr "Vora de selecció" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.blur" msgstr "Difuminat" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.drop-shadow" +msgstr "Ombra caiguda" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.inner-shadow" +msgstr "Ombra interior" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsetx" +msgstr "X" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsety" +msgstr "Y" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.spread" +msgstr "Difusió" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title" +msgstr "Ombra" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.group" +msgstr "Ombra de grup" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.multiple" +msgstr "Ombres de selecció" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.size" +msgstr "Mida" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +msgid "workspace.options.size-presets" +msgstr "Mides predefinides" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke" +msgstr "Vora" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.center" +msgstr "Centre" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dashed" +msgstr "A ratlles" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dotted" +msgstr "Puntejat" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.inner" +msgstr "Interior" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.mixed" +msgstr "Mixte" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.outer" +msgstr "Exterior" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.solid" +msgstr "Sòlid" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-bottom" +msgstr "Alinea baix" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-center" +msgstr "Alinea el centre" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-justify" +msgstr "Justifica" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-left" +msgstr "Alinea a l'esquerra" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-middle" +msgstr "Alinea al centre" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-right" +msgstr "Alinea a la dreta" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-top" +msgstr "Alinea dalt" + +msgid "workspace.options.text-options.decoration" +msgstr "Decoració" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-ltr" +msgstr "LTR" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-rtl" +msgstr "RTL" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.google" +msgstr "Google" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-height" +msgstr "Alt automàtic" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-width" +msgstr "Ample automàtic" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-fixed" +msgstr "Fix" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.letter-spacing" +msgstr "Espaiat de la lletra" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.line-height" +msgstr "Alçada de la línia" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.lowercase" +msgstr "Minúscules" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.none" +msgstr "Cap" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.preset" +msgstr "Predefinit" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.strikethrough" +msgstr "Ratllat" + +msgid "workspace.options.text-options.text-case" +msgstr "Majús./Minús." + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title" +msgstr "Text" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title-group" +msgstr "Text del grup" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title-selection" +msgstr "Text de la selecció" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.titlecase" +msgstr "Inicials en majúscules" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.underline" +msgstr "Subratllat" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.uppercase" +msgstr "Majúscules" + +msgid "workspace.options.text-options.vertical-align" +msgstr "Alineació vertical" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.use-play-button" +msgstr "" +"Utilitzeu el botó de «play» de la capçalera per a executar la vista de " +"prototip." + +msgid "workspace.path.actions.add-node" +msgstr "Afegeix un node (%s)" + +msgid "workspace.path.actions.delete-node" +msgstr "Elimina el node (%s)" + +msgid "workspace.path.actions.draw-nodes" +msgstr "Dibuixa els nodes (%s)" + +msgid "workspace.path.actions.join-nodes" +msgstr "Uneix els nodes (%s)" + +msgid "workspace.path.actions.make-corner" +msgstr "Converteix a cantó (%s)" + +msgid "workspace.path.actions.make-curve" +msgstr "Converteix a corba (%s)" + +msgid "workspace.path.actions.merge-nodes" +msgstr "Combina els nodes (%s)" + +msgid "workspace.path.actions.move-nodes" +msgstr "Mou els nodes (%s)" + +msgid "workspace.path.actions.separate-nodes" +msgstr "Separa els nodes (%s)" + +msgid "workspace.path.actions.snap-nodes" +msgstr "Alinea els nodes (%s)" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.back" +msgstr "Envia-ho al fons" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.backward" +msgstr "Envia-ho darrere" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy" +msgstr "Copia" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.create-component" +msgstr "Crea un component" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.cut" +msgstr "Retalla" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.delete" +msgstr "Elimina" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.detach-instance" +msgstr "Desconnecta la instància" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.duplicate" +msgstr "Duplica" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.edit" +msgstr "Edita" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-horizontal" +msgstr "Volteja horitzontalment" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-vertical" +msgstr "Volteja verticalment" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.forward" +msgstr "Porta-ho endavant" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.front" +msgstr "Porta-ho a primer pla" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.go-main" +msgstr "Vés al fitxer del component principal" + +#: src/app/main/ui/workspace/context_menu.cljs +#, fuzzy +msgid "workspace.shape.menu.group" +msgstr "Agrupa" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.hide" +msgstr "Amaga" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.lock" +msgstr "Bloca" + +#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.mask" +msgstr "Màscara" + +#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.paste" +msgstr "Enganxa" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.reset-overrides" +msgstr "Desfés les modificacions" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show" +msgstr "Mostra" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show-main" +msgstr "Vés al component principal" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.ungroup" +msgstr "Desagrupa" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.unlock" +msgstr "Desbloca" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.unmask" +msgstr "Desemmascara" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.update-main" +msgstr "Actualitza el component principal" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.history" +msgstr "Historial (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.layers" +msgstr "Capes (%s)" + +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs +msgid "workspace.sidebar.options.svg-attrs.title" +msgstr "Atributs SVG importats" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "workspace.sidebar.sitemap" +msgstr "Pàgines" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.sitemap" +msgstr "Mapa del lloc" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.assets" +msgstr "Recursos (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.color-palette" +msgstr "Paleta de colors (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.comments" +msgstr "Comentaris (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.curve" +msgstr "Corba (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.ellipse" +msgstr "El·lipse (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.frame" +msgstr "Taula de treball (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.image" +msgstr "Imatge (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.move" +msgstr "Mou" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.path" +msgstr "Camí (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.rect" +msgstr "Rectangle (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.text" +msgstr "Text (%s)" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.empty" +msgstr "Encara no hi ha canvis a l'historial" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.delete" +msgstr "S'ha eliminat %s" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.modify" +msgstr "S'ha modificat %s" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.move" +msgstr "Objectes moguts" + +msgid "workspace.undo.entry.multiple.circle" +msgstr "cercles" + +msgid "workspace.undo.entry.multiple.color" +msgstr "colors" + +msgid "workspace.undo.entry.multiple.component" +msgstr "components" + +msgid "workspace.undo.entry.multiple.curve" +msgstr "corbes" + +msgid "workspace.undo.entry.multiple.frame" +msgstr "taula de treball" + +msgid "workspace.undo.entry.multiple.group" +msgstr "grups" + +msgid "workspace.undo.entry.multiple.media" +msgstr "recursos gràfics" + +msgid "workspace.undo.entry.multiple.multiple" +msgstr "objectes" + +msgid "workspace.undo.entry.multiple.page" +msgstr "pàgines" + +msgid "workspace.undo.entry.multiple.path" +msgstr "camins" + +msgid "workspace.undo.entry.multiple.rect" +msgstr "rectangles" + +msgid "workspace.undo.entry.multiple.shape" +msgstr "formes" + +msgid "workspace.undo.entry.multiple.text" +msgstr "textos" + +msgid "workspace.undo.entry.multiple.typography" +msgstr "tipus de lletra" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.new" +msgstr "Nou %s" + +msgid "workspace.undo.entry.single.circle" +msgstr "cercle" + +msgid "workspace.undo.entry.single.color" +msgstr "color" + +msgid "workspace.undo.entry.single.component" +msgstr "component" + +msgid "workspace.undo.entry.single.curve" +msgstr "corba" + +msgid "workspace.undo.entry.single.frame" +msgstr "taula de treball" + +msgid "workspace.undo.entry.single.group" +msgstr "grup" + +msgid "workspace.undo.entry.single.image" +msgstr "imatge" + +msgid "workspace.undo.entry.single.media" +msgstr "gràfic" + +msgid "workspace.undo.entry.single.multiple" +msgstr "objecte" + +msgid "workspace.undo.entry.single.page" +msgstr "pàgina" + +msgid "workspace.undo.entry.single.path" +msgstr "camí" + +msgid "workspace.undo.entry.single.rect" +msgstr "rectangle" + +msgid "workspace.undo.entry.single.shape" +msgstr "forma" + +msgid "workspace.undo.entry.single.text" +msgstr "text" + +msgid "workspace.undo.entry.single.typography" +msgstr "tipus de lletra" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.unknown" +msgstr "Operació sobre %s" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.title" +msgstr "Historial" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.dismiss" +msgstr "Descarta" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.there-are-updates" +msgstr "Hi ha actualitzacions a les biblioteques compartides" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.update" +msgstr "Actualitza" + +msgid "workspace.viewport.click-to-close-path" +msgstr "Feu clic per a tancar el camí" \ No newline at end of file diff --git a/frontend/translations/de.po b/frontend/translations/de.po index 4763752014..b66d51f920 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -1,15 +1,15 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-05-25 12:31+0000\n" -"Last-Translator: Yannik Rödel \n" -"Language-Team: German \n" +"PO-Revision-Date: 2021-06-27 08:33+0000\n" +"Last-Translator: nautilusx \n" +"Language-Team: German " +"\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.7.1-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -75,10 +75,18 @@ msgstr "Einloggen mit Github" msgid "auth.login-with-gitlab-submit" msgstr "Einloggen mit Gitlab" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-google-submit" +msgstr "Einloggen mit Google" + #: src/app/main/ui/auth/login.cljs msgid "auth.login-with-ldap-submit" msgstr "Anmelden mit LDAP" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-oidc-submit" +msgstr "Einloggen mit OpenID (SSO)" + #: src/app/main/ui/auth/recovery.cljs msgid "auth.new-password" msgstr "Geben Sie ein neues Passwort ein" @@ -149,6 +157,12 @@ msgstr "Konto erstellen" msgid "auth.sidebar-tagline" msgstr "Die Open-Source-Lösung für Design und Prototyping." +#: src/app/main/ui/auth/register.cljs +msgid "auth.terms-privacy-agreement" +msgstr "" +"Wenn Sie ein neues Konto erstellen, stimmen Sie unseren Nutzungsbedingungen " +"und Datenschutzrichtlinien zu." + #: src/app/main/ui/auth/register.cljs msgid "auth.verification-email-sent" msgstr "Wir haben eine Bestätigungs-E-Mail gesendet an" @@ -184,10 +198,39 @@ msgstr "Entwurf" msgid "dashboard.duplicate" msgstr "Duplizieren" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "%s Dateien duplizieren" + #: src/app/main/ui/dashboard/grid.cljs msgid "dashboard.empty-files" msgstr "Sie haben hier noch keine Dateien" +msgid "dashboard.export-multi" +msgstr "%s Dateien exportieren" + +msgid "dashboard.export-single" +msgstr "Datei exportieren" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Schriftart gelöscht" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Sie haben noch keine benutzerdefinierten Schriftarten installiert." + +#, markdown +msgid "dashboard.fonts.hero-text1" +msgstr "" +"Jede Webschriftart, die Sie hier hochladen, wird der Liste der Schriftarten " +"hinzugefügt, die in den Texteigenschaften der Dateien dieses Teams " +"verfügbar ist. Schriftarten mit dem gleichen Schriftfamilien-Namen werden " +"als **eine einzige Schriftfamilie** gruppiert. Sie können Schriftarten in " +"den folgenden Formaten hochladen: **TTF, OTF und WOFF** (nur eine wird " +"benötigt)." + +msgid "dashboard.import" +msgstr "Dateien importieren" + #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" msgstr "Zum Team einladen" @@ -204,6 +247,9 @@ msgstr "Gemeinsam genutzte Bibliotheken" msgid "dashboard.loading-files" msgstr "laden Ihrer Dateien …" +msgid "dashboard.loading-fonts" +msgstr "laden Ihrer Schriftarten …" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.move-to" msgstr "Verschieben nach" @@ -216,10 +262,18 @@ msgstr "Zu anderem Team verschieben" msgid "dashboard.new-file" msgstr "+ Neue Datei" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-file-prefix" +msgstr "Neue Datei" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.new-project" msgstr "+ Neues Projekt" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-project-prefix" +msgstr "Neues Projekt" + #: src/app/main/ui/dashboard/search.cljs msgid "dashboard.no-matches-for" msgstr "Keine Übereinstimmungen für “%s“ gefunden" @@ -248,6 +302,9 @@ msgstr "%s Mitglieder" msgid "dashboard.open-in-new-tab" msgstr "Datei in neuem Tab öffnen" +msgid "dashboard.options" +msgstr "Optionen" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "Passwort ändern" @@ -408,6 +465,10 @@ msgstr "Etwas ist schief gelaufen." msgid "errors.google-auth-not-enabled" msgstr "Die Authentifizierung mit Google ist im Backend deaktiviert" +#: src/app/main/ui/components/color_input.cljs +msgid "errors.invalid-color" +msgstr "Ungültige Farbe" + #: src/app/main/ui/auth/login.cljs msgid "errors.ldap-disabled" msgstr "Die LDAP-Authentifizierung ist deaktiviert." @@ -456,6 +517,11 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Die Registrierung ist derzeit deaktiviert." +msgid "errors.terms-privacy-agreement-invalid" +msgstr "" +"Sie müssen unsere Nutzungsbedingungen und Datenschutzrichtlinien " +"akzeptieren." + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Ein unerwarteter Fehler ist aufgetreten." @@ -771,10 +837,18 @@ msgstr "Passwort bestätigen" msgid "labels.content" msgstr "Inhalt" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "labels.create" +msgstr "Erstellen" + #: src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/dashboard/team_form.cljs msgid "labels.create-team" msgstr "Neues Team erstellen" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team.placeholder" +msgstr "Neuen Teamnamen eingeben" + #: src/app/main/ui/settings/sidebar.cljs msgid "labels.dashboard" msgstr "Dashboard" @@ -791,6 +865,10 @@ msgstr "Kommentar löschen" msgid "labels.delete-comment-thread" msgstr "Thread löschen" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete-multi-files" +msgstr "%s Dateien löschen" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "labels.drafts" msgstr "Entwürfe" @@ -815,6 +893,12 @@ msgstr "Feedback deaktiviert" msgid "labels.feedback-sent" msgstr "Feedback gesendet" +msgid "labels.font-variants" +msgstr "Stile" + +msgid "labels.fonts" +msgstr "Schriftarten" + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.give-feedback" msgstr "Feedback geben" @@ -832,6 +916,9 @@ msgstr "Icons" msgid "labels.images" msgstr "Bilder" +msgid "labels.installed-fonts" +msgstr "Installierte Schriftarten" + #: src/app/main/ui/static.cljs msgid "labels.internal-error.desc-message" msgstr "" @@ -850,6 +937,9 @@ msgstr "Sprache" msgid "labels.logout" msgstr "Abmelden" +msgid "labels.manage-fonts" +msgstr "Schriftarten verwalten" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.members" msgstr "Mitglieder" @@ -900,6 +990,9 @@ msgstr "Altes Passwort" msgid "labels.only-yours" msgstr "Nur Ihre" +msgid "labels.or" +msgstr "oder" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.owner" msgstr "Eigentümer" @@ -950,6 +1043,9 @@ msgstr "Rolle" msgid "labels.save" msgstr "Speichern" +msgid "labels.search-font" +msgstr "Schriftart suchen" + #: src/app/main/ui/settings/feedback.cljs msgid "labels.send" msgstr "Senden" @@ -970,6 +1066,10 @@ msgstr "Service nicht verfügbar" msgid "labels.settings" msgstr "Einstellungen" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Prototyp teilen" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Gemeinsam genutzte Bibliotheken" @@ -1086,6 +1186,21 @@ msgstr "Sind Sie sicher, dass Sie diese Datei löschen wollen?" msgid "modals.delete-file-confirm.title" msgstr "Datei löschen" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "Dateien löschen" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "Sind Sie sicher, dass Sie %s Dateien löschen möchten?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "Lösche %s Dateien" + +msgid "modals.delete-font.title" +msgstr "Schriftart löschen" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs msgid "modals.delete-page.body" msgstr "Sind Sie sicher, dass Sie diese Seite löschen wollen?" @@ -1329,10 +1444,6 @@ msgstr "Link entfernen" msgid "viewer.header.share.subtitle" msgstr "Jeder mit dem Link hat Zugriff" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Prototyp teilen" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Interaktionen anzeigen" @@ -2412,31 +2523,4 @@ msgid "workspace.updates.update" msgstr "Aktualisieren" msgid "workspace.viewport.click-to-close-path" -msgstr "Klicken Sie, um den Pfad zu schließen" - -#, markdown -msgid "dashboard.fonts.hero-text1" -msgstr "" -"Jede Webschriftart, die Sie hier hochladen, wird der Liste der Schriftarten " -"hinzugefügt, die in den Texteigenschaften der Dateien dieses Teams verfügbar " -"ist. Schriftarten mit dem gleichen Schriftfamilien-Namen werden als **eine " -"einzige Schriftfamilie** gruppiert. Sie können Schriftarten in den folgenden " -"Formaten hochladen: **TTF, OTF und WOFF** (nur eine wird benötigt)." - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate-multi" -msgstr "%s Dateien duplizieren" - -#: src/app/main/ui/auth/register.cljs -msgid "auth.terms-privacy-agreement" -msgstr "" -"Wenn Sie ein neues Konto erstellen, stimmen Sie unseren Nutzungsbedingungen " -"und Datenschutzrichtlinien zu." - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-oidc-submit" -msgstr "Einloggen mit OpenID (SSO)" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-google-submit" -msgstr "Einloggen mit Google" +msgstr "Klicken Sie, um den Pfad zu schließen" \ No newline at end of file diff --git a/frontend/translations/el.po b/frontend/translations/el.po index b8a58c0116..3812b41826 100644 --- a/frontend/translations/el.po +++ b/frontend/translations/el.po @@ -966,6 +966,10 @@ msgstr "Η υπηρεσία δεν είναι διαθέσιμη" msgid "labels.settings" msgstr "Σύνθεση" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Μοιραστείτε το link" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Κοινόχρηστες βιβλιοθήκες" @@ -1279,10 +1283,6 @@ msgstr "Κατάργηση link" msgid "viewer.header.share.subtitle" msgstr "Όποιος έχει τον link θα έχει πρόσβαση" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Μοιραστείτε το link" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Εμφάνιση αλληλεπιδράσεων" diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 3c07c0e8a7..80da61909b 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -167,6 +167,44 @@ msgstr "" msgid "auth.verification-email-sent" msgstr "We've sent a verification email to" +msgid "common.share-link.confirm-deletion-link-description" +msgstr "" +"Are you sure you want to remove this link? If you do it, it's no longer be " +"available for anyone" + +msgid "common.share-link.get-link" +msgstr "Get link" + +msgid "common.share-link.link-copied-success" +msgstr "Link copied successfully" + +msgid "common.share-link.link-deleted-success" +msgstr "Link deleted successfully" + +msgid "common.share-link.permissions-can-access" +msgstr "Can access" + +msgid "common.share-link.permissions-can-view" +msgstr "Can view" + +msgid "common.share-link.permissions-hint" +msgstr "Anyone with link will have access" + +msgid "common.share-link.remove-link" +msgstr "Remove link" + +msgid "common.share-link.title" +msgstr "Share prototypes" + +msgid "common.share-link.view-all-pages" +msgstr "All pages" + +msgid "common.share-link.view-current-page" +msgstr "Only this page" + +msgid "common.share-link.view-selected-pages" +msgstr "Selected pages" + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.add-shared" msgstr "Add as Shared Library" @@ -206,6 +244,9 @@ msgstr "Duplicate %s files" msgid "dashboard.empty-files" msgstr "You still have no files here" +msgid "dashboard.export-frames" +msgstr "Export artboards to PDF..." + msgid "dashboard.export-multi" msgstr "Export %s files" @@ -855,9 +896,6 @@ msgstr "You are seeing version %s" msgid "labels.accept" msgstr "Accept" -msgid "labels.close" -msgstr "Close" - msgid "labels.add-custom-font" msgstr "Add custom font" @@ -886,6 +924,9 @@ msgstr "Cancel" msgid "labels.centered" msgstr "Center" +msgid "labels.close" +msgstr "Close" + #: src/app/main/ui/dashboard/comments.cljs msgid "labels.comments" msgstr "Comments" @@ -919,6 +960,9 @@ msgstr "Custom fonts" msgid "labels.dashboard" msgstr "Dashboard" +msgid "labels.default" +msgstr "default" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "labels.delete" msgstr "Delete" @@ -943,6 +987,9 @@ msgstr "Drafts" msgid "labels.edit" msgstr "Edit" +msgid "labels.edit-file" +msgstr "Edit file" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.editor" msgstr "Editor" @@ -1008,6 +1055,9 @@ msgstr "Internal Error" msgid "labels.language" msgstr "Language" +msgid "labels.link" +msgstr "Link" + #: src/app/main/ui/settings.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.logout" msgstr "Logout" @@ -1144,6 +1194,9 @@ msgstr "Service Unavailable" msgid "labels.settings" msgstr "Settings" +msgid "labels.share-prototype" +msgstr "Share prototype" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Shared Libraries" @@ -1160,6 +1213,12 @@ msgstr "Show only yours comments" msgid "labels.sign-out" msgstr "Sign out" +msgid "labels.skip" +msgstr "Skip" + +msgid "labels.start" +msgstr "Start" + #: src/app/main/ui/settings/profile.cljs msgid "labels.update" msgstr "Update" @@ -1181,6 +1240,9 @@ msgstr "Uploading…" msgid "labels.viewer" msgstr "Viewer" +msgid "labels.workspace" +msgstr "Workspace" + #: src/app/main/ui/comments.cljs msgid "labels.write-new-comment" msgstr "Write new comment" @@ -1444,6 +1506,118 @@ msgstr "Profile saved successfully!" msgid "notifications.validation-email-sent" msgstr "Verification email sent to %s. Check your email!" +msgid "onboarding.contrib.alt" +msgstr "Open Source" + +msgid "onboarding.contrib.desc1" +msgstr "" +"Penpot is Open Source, made by and for the community. If you want to " +"collaborate, you are more than welcome!" + +msgid "onboarding.contrib.desc2.1" +msgstr "You can access the" + +msgid "onboarding.contrib.desc2.2" +msgstr "and follow the contribution instructions :)" + +msgid "onboarding.contrib.link" +msgstr "project on github" + +msgid "onboarding.contrib.title" +msgstr "Open Source Contributor?" + +msgid "onboarding.slide.0.alt" +msgstr "Create designs" + +msgid "onboarding.slide.0.desc1" +msgstr "Create beautiful user interfaces in collaboration with all team members." + +msgid "onboarding.slide.0.desc2" +msgstr "Maintain consistency at scale with components, libraries and design systems." + +msgid "onboarding.slide.0.title" +msgstr "Design libraries, styles and components" + +msgid "onboarding.slide.1.alt" +msgstr "Interactive prototypes" + +msgid "onboarding.slide.1.desc1" +msgstr "Create rich interactions to mimic the product behaviour." + +msgid "onboarding.slide.1.desc2" +msgstr "" +"Share to stakeholders, present proposals to your team and start user " +"testing with your designs, all in one place." + +msgid "onboarding.slide.1.title" +msgstr "Bring your designs to life with interactions" + +msgid "onboarding.slide.2.alt" +msgstr "Get feedback" + +msgid "onboarding.slide.2.desc1" +msgstr "" +"All team members working simultaneously with real time design multiplayer " +"and centralised comments, ideas and feedback right over the designs." + +msgid "onboarding.slide.2.title" +msgstr "Get feedback, present and share your work" + +msgid "onboarding.slide.3.alt" +msgstr "Handoff and lowcode" + +msgid "onboarding.slide.3.desc1" +msgstr "" +"Sync the design and code of all your components and styles and get code " +"snippets." + +msgid "onboarding.slide.3.desc2" +msgstr "" +"Get and provide code specifications like markup (SVG, HTML) or styles (CSS, " +"Less, Stylus…)." + +msgid "onboarding.slide.3.title" +msgstr "One shared source of truth" + +msgid "onboarding.team.create.button" +msgstr "Create a team" + +msgid "onboarding.team.create.desc1" +msgstr "" +"Are you working with someone? Create a team to work together on projects " +"and share design assets." + +msgid "onboarding.team.create.input-placeholder" +msgstr "Enter new team name" + +msgid "onboarding.team.create.title" +msgstr "Create team" + +msgid "onboarding.team.start.button" +msgstr "Start right away" + +msgid "onboarding.team.start.desc1" +msgstr "" +"Jump right away into Penpot and start designing by your own. You will still " +"have the chance to create teams later." + +msgid "onboarding.team.start.title" +msgstr "Start designing" + +msgid "onboarding.welcome.alt" +msgstr "Penpot" + +msgid "onboarding.welcome.desc1" +msgstr "We are very happy to introduce you to the very first Alpha release." + +msgid "onboarding.welcome.desc2" +msgstr "" +"Penpot is still at development stage and there will be constant updates. We " +"hope you enjoy the first stable version." + +msgid "onboarding.welcome.title" +msgstr "Welcome to Penpot!" + #: src/app/main/ui/auth/recovery.cljs msgid "profile.recovery.go-to-login" msgstr "Go to login" @@ -1520,22 +1694,27 @@ msgstr "No artboards found on the page." msgid "viewer.frame-not-found" msgstr "Artboard not found." +msgid "viewer.header.comments-section" +msgstr "Comments" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.dont-show-interactions" msgstr "Don't show interactions" -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.edit-file" -msgstr "Edit file" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.fullscreen" msgstr "Full Screen" +msgid "viewer.header.handsoff-section" +msgstr "Handsoff" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.interactions" msgstr "Interactions" +msgid "viewer.header.interactions-section" +msgstr "Interactions" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.share.copy-link" msgstr "Copy link" @@ -1556,10 +1735,6 @@ msgstr "Remove link" msgid "viewer.header.share.subtitle" msgstr "Anyone with the link will have access" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Share prototype" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Show interactions" @@ -1841,10 +2016,22 @@ msgstr "Big thumbnails" msgid "workspace.libraries.colors.file-library" msgstr "File library" +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.hsv" +msgstr "HSV" + #: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs msgid "workspace.libraries.colors.recent-colors" msgstr "Recent colors" +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.rgb-complementary" +msgstr "RGB Complementary" + +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.rgba" +msgstr "RGBA" + #: src/app/main/ui/workspace/colorpicker.cljs msgid "workspace.libraries.colors.save-color" msgstr "Save color style" @@ -2282,6 +2469,38 @@ msgstr "Size presets" msgid "workspace.options.stroke" msgstr "Stroke" +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.circle-marker" +msgstr "Circle marker" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.diamond-marker" +msgstr "Diamond marker" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.line-arrow" +msgstr "Line arrow" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.none" +msgstr "None" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.round" +msgstr "Round" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.square" +msgstr "Square" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.square-marker" +msgstr "Square marker" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.triangle-arrow" +msgstr "Triangle arrow" + #: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs msgid "workspace.options.stroke.center" msgstr "Center" @@ -2750,3 +2969,9 @@ msgstr "Update" msgid "workspace.viewport.click-to-close-path" msgstr "Click to close the path" + +msgid "viewer.breaking-change.message" +msgstr "Sorry!" + +msgid "viewer.breaking-change.description" +msgstr "This shareable link is no longer valid. Create a new one or ask the owner for a new one. diff --git a/frontend/translations/es.po b/frontend/translations/es.po index d6c2bc4e97..f2c97d48e8 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-06-18 09:19+0000\n" -"Last-Translator: andy \n" +"PO-Revision-Date: 2021-09-04 15:33+0000\n" +"Last-Translator: Rubén \n" "Language-Team: Spanish " "\n" "Language: es\n" @@ -9,7 +9,7 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7\n" +"X-Generator: Weblate 4.8.1-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -171,6 +171,44 @@ msgstr "" msgid "auth.verification-email-sent" msgstr "Hemos enviado un email de verificación a" +msgid "common.share-link.confirm-deletion-link-description" +msgstr "" +"¿Estas seguro que quieres eliminar el enlace? Si lo haces, el enlace dejara " +"de funcionar para todos" + +msgid "common.share-link.get-link" +msgstr "Obtener enlace" + +msgid "common.share-link.link-copied-success" +msgstr "Enlace copiado satisfactoriamente" + +msgid "common.share-link.link-deleted-success" +msgstr "Enlace eliminado correctamente" + +msgid "common.share-link.permissions-can-access" +msgstr "Puede acceder a" + +msgid "common.share-link.permissions-can-view" +msgstr "Puede ver" + +msgid "common.share-link.permissions-hint" +msgstr "Cualquiera con el enlace puede acceder" + +msgid "common.share-link.remove-link" +msgstr "Eliminar enlace" + +msgid "common.share-link.title" +msgstr "Compartir prototipos" + +msgid "common.share-link.view-all-pages" +msgstr "Todas las paginas" + +msgid "common.share-link.view-current-page" +msgstr "Solo esta pagina" + +msgid "common.share-link.view-selected-pages" +msgstr "Paginas seleccionadas" + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.add-shared" msgstr "Añadir como Biblioteca Compartida" @@ -210,6 +248,15 @@ msgstr "Duplicar %s archivos" msgid "dashboard.empty-files" msgstr "Todavía no hay ningún archivo aquí" +msgid "dashboard.export-frames" +msgstr "Exportar tableros a PDF..." + +msgid "dashboard.export-multi" +msgstr "Exportar %s archivos" + +msgid "dashboard.export-single" +msgstr "Exportar archivo" + msgid "dashboard.export.detail" msgstr "* Pueden incluir components, gráficos, colores y/o tipografias." @@ -855,9 +902,6 @@ msgstr "Estás viendo la versión %s" msgid "labels.accept" msgstr "Aceptar" -msgid "labels.close" -msgstr "Cerrar" - msgid "labels.add-custom-font" msgstr "Añadir fuentes personalizada" @@ -886,6 +930,9 @@ msgstr "Cancelar" msgid "labels.centered" msgstr "Centrado" +msgid "labels.close" +msgstr "Cerrar" + #: src/app/main/ui/dashboard/comments.cljs msgid "labels.comments" msgstr "Comentarios" @@ -908,7 +955,6 @@ msgstr "Crear" msgid "labels.create-team" msgstr "Crea un nuevo equipo" -#: src/app/main/ui/dashboard/team_form.cljs msgid "labels.create-team.placeholder" msgstr "Introduce un nuevo nombre de equipo" @@ -919,6 +965,9 @@ msgstr "Fuentes personalizadas" msgid "labels.dashboard" msgstr "Panel" +msgid "labels.default" +msgstr "por defecto" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "labels.delete" msgstr "Borrar" @@ -943,6 +992,10 @@ msgstr "Borradores" msgid "labels.edit" msgstr "Editar" +#: src/app/main/ui/viewer/header.cljs +msgid "labels.edit-file" +msgstr "Editar archivo" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.editor" msgstr "Editor" @@ -1008,6 +1061,9 @@ msgstr "Error interno" msgid "labels.language" msgstr "Idioma" +msgid "labels.link" +msgstr "Enlace" + #: src/app/main/ui/settings.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.logout" msgstr "Salir" @@ -1144,6 +1200,10 @@ msgstr "El servicio no está disponible" msgid "labels.settings" msgstr "Configuración" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Compartir prototipo" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Bibliotecas Compartidas" @@ -1181,6 +1241,9 @@ msgstr "Subiendo…" msgid "labels.viewer" msgstr "Visualizador" +msgid "labels.workspace" +msgstr "Espacio de trabajo" + #: src/app/main/ui/comments.cljs msgid "labels.write-new-comment" msgstr "Escribir un nuevo comentario" @@ -1512,22 +1575,27 @@ msgstr "No se ha encontrado ningún tablero." msgid "viewer.frame-not-found" msgstr "No se encuentra el tablero." +msgid "viewer.header.comments-section" +msgstr "Comentarios" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.dont-show-interactions" msgstr "No mostrar interacciones" -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.edit-file" -msgstr "Editar archivo" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.fullscreen" msgstr "Pantalla completa" +msgid "viewer.header.handsoff-section" +msgstr "Handsoff" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.interactions" msgstr "Interacciones" +msgid "viewer.header.interactions-section" +msgstr "Interacciones" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.share.copy-link" msgstr "Copiar enlace" @@ -1548,10 +1616,6 @@ msgstr "Eliminar enlace" msgid "viewer.header.share.subtitle" msgstr "Cualquiera con el enlace podrá acceder" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Compartir prototipo" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Mostrar interacciones" @@ -1835,10 +1899,22 @@ msgstr "Miniaturas grandes" msgid "workspace.libraries.colors.file-library" msgstr "Biblioteca del archivo" +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.hsv" +msgstr "HSV" + #: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs msgid "workspace.libraries.colors.recent-colors" msgstr "Colores recientes" +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.rgb-complementary" +msgstr "RGB Complementario" + +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.rgba" +msgstr "RGBA" + #: src/app/main/ui/workspace/colorpicker.cljs msgid "workspace.libraries.colors.save-color" msgstr "Guardar estilo de color" @@ -2278,6 +2354,38 @@ msgstr "Tamaños predefinidos" msgid "workspace.options.stroke" msgstr "Borde" +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.circle-marker" +msgstr "Marcador círculo" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.diamond-marker" +msgstr "Marcador diamante" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.line-arrow" +msgstr "Flecha de línea" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.none" +msgstr "Ninguno" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.round" +msgstr "Redondo" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.square" +msgstr "Cuadrado" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.square-marker" +msgstr "Marcador cuadrado" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke-cap.triangle-arrow" +msgstr "Flecha triángulo" + #: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs msgid "workspace.options.stroke.center" msgstr "Centro" @@ -2746,3 +2854,9 @@ msgstr "Actualizar" msgid "workspace.viewport.click-to-close-path" msgstr "Pulsar para cerrar la ruta" + +msgid "viewer.breaking-change.message" +msgstr "¡Lo sentimos!" + +msgid "viewer.breaking-change.description" +msgstr "Este link compartido ya no funciona. Crea uno nuevo o pídelo a la persona que lo creó." diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index b43da10802..3b98ea2394 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-05-13 08:47+0000\n" -"Last-Translator: Andrey Antukh \n" +"PO-Revision-Date: 2021-08-10 23:33+0000\n" +"Last-Translator: Voxybuns \n" "Language-Team: French " "\n" "Language: fr\n" @@ -9,7 +9,7 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -75,10 +75,18 @@ msgstr "Se connecter via Github" msgid "auth.login-with-gitlab-submit" msgstr "Se connecter via Gitlab" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-google-submit" +msgstr "Se connecter via Google" + #: src/app/main/ui/auth/login.cljs msgid "auth.login-with-ldap-submit" msgstr "Se connecter via LDAP" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-oidc-submit" +msgstr "Se connecter via OpenID (SSO)" + #: src/app/main/ui/auth/recovery.cljs msgid "auth.new-password" msgstr "Saisissez un nouveau mot de passe" @@ -147,6 +155,12 @@ msgstr "Créer un compte" msgid "auth.sidebar-tagline" msgstr "La solution Open Source pour la conception et le prototypage." +#: src/app/main/ui/auth/register.cljs +msgid "auth.terms-privacy-agreement" +msgstr "" +"En créant un compte, vous acceptez nos conditions générales d'utilisation " +"et notre politique de confidentialité." + #: src/app/main/ui/auth/register.cljs msgid "auth.verification-email-sent" msgstr "Nous avons envoyé un e-mail de vérification à" @@ -159,6 +173,10 @@ msgstr "Ajouter une Bibliothèque Partagée" msgid "dashboard.change-email" msgstr "Changer adresse e‑mail" +#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs +msgid "dashboard.copy-suffix" +msgstr "(copie)" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.create-new-team" msgstr "+ Créer nouvelle équipe" @@ -174,10 +192,51 @@ msgstr "Supprimer l’équipe" msgid "dashboard.draft-title" msgstr "Brouillon" +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate" +msgstr "Dupliquer" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "Dupliquer %s fichiers" + #: src/app/main/ui/dashboard/grid.cljs msgid "dashboard.empty-files" msgstr "Vous n’avez encore aucun fichier ici" +msgid "dashboard.export-multi" +msgstr "Exporter %s fichiers" + +msgid "dashboard.export-single" +msgstr "Exporter le fichier" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Police supprimée" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Vous n'avez installé aucune police personnalisée." + +#, markdown +msgid "dashboard.fonts.hero-text1" +msgstr "" +"Toute police Web que vous téléchargez sera ajoutée à la liste de polices de " +"caractères disponible dans les propriétés de texte des fichiers de cette " +"équipe. Les polices du même nom de famille de polices seront groupées en " +"**une seule famille de polices**. Vous pouvez télécharger les polices au " +"formats suivants : **TTF, OTF et WOFF** (un seul format est nécessaire)." + +#, markdown +msgid "dashboard.fonts.hero-text2" +msgstr "" +"Ne téléchargez que des polices que vous possédez ou dont la license vous " +"permet de les utiliser dans Penpot. Vous trouverez plus d'informations dans " +"la section Propriété des Contenus des [conditions générales d'utilisation " +"de Penpot](https://penpot.app/terms.html). Vous pouvez également vous " +"renseigner sur les [licenses de polices](https://www.typography.com/faq)." + +msgid "dashboard.import" +msgstr "Importer fichiers" + #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" msgstr "Inviter dans l’équipe" @@ -194,14 +253,37 @@ msgstr "Bibliothèques Partagées" msgid "dashboard.loading-files" msgstr "chargement de vos fichiers…" +msgid "dashboard.loading-fonts" +msgstr "chargement de vos polices…" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to" +msgstr "Déplacer vers" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-multi" +msgstr "Déplacer %s fichiers vers" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-other-team" +msgstr "Déplacer vers une autre équipe" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/files.cljs msgid "dashboard.new-file" msgstr "+ Nouveau fichier" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-file-prefix" +msgstr "Nouveau fichier" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.new-project" msgstr "+ Nouveau projet" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-project-prefix" +msgstr "Nouveau projet" + #: src/app/main/ui/dashboard/search.cljs msgid "dashboard.no-matches-for" msgstr "Aucune correspondance pour « %s »" @@ -226,10 +308,21 @@ msgstr "Mot de passe enregistré avec succès !" msgid "dashboard.num-of-members" msgstr "%s membres" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.open-in-new-tab" +msgstr "Ouvrir fichier dans un nouvel onglet" + +msgid "dashboard.options" +msgstr "Options" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "Changer le mot de passe" +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.pin-unpin" +msgstr "Épingler/Désépingler" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.projects-title" msgstr "Projets" @@ -266,6 +359,34 @@ msgstr "Sélectionnez un thème" msgid "dashboard.show-all-files" msgstr "Voir tous les fichiers" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-delete-file" +msgstr "Votre fichier a été supprimé avec succès" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-delete-project" +msgstr "Votre projet a été supprimé avec succès" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-duplicate-file" +msgstr "Votre fichier a été dupliqué avec succès" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-duplicate-project" +msgstr "Votre projet a été dupliqué avec succès" + +#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-file" +msgstr "Votre fichier a été déplacé avec succès" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-files" +msgstr "Vos fichiers ont été déplacés avec succès" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-move-project" +msgstr "Votre projet a été déplacé avec succès" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.switch-team" msgstr "Changer d’équipe" @@ -342,6 +463,10 @@ msgstr "Adresse e‑mail déjà utilisée" msgid "errors.email-already-validated" msgstr "Adresse e‑mail déjà validée." +#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs +msgid "errors.email-has-permanent-bounces" +msgstr "L'adresse e-mail « %s » a un taux de rebond trop élevé." + #: src/app/main/ui/settings/change_email.cljs msgid "errors.email-invalid-confirmation" msgstr "L’adresse e‑mail de confirmation doit correspondre" @@ -350,6 +475,18 @@ msgstr "L’adresse e‑mail de confirmation doit correspondre" msgid "errors.generic" msgstr "Un problème s’est produit." +#: src/app/main/ui/auth/login.cljs +msgid "errors.google-auth-not-enabled" +msgstr "Authentification via Google désactivée dans le backend" + +#: src/app/main/ui/components/color_input.cljs +msgid "errors.invalid-color" +msgstr "Couleur invalide" + +#: src/app/main/ui/auth/login.cljs +msgid "errors.ldap-disabled" +msgstr "Authentification LDAP désactivée." + msgid "errors.media-format-unsupported" msgstr "Le format d’image n’est pas supporté (doit être svg, jpg ou png)." @@ -367,6 +504,12 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "L’image ne semble pas être valide." +#: src/app/main/ui/dashboard/team.cljs +msgid "errors.member-is-muted" +msgstr "" +"L'adresse e-mail du profil que vous invitez est ignorée (signalée comme " +"spam ou taux de rebond élevé)." + msgid "errors.network" msgstr "Impossible de se connecter au serveur principal." @@ -378,14 +521,33 @@ msgstr "Le mot de passe de confirmation doit correspondre" msgid "errors.password-too-short" msgstr "Le mot de passe doit contenir au moins 8 caractères" +#: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs +msgid "errors.profile-is-muted" +msgstr "" +"L'adresse e-mail de votre profil est ignorée (signalée comme spam ou taux " +"de rebond élevé)." + #: src/app/main/ui/auth/register.cljs msgid "errors.registration-disabled" msgstr "L’enregistrement est actuellement désactivé." +msgid "errors.terms-privacy-agreement-invalid" +msgstr "" +"Vous devez accepter nos conditions générales d'utilisation et notre " +"politique de confidentialité." + +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.token-expired" +msgstr "Jeton expiré" + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Une erreur inattendue s’est produite" +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.unexpected-token" +msgstr "Jeton inconnu" + #: src/app/main/ui/auth/login.cljs msgid "errors.wrong-credentials" msgstr "Le nom d’utilisateur ou le mot de passe semble être faux." @@ -657,6 +819,9 @@ msgstr "Vous voyez la version %s" msgid "labels.accept" msgstr "Accepter" +msgid "labels.add-custom-font" +msgstr "Ajouter police personnalisée" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.admin" msgstr "Administration" @@ -693,10 +858,21 @@ msgstr "Confirmer le mot de passe" msgid "labels.content" msgstr "Contenu" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "labels.create" +msgstr "Créer" + #: src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/dashboard/team_form.cljs msgid "labels.create-team" msgstr "Créer nouvelle équipe" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team.placeholder" +msgstr "Entrer le nom de la nouvelle équipe" + +msgid "labels.custom-fonts" +msgstr "Polices personnalisées" + #: src/app/main/ui/settings/sidebar.cljs msgid "labels.dashboard" msgstr "Tableau de bord" @@ -713,6 +889,10 @@ msgstr "Supprimer le commentaire" msgid "labels.delete-comment-thread" msgstr "Supprimer le fil" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete-multi-files" +msgstr "Supprimer %s fichiers" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "labels.drafts" msgstr "Brouillons" @@ -729,6 +909,26 @@ msgstr "Éditeur" msgid "labels.email" msgstr "Adresse e‑mail" +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-disabled" +msgstr "Avis désactivés" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.feedback-sent" +msgstr "Avis envoyé" + +msgid "labels.font-family" +msgstr "Famille de polices" + +msgid "labels.font-providers" +msgstr "Fournisseurs de polices" + +msgid "labels.font-variants" +msgstr "Styles" + +msgid "labels.fonts" +msgstr "Polices" + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.give-feedback" msgstr "Donnez votre avis" @@ -746,6 +946,9 @@ msgstr "Icônes" msgid "labels.images" msgstr "Images" +msgid "labels.installed-fonts" +msgstr "Polices installées" + #: src/app/main/ui/static.cljs msgid "labels.internal-error.desc-message" msgstr "" @@ -764,6 +967,9 @@ msgstr "Langue" msgid "labels.logout" msgstr "Se déconnecter" +msgid "labels.manage-fonts" +msgstr "Gérer les polices" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.members" msgstr "Membres" @@ -800,6 +1006,11 @@ msgid_plural "labels.num-of-files" msgstr[0] "1 fichier" msgstr[1] "%s fichiers" +msgid "labels.num-of-frames" +msgid_plural "labels.num-of-frames" +msgstr[0] "1 plan de travail" +msgstr[1] "%s plans de travail" + #: src/app/main/ui/dashboard/team.cljs msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" @@ -814,6 +1025,9 @@ msgstr "Ancien mot de passe" msgid "labels.only-yours" msgstr "Seulement les vôtres" +msgid "labels.or" +msgstr "ou" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.owner" msgstr "Propriétaire" @@ -849,6 +1063,10 @@ msgstr "Retirer" msgid "labels.rename" msgstr "Renommer" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.rename-team" +msgstr "Renommer l'équipe" + #: src/app/main/ui/static.cljs, src/app/main/ui/static.cljs, src/app/main/ui/static.cljs msgid "labels.retry" msgstr "Réessayer" @@ -860,6 +1078,17 @@ msgstr "Rôle" msgid "labels.save" msgstr "Enregistrer" +msgid "labels.search-font" +msgstr "Rechercher une police" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.send" +msgstr "Envoyer" + +#: src/app/main/ui/settings/feedback.cljs +msgid "labels.sending" +msgstr "Envoi…" + #: src/app/main/ui/static.cljs msgid "labels.service-unavailable.desc-message" msgstr "Nous sommes en maintenance planifiée de nos systèmes." @@ -872,6 +1101,10 @@ msgstr "Service non disponible" msgid "labels.settings" msgstr "Configuration" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Partager le prototype" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Bibliothèques Partagées" @@ -892,6 +1125,19 @@ msgstr "Se déconnecter" msgid "labels.update" msgstr "Actualiser" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.update-team" +msgstr "Mettre à jour l'équipe" + +msgid "labels.upload" +msgstr "Télécharger" + +msgid "labels.upload-custom-fonts" +msgstr "Télécharger des polices personnalisées" + +msgid "labels.uploading" +msgstr "Téléchargement…" + #: src/app/main/ui/dashboard/team.cljs msgid "labels.viewer" msgstr "Spectateur" @@ -985,6 +1231,34 @@ msgstr "Êtes‑vous sûr de vouloir supprimer ce fichier ?" msgid "modals.delete-file-confirm.title" msgstr "Supprimer un fichier" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "Supprimer les fichiers" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "Êtes-vous sûr de vouloir supprimer %s fichiers ?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "Suppression de %s fichiers" + +msgid "modals.delete-font-variant.message" +msgstr "" +"Êtes-vous sûr de vouloir supprimer ce style de police ? Il ne sera pas " +"chargé si il est utilisé dans un fichier." + +msgid "modals.delete-font-variant.title" +msgstr "Suppression du style de police" + +msgid "modals.delete-font.message" +msgstr "" +"Êtes-vous sûr de vouloir supprimer cette police ? Elle ne se chargera pas " +"si elle est utilisée dans un fichier." + +msgid "modals.delete-font.title" +msgstr "Suppression de la police" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs msgid "modals.delete-page.body" msgstr "Êtes‑vous sûr de vouloir supprimer cette page ?" @@ -1039,6 +1313,11 @@ msgstr "Envoyer l'invitation" msgid "modals.invite-member.title" msgstr "Inviter à rejoindre l’équipe" +msgid "modals.leave-and-reassign.forbiden" +msgstr "" +"Vous ne pouvez pas quitter l'équipe si il n'y a aucun membre à promouvoir " +"comme propriétaire. Vous pourriez plutôt supprimer l'équipe." + #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-and-reassign.hint1" msgstr "Vous êtes le propriétaire de %s." @@ -1141,6 +1420,18 @@ msgstr "Aller à la page de connexion" msgid "settings.multiple" msgstr "Divers" +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.font-providers" +msgstr "Fournisseurs de polices - %s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.fonts" +msgstr "Polices - %s - Penpot" + #: src/app/main/ui/dashboard/projects.cljs msgid "title.dashboard.projects" msgstr "Projets - %s - Penpot" @@ -1153,6 +1444,10 @@ msgstr "Rechercher - %s - Penpot" msgid "title.dashboard.shared-libraries" msgstr "Bibliothèques Partagées - %s - Penpot" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot - Liberté de conception pour les équipes" + #: src/app/main/ui/settings/feedback.cljs msgid "title.settings.feedback" msgstr "Donnez votre avis - Penpot" @@ -1181,18 +1476,26 @@ msgstr "Configuration - %s - Penpot" msgid "title.viewer" msgstr "%s - Mode spectateur - Penpot" +#: src/app/main/ui/workspace.cljs +msgid "title.workspace" +msgstr "%s - Penpot" + #: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs msgid "viewer.empty-state" -msgstr "Aucun cadre trouvé sur la page." +msgstr "Aucun plan de travail trouvé sur la page." #: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs msgid "viewer.frame-not-found" -msgstr "Cadre introuvable." +msgstr "Plan de travail introuvable." #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.dont-show-interactions" msgstr "Ne pas afficher les interactions" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "Modifier le fichier" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.edit-page" msgstr "Modifier la page" @@ -1225,10 +1528,6 @@ msgstr "Supprimer le lien" msgid "viewer.header.share.subtitle" msgstr "Toute personne disposant du lien aura accès" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Partager le prototype" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Afficher les interactions" @@ -1292,6 +1591,16 @@ msgstr "Couleurs" msgid "workspace.assets.components" msgstr "Composants" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "Créer un groupe" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group-hint" +msgstr "" +"Vos éléments seront automatiquement nommées comme tels : « nom du groupe / " +"nom de l'élément »" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.delete" msgstr "Supprimer" @@ -1312,6 +1621,14 @@ msgstr "Bibliothèque du fichier" msgid "workspace.assets.graphics" msgstr "Graphiques" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group" +msgstr "Grouper" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group-name" +msgstr "Nom du groupe" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.libraries" msgstr "Bibliothèques" @@ -1324,10 +1641,20 @@ msgstr "Aucune ressource trouvée" msgid "workspace.assets.rename" msgstr "Renommer" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename-group" +msgstr "Renommer le groupe" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.search" msgstr "Chercher des ressources" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.selected-count" +msgid_plural "workspace.assets.selected-count" +msgstr[0] "%s élément sélectionné" +msgstr[1] "%s éléments sélectionnés" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.shared" msgstr "PARTAGÉ" @@ -1368,6 +1695,10 @@ msgstr "Ag" msgid "workspace.assets.typography.text-transform" msgstr "Transformer le texte" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.ungroup" +msgstr "Dissocier" + #: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs msgid "workspace.gradients.linear" msgstr "Dégradé linéaire" @@ -1380,6 +1711,10 @@ msgstr "Dégradé radial" msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Désactiver l’alignement dynamique" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-scale-text" +msgstr "Désactiver la mise à l'échelle du texte" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.menu.disable-snap-grid" msgstr "Désactiver l’alignement sur la grille" @@ -1388,6 +1723,10 @@ msgstr "Désactiver l’alignement sur la grille" msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Activer l’alignement dynamique" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-scale-text" +msgstr "Activer le redimensionnement du texte" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.menu.enable-snap-grid" msgstr "Aligner sur la grille" @@ -1586,6 +1925,46 @@ msgstr "Couleur de fond du canvas" msgid "workspace.options.component" msgstr "Composant" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints" +msgstr "Contraintes" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.bottom" +msgstr "Bas" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.center" +msgstr "Centre" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.fix-when-scrolling" +msgstr "Fixe lors du défilement" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.left" +msgstr "Gauche" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.leftright" +msgstr "Gauche & droite" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.right" +msgstr "Droite" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.scale" +msgstr "Redimensionner" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.top" +msgstr "Haut" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.topbottom" +msgstr "Haut & bas" + #: src/app/main/ui/workspace/sidebar/options.cljs msgid "workspace.options.design" msgstr "Conception" @@ -1604,7 +1983,7 @@ msgstr "Suffixe" #: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "workspace.options.exporting-object" -msgstr "Export en cours…" +msgstr "Exportation…" #: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs msgid "workspace.options.fill" @@ -1702,6 +2081,82 @@ msgstr "Remplissage de groupe" msgid "workspace.options.group-stroke" msgstr "Contour de groupe" +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "Couleur" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-burn" +msgstr "Densité plus forte" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-dodge" +msgstr "Densité plus faible" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.darken" +msgstr "Assombrir" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.difference" +msgstr "Différence" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.exclusion" +msgstr "Exclusion" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hard-light" +msgstr "Lumière crue" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hue" +msgstr "Teinte" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.lighten" +msgstr "Éclaircir" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.luminosity" +msgstr "Luminosité" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.multiply" +msgstr "Produit" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.normal" +msgstr "Normal" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.overlay" +msgstr "Incrustation" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.saturation" +msgstr "Saturation" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.screen" +msgstr "Superposition" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.soft-light" +msgstr "Lumière tamisée" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title" +msgstr "Calque" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.group" +msgstr "Grouper les calques" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.multiple" +msgstr "Calques sélectionnés" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.navigate-to" msgstr "Naviguer vers" @@ -1721,6 +2176,14 @@ msgstr "Prototype" msgid "workspace.options.radius" msgstr "Rayon" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.all-corners" +msgstr "Tous les coins" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.single-corners" +msgstr "Coins individuels" + #: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs msgid "workspace.options.rotation" msgstr "Rotation" @@ -1850,6 +2313,18 @@ msgstr "Aligner en haut" msgid "workspace.options.text-options.decoration" msgstr "Décoration" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-ltr" +msgstr "Gauche à droite" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-rtl" +msgstr "Droite à gauche" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.google" +msgstr "Google" + #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs msgid "workspace.options.text-options.grow-auto-height" msgstr "Hauteur automatique" @@ -1878,6 +2353,10 @@ msgstr "Minuscule" msgid "workspace.options.text-options.none" msgstr "Aucune" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.preset" +msgstr "Préréglage" + #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs msgid "workspace.options.text-options.strikethrough" msgstr "Barré" @@ -1918,6 +2397,36 @@ msgstr "" "Utilisez le bouton de lecture dans l’en‑tête pour exécuter la vue du " "prototype." +msgid "workspace.path.actions.add-node" +msgstr "Ajouter un nœud (%s)" + +msgid "workspace.path.actions.delete-node" +msgstr "Supprimer le nœud (%s)" + +msgid "workspace.path.actions.draw-nodes" +msgstr "Dessiner des nœuds (%s)" + +msgid "workspace.path.actions.join-nodes" +msgstr "Joindre les nœuds (%s)" + +msgid "workspace.path.actions.make-corner" +msgstr "Convertir en coin (%s)" + +msgid "workspace.path.actions.make-curve" +msgstr "Convertir en courbe (%s)" + +msgid "workspace.path.actions.merge-nodes" +msgstr "Fusionner les nœuds (%s)" + +msgid "workspace.path.actions.move-nodes" +msgstr "Déplacer les nœuds (%s)" + +msgid "workspace.path.actions.separate-nodes" +msgstr "Dissocier les nœuds (%s)" + +msgid "workspace.path.actions.snap-nodes" +msgstr "Aligner les nœuds (%s)" + #: src/app/main/ui/workspace/context_menu.cljs msgid "workspace.shape.menu.back" msgstr "Envoyer au fond" @@ -1950,6 +2459,18 @@ msgstr "Détacher l’instance" msgid "workspace.shape.menu.duplicate" msgstr "Dupliquer" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.edit" +msgstr "Modifier" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-horizontal" +msgstr "Retourner horizontalement" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-vertical" +msgstr "Retourner verticalement" + #: src/app/main/ui/workspace/context_menu.cljs msgid "workspace.shape.menu.forward" msgstr "Avancer" @@ -2018,6 +2539,10 @@ msgstr "Historique (%s)" msgid "workspace.sidebar.layers" msgstr "Calques (%s)" +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs +msgid "workspace.sidebar.options.svg-attrs.title" +msgstr "Attributs SVG importés" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs msgid "workspace.sidebar.sitemap" msgstr "Pages" diff --git a/frontend/translations/id.po b/frontend/translations/id.po index 5210b494db..800120d39f 100644 --- a/frontend/translations/id.po +++ b/frontend/translations/id.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "PO-Revision-Date: 2021-05-23 21:33+0000\n" "Last-Translator: luthfi azhari \n" -"Language-Team: Indonesian \n" +"Language-Team: Indonesian " +"\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" @@ -11,84 +11,9 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.7-dev\n" -#: src/app/main/ui/dashboard/grid.cljs -msgid "dashboard.empty-files" -msgstr "Anda belum memiliki file disini" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate-multi" -msgstr "Duplikasi % file" - -#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.duplicate" -msgstr "Duplikat" - -msgid "dashboard.draft-title" -msgstr "Konsep" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.delete-team" -msgstr "Hapus tim" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.default-team-name" -msgstr "Penpot anda" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "dashboard.create-new-team" -msgstr "+ Buat tim baru" - -#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs -msgid "dashboard.copy-suffix" -msgstr "(salin)" - -#: src/app/main/ui/settings/profile.cljs -msgid "dashboard.change-email" -msgstr "Ubah email" - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "dashboard.add-shared" -msgstr "Tambahkan sebagai pustaka bersama" - #: src/app/main/ui/auth/register.cljs -msgid "auth.verification-email-sent" -msgstr "Kami mengirim verifikasi ke surel anda" - -#: src/app/main/ui/auth/register.cljs -msgid "auth.register-title" -msgstr "Buat akun baru" - -#: src/app/main/ui/auth/register.cljs -msgid "auth.register-subtitle" -msgstr "Ini gratis, Open Source" - -#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs -msgid "auth.register-submit" -msgstr "Buat akun baru" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.register" -msgstr "Tidak ada akun?" - -#: src/app/main/ui/auth/recovery.cljs -msgid "auth.recovery-submit" -msgstr "Ubah kata sandi anda" - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.recovery-request-title" -msgstr "Lupa kata sandi?" - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.recovery-request-subtitle" -msgstr "Kami akan mengirimi anda surel dengan intruksi" - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.recovery-request-submit" -msgstr "Pemulihan Kata sandi" - -#: src/app/main/ui/auth/register.cljs -msgid "auth.password-length-hint" -msgstr "Paling tidak 8 karakter" +msgid "auth.already-have-account" +msgstr "Sudah memiliki akun?" #: src/app/main/ui/auth/register.cljs msgid "auth.check-your-email" @@ -100,72 +25,13 @@ msgstr "" msgid "auth.confirm-password" msgstr "Konfirmasi kata sandi" -#: src/app/main/ui/auth/register.cljs -msgid "auth.terms-privacy-agreement" -msgstr "" -"Ketika membuat akun baru, anda menyetujui persyaratan layanan dan kebijakan " -"privasi kami." - -#: src/app/main/ui/auth.cljs -msgid "auth.sidebar-tagline" -msgstr "Solusi open-source untuk desain dan pembuatan prototype." +#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs +msgid "auth.create-demo-account" +msgstr "Buat akun demo" #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs -msgid "auth.password" -msgstr "Kata sandi" - -#: src/app/main/ui/auth/verify_token.cljs -msgid "auth.notifications.team-invitation-accepted" -msgstr "Berhasil bergabung dengan tim" - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.notifications.recovery-token-sent" -msgstr "Link pemulihan kata sandi berhasil dikirim ke kotak masuk anda." - -#: src/app/main/ui/auth/recovery_request.cljs -msgid "auth.notifications.profile-not-verified" -msgstr "" -"Akun belum terverifikasi, harap verifikasi profile anda sebelum melanjutkan." - -#: src/app/main/ui/auth/recovery.cljs -msgid "auth.notifications.password-changed-succesfully" -msgstr "Kata sandi berhasil diubah" - -#: src/app/main/ui/auth/recovery.cljs -msgid "auth.notifications.invalid-token-error" -msgstr "Token pemulihan tidak valid." - -#: src/app/main/ui/auth/recovery.cljs -msgid "auth.new-password" -msgstr "Ketikkan kata sandi baru" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-oidc-submit" -msgstr "Masuk dengan OpenID (SSO)" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-ldap-submit" -msgstr "Masuk dengan LDAP" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-google-submit" -msgstr "Masuk dengan Google" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-gitlab-submit" -msgstr "Masuk dengan Gitlab" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-with-github-submit" -msgstr "Masuk dengan Github" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-title" -msgstr "Senang bertemu denganmu lagi!" - -#: src/app/main/ui/auth/login.cljs -msgid "auth.login-subtitle" -msgstr "Masukkan detail anda di bawah ini" +msgid "auth.create-demo-profile" +msgstr "Ingin mencobanya?" #: src/app/main/ui/auth/register.cljs msgid "auth.demo-warning" @@ -173,38 +39,171 @@ msgstr "" "Ini layanan DEMO, JANGAN GUNAKAN untuk pekerjaan nyata, project ini akan di " "hapus secara berkala." +#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs +msgid "auth.email" +msgstr "Surel" + #: src/app/main/ui/auth/login.cljs -msgid "auth.login-submit" -msgstr "Masuk" +msgid "auth.forgot-password" +msgstr "Lupa kata sandi?" #: src/app/main/ui/auth/register.cljs -msgid "auth.login-here" -msgstr "Masuk disini" +msgid "auth.fullname" +msgstr "Nama Lengkap" #: src/app/main/ui/auth/recovery_request.cljs msgid "auth.go-back-to-login" msgstr "Kembali!" #: src/app/main/ui/auth/register.cljs -msgid "auth.fullname" -msgstr "Nama Lengkap" +msgid "auth.login-here" +msgstr "Masuk disini" #: src/app/main/ui/auth/login.cljs -msgid "auth.forgot-password" -msgstr "Lupa kata sandi?" +msgid "auth.login-submit" +msgstr "Masuk" -#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/auth/login.cljs -msgid "auth.email" -msgstr "Surel" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-subtitle" +msgstr "Masukkan detail anda di bawah ini" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-title" +msgstr "Senang bertemu denganmu lagi!" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-github-submit" +msgstr "Masuk dengan Github" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-gitlab-submit" +msgstr "Masuk dengan Gitlab" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-google-submit" +msgstr "Masuk dengan Google" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-ldap-submit" +msgstr "Masuk dengan LDAP" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-oidc-submit" +msgstr "Masuk dengan OpenID (SSO)" + +#: src/app/main/ui/auth/recovery.cljs +msgid "auth.new-password" +msgstr "Ketikkan kata sandi baru" + +#: src/app/main/ui/auth/recovery.cljs +msgid "auth.notifications.invalid-token-error" +msgstr "Token pemulihan tidak valid." + +#: src/app/main/ui/auth/recovery.cljs +msgid "auth.notifications.password-changed-succesfully" +msgstr "Kata sandi berhasil diubah" + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.notifications.profile-not-verified" +msgstr "Akun belum terverifikasi, harap verifikasi profile anda sebelum melanjutkan." + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.notifications.recovery-token-sent" +msgstr "Link pemulihan kata sandi berhasil dikirim ke kotak masuk anda." + +#: src/app/main/ui/auth/verify_token.cljs +msgid "auth.notifications.team-invitation-accepted" +msgstr "Berhasil bergabung dengan tim" #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs -msgid "auth.create-demo-profile" -msgstr "Ingin mencobanya?" - -#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs -msgid "auth.create-demo-account" -msgstr "Buat akun demo" +msgid "auth.password" +msgstr "Kata sandi" #: src/app/main/ui/auth/register.cljs -msgid "auth.already-have-account" -msgstr "Sudah memiliki akun?" +msgid "auth.password-length-hint" +msgstr "Paling tidak 8 karakter" + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.recovery-request-submit" +msgstr "Pemulihan Kata sandi" + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.recovery-request-subtitle" +msgstr "Kami akan mengirimi anda surel dengan intruksi" + +#: src/app/main/ui/auth/recovery_request.cljs +msgid "auth.recovery-request-title" +msgstr "Lupa kata sandi?" + +#: src/app/main/ui/auth/recovery.cljs +msgid "auth.recovery-submit" +msgstr "Ubah kata sandi anda" + +#: src/app/main/ui/auth/login.cljs +msgid "auth.register" +msgstr "Tidak ada akun?" + +#: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs +msgid "auth.register-submit" +msgstr "Buat akun baru" + +#: src/app/main/ui/auth/register.cljs +msgid "auth.register-subtitle" +msgstr "Ini gratis, Open Source" + +#: src/app/main/ui/auth/register.cljs +msgid "auth.register-title" +msgstr "Buat akun baru" + +#: src/app/main/ui/auth.cljs +msgid "auth.sidebar-tagline" +msgstr "Solusi open-source untuk desain dan pembuatan prototype." + +#: src/app/main/ui/auth/register.cljs +msgid "auth.terms-privacy-agreement" +msgstr "" +"Ketika membuat akun baru, anda menyetujui persyaratan layanan dan kebijakan " +"privasi kami." + +#: src/app/main/ui/auth/register.cljs +msgid "auth.verification-email-sent" +msgstr "Kami mengirim verifikasi ke surel anda" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.add-shared" +msgstr "Tambahkan sebagai pustaka bersama" + +#: src/app/main/ui/settings/profile.cljs +msgid "dashboard.change-email" +msgstr "Ubah email" + +#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs +msgid "dashboard.copy-suffix" +msgstr "(salin)" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.create-new-team" +msgstr "+ Buat tim baru" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.default-team-name" +msgstr "Penpot anda" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "dashboard.delete-team" +msgstr "Hapus tim" + +msgid "dashboard.draft-title" +msgstr "Konsep" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate" +msgstr "Duplikat" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "Duplikasi % file" + +#: src/app/main/ui/dashboard/grid.cljs +msgid "dashboard.empty-files" +msgstr "Anda belum memiliki file disini" \ No newline at end of file diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po index ae9e3d988b..8e80464586 100644 --- a/frontend/translations/pt_BR.po +++ b/frontend/translations/pt_BR.po @@ -1,15 +1,15 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-06-15 18:34+0000\n" +"PO-Revision-Date: 2021-08-08 01:35+0000\n" "Last-Translator: Eranot \n" -"Language-Team: Portuguese (Brazil) \n" +"Language-Team: Portuguese (Brazil) " +"\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -204,6 +204,18 @@ msgstr "Duplicar %s arquivos" msgid "dashboard.empty-files" msgstr "Você ainda não tem arquivos aqui" +msgid "dashboard.export-multi" +msgstr "Exportar %s arquivos" + +msgid "dashboard.export-single" +msgstr "Exportar arquivo" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Fonte deletada" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Você ainda não tem nenhuma fonte personalizada instalada." + #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -219,7 +231,10 @@ msgstr "" "Você deve carregar apenas fontes que possui ou tem licença para usar na " "Penpot. Descubra mais na seção de Direitos de conteúdo nos [Termos de " "Serviço da Penpot](https://penpot.app/terms.html). Você pode também querer " -"ler sobre [licenciamento de fontes](2)." +"ler sobre [licenciamento de fontes](https://www.typography.com/faq)." + +msgid "dashboard.import" +msgstr "Importar arquivos" #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" @@ -237,6 +252,9 @@ msgstr "Bibliotecas Compartilhadas" msgid "dashboard.loading-files" msgstr "carregando seus arquivos…" +msgid "dashboard.loading-fonts" +msgstr "carregando suas fontes …" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.move-to" msgstr "Mover para" @@ -293,6 +311,9 @@ msgstr "%s membros" msgid "dashboard.open-in-new-tab" msgstr "Abrir arquivo em uma nova guia" +msgid "dashboard.options" +msgstr "Opções" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "Alterar senha" @@ -487,9 +508,17 @@ msgstr "A senha de confirmação deve ser a mesma" msgid "errors.password-too-short" msgstr "A senha deve ter pelo menos 8 caracteres" +#: src/app/main/ui/auth/register.cljs +msgid "errors.registration-disabled" +msgstr "O registro de contas está desativado no momento." + msgid "errors.terms-privacy-agreement-invalid" msgstr "Você deve aceitar nossos termos de serviço e política de privacidade." +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.token-expired" +msgstr "Token expirado" + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Um erro inesperado ocorreu." @@ -522,10 +551,179 @@ msgstr "Descrição" msgid "feedback.discussions-go-to" msgstr "Ir para discussões" +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-subtitle1" +msgstr "Junte-se ao fórum de comunicação colaborativa da equipe da Penpot." + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-subtitle2" +msgstr "" +"Você pode fazer e responder perguntas, ter conversas abertas e acompanhar " +"as decisões que afetam o projeto." + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-title" +msgstr "Discussões da equipe" + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.subject" +msgstr "Assunto" + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.subtitle" +msgstr "" +"Por favor, descreva o motivo do seu e-mail, especificando se é um problema, " +"uma ideia ou uma dúvida. Um membro de nossa equipe responderá o mais rápido " +"possível." + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.title" +msgstr "E-mail" + +#: src/app/main/ui/settings/password.cljs +msgid "generic.error" +msgstr "Um erro ocorreu" + +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur" +msgstr "Borrar" + +#: src/app/main/ui/handoff/attributes/blur.cljs +msgid "handoff.attributes.blur.value" +msgstr "Valor" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hex" +msgstr "HEX" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.hsla" +msgstr "HSLA" + +#: src/app/main/ui/handoff/attributes/common.cljs +msgid "handoff.attributes.color.rgba" +msgstr "RGBA" + +#: src/app/main/ui/handoff/attributes/fill.cljs +msgid "handoff.attributes.fill" +msgstr "Preencher" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.download" +msgstr "Baixar imagem original" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.height" +msgstr "Altura" + +#: src/app/main/ui/handoff/attributes/image.cljs +msgid "handoff.attributes.image.width" +msgstr "Largura" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout" +msgstr "Layout" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.height" +msgstr "Altura" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.left" +msgstr "Esquerda" + +#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.radius" +msgstr "Raio" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.rotation" +msgstr "Rotação" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.top" +msgstr "Superior" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.width" +msgstr "Largura" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow" +msgstr "Sombra" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.blur" +msgstr "B" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-x" +msgstr "X" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-y" +msgstr "Y" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.spread" +msgstr "S" + +#, permanent +msgid "handoff.attributes.stroke.alignment.center" +msgstr "Centro" + +#, permanent +msgid "handoff.attributes.stroke.alignment.inner" +msgstr "Dentro" + +#, permanent +msgid "handoff.attributes.stroke.alignment.outer" +msgstr "Fora" + +msgid "handoff.attributes.stroke.style.dotted" +msgstr "Pontilhado" + +msgid "handoff.attributes.stroke.style.mixed" +msgstr "Misto" + +msgid "handoff.attributes.stroke.style.none" +msgstr "Nenhum" + +msgid "handoff.attributes.stroke.style.solid" +msgstr "Sólido" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke.width" +msgstr "Largura" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography" +msgstr "Tipografia" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-family" +msgstr "Família da fonte" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-size" +msgstr "Tamanho da fonte" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-style" +msgstr "Estilo da fonte" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.letter-spacing" +msgstr "Espaçamento entre letras" + #: src/app/main/ui/handoff/attributes/text.cljs msgid "handoff.attributes.typography.line-height" msgstr "Altura da linha" +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-decoration" +msgstr "Decoração de texto" + msgid "handoff.attributes.typography.text-decoration.none" msgstr "Nenhum" @@ -535,12 +733,19 @@ msgstr "Riscado" msgid "handoff.attributes.typography.text-decoration.underline" msgstr "Sublinhado" +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-transform" +msgstr "Transformação de texto" + msgid "handoff.attributes.typography.text-transform.lowercase" msgstr "Minúsculo" msgid "handoff.attributes.typography.text-transform.none" msgstr "Nenhum" +msgid "handoff.attributes.typography.text-transform.titlecase" +msgstr "Title Case" + msgid "handoff.attributes.typography.text-transform.uppercase" msgstr "Maiúsculo" @@ -567,6 +772,9 @@ msgstr "Imagem" msgid "handoff.tabs.code.selected.multiple" msgstr "%s selecionados" +msgid "handoff.tabs.code.selected.path" +msgstr "Caminho" + msgid "handoff.tabs.code.selected.rect" msgstr "Retângulo" @@ -586,10 +794,17 @@ msgstr "Você está vendo a versão %s" msgid "labels.accept" msgstr "Aceitar" +msgid "labels.add-custom-font" +msgstr "Adicionar fonte personalizada" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.admin" msgstr "Administrador" +#: src/app/main/ui/workspace/comments.cljs +msgid "labels.all" +msgstr "Todos" + #: src/app/main/ui/static.cljs msgid "labels.bad-gateway.desc-message" msgstr "" @@ -686,6 +901,9 @@ msgstr "Provedores de fonte" msgid "labels.font-variant" msgstr "Estilo" +msgid "labels.font-variants" +msgstr "Estilos" + msgid "labels.fonts" msgstr "Fontes" @@ -696,6 +914,10 @@ msgstr "Enviar feedback" msgid "labels.go-back" msgstr "Voltar" +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.hide-resolved-comments" +msgstr "Ocultar comentários resolvidos" + msgid "labels.icons" msgstr "Ícones" @@ -723,6 +945,9 @@ msgstr "Linguagem" msgid "labels.logout" msgstr "Sair" +msgid "labels.manage-fonts" +msgstr "Gerenciar fontes" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.members" msgstr "Membros" @@ -757,6 +982,11 @@ msgid_plural "labels.num-of-files" msgstr[0] "1 arquivo" msgstr[1] "%s arquivos" +msgid "labels.num-of-frames" +msgid_plural "labels.num-of-frames" +msgstr[0] "1 prancheta" +msgstr[1] "%s pranchetas" + #: src/app/main/ui/dashboard/team.cljs msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" @@ -771,6 +1001,9 @@ msgstr "Senha antiga" msgid "labels.only-yours" msgstr "Apenas seu" +msgid "labels.or" +msgstr "ou" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.owner" msgstr "Proprietário" @@ -829,8 +1062,45 @@ msgid "labels.send" msgstr "Enviar" #: src/app/main/ui/settings/feedback.cljs +#, fuzzy msgid "labels.sending" -msgstr "Enviando..." +msgstr "Enviando…" + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.desc-message" +msgstr "Estamos em manutenção programada de nossos sistemas." + +#: src/app/main/ui/static.cljs +msgid "labels.service-unavailable.main-message" +msgstr "Serviço indisponível" + +#: src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.settings" +msgstr "Configurações" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "labels.shared-libraries" +msgstr "Bibliotecas compartilhadas" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-all-comments" +msgstr "Mostrar todos os comentários" + +#: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.show-your-comments" +msgstr "Mostrar apenas os seus comentários" + +#: src/app/main/ui/static.cljs +msgid "labels.sign-out" +msgstr "Sair" + +#: src/app/main/ui/settings/profile.cljs +msgid "labels.update" +msgstr "Atualizar" + +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.update-team" +msgstr "Atualizar equipe" msgid "labels.upload" msgstr "Carregar" @@ -838,8 +1108,112 @@ msgstr "Carregar" msgid "labels.upload-custom-fonts" msgstr "Carregar fontes personalizadas" +#, fuzzy msgid "labels.uploading" -msgstr "Carregando..." +msgstr "Carregando…" + +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.viewer" +msgstr "Visualizador" + +#: src/app/main/ui/comments.cljs +msgid "labels.write-new-comment" +msgstr "Escrever novo comentário" + +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "media.loading" +msgstr "Carregando imagem…" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.accept" +msgstr "Adicionar como biblioteca compartilhada" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.hint" +msgstr "" +"Depois de adicionados como Biblioteca Compartilhada, os recursos desta " +"biblioteca de arquivos estarão disponíveis para serem usados com o restante " +"de seus arquivos." + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.message" +msgstr "Adicionar “%s” como Biblioteca Compartilhada" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.confirm-email" +msgstr "Verificar o novo e-mail" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.info" +msgstr "" +"Enviaremos a você um e-mail para seu e-mail atual “%s” para verificar sua " +"identidade." + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.new-email" +msgstr "Novo e-mail" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.submit" +msgstr "Alterar e-mail" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.title" +msgstr "Alterar seu e-mail" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.cancel" +msgstr "Cancelar e manter minha conta" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.confirm" +msgstr "Sim, exclua minha conta" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.info" +msgstr "Ao remover sua conta, você perderá todos os seus projetos e arquivos atuais." + +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.title" +msgstr "Tem certeza que deseja excluir sua conta?" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.accept" +msgstr "Excluir conversa" + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.message" +msgstr "" +"Tem certeza de que deseja excluir esta conversa? Todos os comentários neste " +"tópico serão excluídos." + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.title" +msgstr "Excluir conversa" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.accept" +msgstr "Excluir arquivo" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.message" +msgstr "Tem certeza que deseja excluir este arquivo?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.title" +msgstr "Excluindo arquivo" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "Excluir arquivos" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "Tem certeza de que deseja excluir %s arquivos?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "Excluindo %s arquivos" msgid "modals.delete-font.message" msgstr "" @@ -849,6 +1223,14 @@ msgstr "" msgid "modals.delete-font.title" msgstr "Excluindo fonte" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.message" +msgstr "Tem certeza de que deseja excluir este membro da equipe?" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.title" +msgstr "Excluir membro da equipe" + #: src/app/main/ui/dashboard/team.cljs msgid "modals.invite-member-confirm.accept" msgstr "Enviar convite" @@ -862,6 +1244,10 @@ msgstr "" "Você não pode deixar a equipe se não houver outro membro para promover a " "proprietário. Você pode excluir a equipe." +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint1" +msgstr "Você é o proprietário de %s." + #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-and-reassign.hint2" msgstr "Selecione outro membro para promover antes de sair" @@ -906,6 +1292,13 @@ msgstr "Promover a proprietário" msgid "modals.remove-shared-confirm.accept" msgstr "Remover como Biblioteca Compartilhada" +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.hint" +msgstr "" +"Depois de removida como Biblioteca Compartilhada, os Componentes deste " +"arquivo deixarão de estar disponível para serem usados com o resto de seus " +"arquivos." + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "modals.remove-shared-confirm.message" msgstr "Remover “%s” como Biblioteca Compartilhada" @@ -918,6 +1311,46 @@ msgstr "Atualizar componente" msgid "modals.update-remote-component.cancel" msgstr "Cancelar" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.hint" +msgstr "" +"Você está prestes a atualizar um Componente em uma Biblioteca " +"Compartilhada. Isso pode afetar outros arquivos que a utilizam." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.message" +msgstr "Atualizar Componente em uma Biblioteca Compartilhada" + +#: src/app/main/ui/dashboard/team.cljs +msgid "notifications.invitation-email-sent" +msgstr "Convite enviado com sucesso" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "notifications.profile-deletion-not-allowed" +msgstr "" +"Você não pode deletar seu perfil. Designe um novo proprietário para suas " +"equipes antes de continuar." + +#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs +msgid "notifications.profile-saved" +msgstr "Perfil salvo com sucesso!" + +#: src/app/main/ui/settings/change_email.cljs +msgid "notifications.validation-email-sent" +msgstr "E-mail de verificação enviado para %s. Verifique seu e-mail!" + +#: src/app/main/ui/auth/recovery.cljs +msgid "profile.recovery.go-to-login" +msgstr "Ir para a página de login" + +#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "settings.multiple" +msgstr "Misto" + +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + #: src/app/main/ui/dashboard/fonts.cljs msgid "title.dashboard.font-providers" msgstr "Provedores de fonte - %s - Penpot" @@ -926,330 +1359,638 @@ msgstr "Provedores de fonte - %s - Penpot" msgid "title.dashboard.fonts" msgstr "Fontes - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs -msgid "title.team-settings" -msgstr "Configurações - %s - Penpot" - -#: src/app/main/ui/dashboard/team.cljs -msgid "title.team-members" -msgstr "Membros - %s - Penpot" - -#: src/app/main/ui/settings/profile.cljs -msgid "title.settings.profile" -msgstr "Perfil - Penpot" - -#: src/app/main/ui/settings/password.cljs -msgid "title.settings.password" -msgstr "Senha - Penpot" - -#: src/app/main/ui/settings/options.cljs -msgid "title.settings.options" -msgstr "Configurações - Penpot" - -#: src/app/main/ui/settings/feedback.cljs -msgid "title.settings.feedback" -msgstr "Dê sua opinião - Penpot" - -#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs -msgid "title.default" -msgstr "Penpot - Liberdade de design para equipes" - -#: src/app/main/ui/dashboard/libraries.cljs -msgid "title.dashboard.shared-libraries" -msgstr "Bibliotecas Compartilhadas - %s - Penpot" +#: src/app/main/ui/dashboard/projects.cljs +msgid "title.dashboard.projects" +msgstr "Projetos - %s - Penpot" #: src/app/main/ui/dashboard/search.cljs msgid "title.dashboard.search" msgstr "Pesquisar - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs -msgid "title.dashboard.projects" -msgstr "Projetos - %s - Penpot" +#: src/app/main/ui/dashboard/libraries.cljs +msgid "title.dashboard.shared-libraries" +msgstr "Bibliotecas Compartilhadas - %s - Penpot" -#: src/app/main/ui/dashboard/files.cljs -msgid "title.dashboard.files" -msgstr "%s - Penpot" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot - Liberdade de design para equipes" -#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs -msgid "settings.multiple" -msgstr "Misto" +#: src/app/main/ui/settings/feedback.cljs +msgid "title.settings.feedback" +msgstr "Dê sua opinião - Penpot" -#: src/app/main/ui/auth/recovery.cljs -msgid "profile.recovery.go-to-login" -msgstr "Ir para a página de login" +#: src/app/main/ui/settings/options.cljs +msgid "title.settings.options" +msgstr "Configurações - Penpot" -#: src/app/main/ui/settings/change_email.cljs -msgid "notifications.validation-email-sent" -msgstr "E-mail de verificação enviado para %s. Verifique seu e-mail!" +#: src/app/main/ui/settings/password.cljs +msgid "title.settings.password" +msgstr "Senha - Penpot" -#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs -msgid "notifications.profile-saved" -msgstr "Perfil salvo com sucesso!" - -#: src/app/main/ui/settings/delete_account.cljs -msgid "notifications.profile-deletion-not-allowed" -msgstr "" -"Você não pode deletar seu perfil. Designe um novo proprietário para suas " -"equipes antes de continuar." +#: src/app/main/ui/settings/profile.cljs +msgid "title.settings.profile" +msgstr "Perfil - Penpot" #: src/app/main/ui/dashboard/team.cljs -msgid "notifications.invitation-email-sent" -msgstr "Convite enviado com sucesso" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.message" -msgstr "Atualizar Componente em uma Biblioteca Compartilhada" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.hint" -msgstr "" -"Você está prestes a atualizar um Componente em uma Biblioteca Compartilhada. " -"Isso pode afetar outros arquivos que a utilizam." - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.remove-shared-confirm.hint" -msgstr "" -"Depois de removida como Biblioteca Compartilhada, os Componentes deste " -"arquivo deixarão de estar disponível para serem usados com o resto de seus " -"arquivos." - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.hint1" -msgstr "Você é o proprietário de %s." +msgid "title.team-members" +msgstr "Membros - %s - Penpot" #: src/app/main/ui/dashboard/team.cljs -msgid "modals.delete-team-member-confirm.title" -msgstr "Excluir membro da equipe" +msgid "title.team-settings" +msgstr "Configurações - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.delete-team-member-confirm.message" -msgstr "Tem certeza de que deseja excluir este membro da equipe?" +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.frame-not-found" +msgstr "Prancheta não encontrada." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.solid" -msgstr "Sólido" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.dont-show-interactions" +msgstr "Não mostrar interações" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.outer" -msgstr "Fora" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "Editar arquivo" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.mixed" -msgstr "Misturado" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.fullscreen" +msgstr "Tela cheia" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.inner" -msgstr "Dentro" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.interactions" +msgstr "Interações" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.dotted" -msgstr "Pontilhada" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.copy-link" +msgstr "Copiar link" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.dashed" -msgstr "Tracejada" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.create-link" +msgstr "Criar link" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.center" -msgstr "Centro" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.placeholder" +msgstr "O link de compartilhamento aparecerá aqui" -#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs -msgid "workspace.options.size-presets" -msgstr "Predefinições de tamanho" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.remove-link" +msgstr "Remover link" -#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.size" -msgstr "Tamanho" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.subtitle" +msgstr "Qualquer pessoa com o link terá acesso" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.title" -msgstr "Sombra" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.title" +msgstr "Compartilhar protótipo" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.offsety" -msgstr "Y" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions" +msgstr "Mostrar interações" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.offsetx" -msgstr "X" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions-on-click" +msgstr "Mostrar interações ao clicar" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.inner-shadow" -msgstr "Sombra interior" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.sitemap" +msgstr "Mapa do site" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.drop-shadow" -msgstr "Sombra projetada" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hcenter" +msgstr "Alinhar no centro horizontalmente" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.blur" -msgstr "Borrar" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hdistribute" +msgstr "Distribuir espaçamento horizontal" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.rotation" -msgstr "Rotação" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hleft" +msgstr "Alinhar à esquerda" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.radius.single-corners" -msgstr "Cantos individuais" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hright" +msgstr "Alinhar à direita" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.radius.all-corners" -msgstr "Todos cantos" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vbottom" +msgstr "Alinhar à base" -msgid "workspace.options.radius" -msgstr "Raio" +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vcenter" +msgstr "Alinhar no centro verticalmente" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vdistribute" +msgstr "Distribuir espaçamento vertical" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vtop" +msgstr "Alinhar ao topo" + +msgid "workspace.assets.box-filter-graphics" +msgstr "Gráficos" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.colors" +msgstr "Cores" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.components" +msgstr "Componentes" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "Criar grupo" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group-hint" +msgstr "" +"Seus itens serão nomeados automaticamente como \"nome do grupo / nome do " +"item\"" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.delete" +msgstr "Deletar" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.duplicate" +msgstr "Duplicar" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.edit" +msgstr "Editar" + +msgid "workspace.library.all" +msgstr "Todas bibliotecas" + +msgid "workspace.library.libraries" +msgstr "Bibliotecas" + +msgid "workspace.library.own" +msgstr "Minhas bibliotecas" + +msgid "workspace.library.store" +msgstr "Bibliotecas da loja" + +msgid "workspace.options.blur-options.background-blur" +msgstr "Fundo" + +msgid "workspace.options.blur-options.layer-blur" +msgstr "Camada" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title" +msgstr "" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs +msgid "workspace.options.component" +msgstr "Componente" #: src/app/main/ui/workspace/sidebar/options.cljs -msgid "workspace.options.prototype" -msgstr "Protótipo" - -#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.position" -msgstr "Posição" - -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.none" -msgstr "Nenhum" - -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.navigate-to" -msgstr "Navegar para" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.title.multiple" -msgstr "Camadas selecionadas" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.color" -msgstr "Cor" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.title" -msgstr "Grades & Layouts" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.square" -msgstr "Quadrado" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.row" -msgstr "Linhas" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.width" -msgstr "Largura" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.use-default" -msgstr "Usar padrão" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.top" -msgstr "Superior" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.stretch" -msgstr "Esticar" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.right" -msgstr "Direita" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.left" -msgstr "Esquerda" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.center" -msgstr "Centro" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.bottom" -msgstr "Inferior" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type" -msgstr "Tipo" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.size" -msgstr "Tamanho" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.set-default" -msgstr "Definir como padrão" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.rows" -msgstr "Linhas" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.margin" -msgstr "Margem" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.height" -msgstr "Altura" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.columns" -msgstr "Colunas" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.column" -msgstr "Colunas" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.auto" -msgstr "Automático" - -#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs -msgid "workspace.options.fill" -msgstr "Preencher" +msgid "workspace.options.design" +msgstr "Design" #: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.exporting-object" -msgstr "Exportando…" +msgid "workspace.options.export" +msgstr "Exportar" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export-object" +msgstr "Exportar forma" #: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs msgid "workspace.options.export.suffix" msgstr "Sufixo" #: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.export-object" -msgstr "Exportar forma" +msgid "workspace.options.exporting-object" +msgstr "Exportando…" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.export" -msgstr "Exportar" +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.fill" +msgstr "Preencher" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.auto" +msgstr "Automático" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.column" +msgstr "Colunas" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.columns" +msgstr "Colunas" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.height" +msgstr "Altura" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.margin" +msgstr "Margem" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.rows" +msgstr "Linhas" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.set-default" +msgstr "Definir como padrão" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.size" +msgstr "Tamanho" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type" +msgstr "Tipo" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.bottom" +msgstr "Inferior" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.center" +msgstr "Centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.left" +msgstr "Esquerda" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.right" +msgstr "Direita" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.stretch" +msgstr "Esticar" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.top" +msgstr "Superior" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.use-default" +msgstr "Usar padrão" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.width" +msgstr "Largura" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.row" +msgstr "Linhas" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.square" +msgstr "Quadrado" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.title" +msgstr "Grades & Layouts" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "Cor" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.group" +msgstr "Camadas do grupo" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.multiple" +msgstr "Camadas selecionadas" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.navigate-to" +msgstr "Navegar para" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.none" +msgstr "Nenhum" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.position" +msgstr "Posição" #: src/app/main/ui/workspace/sidebar/options.cljs -msgid "workspace.options.design" -msgstr "Design" +msgid "workspace.options.prototype" +msgstr "Protótipo" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs -msgid "workspace.options.component" -msgstr "Componente" +msgid "workspace.options.radius" +msgstr "Raio" -#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs -msgid "workspace.options.blur-options.title" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.all-corners" +msgstr "Todos cantos" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.single-corners" +msgstr "Cantos individuais" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.rotation" +msgstr "Rotação" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-a-shape" msgstr "" +"Selecione uma forma, prancheta ou grupo para arrastar uma conexão para " +"outra prancheta." -msgid "workspace.options.blur-options.layer-blur" -msgstr "Camada" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-artboard" +msgstr "Selecionar prancheta" -msgid "workspace.options.blur-options.background-blur" -msgstr "Fundo" +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.selection-fill" +msgstr "Preenchimento da seleção" -msgid "workspace.library.store" -msgstr "Bibliotecas da loja" +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.blur" +msgstr "Borrar" -msgid "workspace.library.own" -msgstr "Minhas bibliotecas" +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.drop-shadow" +msgstr "Sombra projetada" -msgid "workspace.library.libraries" -msgstr "Bibliotecas" +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.inner-shadow" +msgstr "Sombra interior" -msgid "workspace.library.all" -msgstr "Todas bibliotecas" +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsetx" +msgstr "X" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsety" +msgstr "Y" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title" +msgstr "Sombra" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.group" +msgstr "Sombra do grupo" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.multiple" +msgstr "Sombras da seleção" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.size" +msgstr "Tamanho" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +msgid "workspace.options.size-presets" +msgstr "Predefinições de tamanho" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.center" +msgstr "Centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dashed" +msgstr "Tracejada" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dotted" +msgstr "Pontilhada" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.inner" +msgstr "Dentro" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.mixed" +msgstr "Misturado" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.outer" +msgstr "Fora" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.solid" +msgstr "Sólido" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-center" +msgstr "Alinhar ao centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-justify" +msgstr "Justificar" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-left" +msgstr "Alinhar à esquerda" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-middle" +msgstr "Alinhar no meio" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-right" +msgstr "Alinhar à direita" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-top" +msgstr "Alinhar ao topo" + +msgid "workspace.options.text-options.decoration" +msgstr "Decoração" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.google" +msgstr "Google" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-height" +msgstr "Altura automática" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-width" +msgstr "Largura automática" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-fixed" +msgstr "Fixado" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.letter-spacing" +msgstr "Espaçamento entre letras" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show" +msgstr "Mostrar" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show-main" +msgstr "Mostrar componente principal" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.ungroup" +msgstr "Desagrupar" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.unlock" +msgstr "Desbloquear" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.unmask" +msgstr "Remover máscara" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.update-main" +msgstr "Atualizar o componente principal" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.history" +msgstr "Histórico (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.layers" +msgstr "Camadas (%s)" + +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs +msgid "workspace.sidebar.options.svg-attrs.title" +msgstr "Atributos SVG importados" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "workspace.sidebar.sitemap" +msgstr "Páginas" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.sitemap" +msgstr "Mapa do site" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.color-palette" +msgstr "Paleta de cores (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.comments" +msgstr "Comentários (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.curve" +msgstr "Curva (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.ellipse" +msgstr "Elipse (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.frame" +msgstr "Prancheta (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.image" +msgstr "Imagem (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.move" +msgstr "Mover" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.rect" +msgstr "Retângulo (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.text" +msgstr "Texto (%s)" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.empty" +msgstr "Não há mudanças no histórico até agora" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.move" +msgstr "Objetos movidos" + +msgid "workspace.undo.entry.multiple.circle" +msgstr "círculos" + +msgid "workspace.undo.entry.multiple.component" +msgstr "componentes" + +msgid "workspace.undo.entry.multiple.curve" +msgstr "curvas" + +msgid "workspace.undo.entry.multiple.frame" +msgstr "prancheta" + +msgid "workspace.undo.entry.multiple.group" +msgstr "grupos" + +msgid "workspace.undo.entry.multiple.multiple" +msgstr "objetos" + +msgid "workspace.undo.entry.multiple.page" +msgstr "páginas" + +msgid "workspace.undo.entry.multiple.rect" +msgstr "retângulos" + +msgid "workspace.undo.entry.multiple.shape" +msgstr "formas" + +msgid "workspace.undo.entry.multiple.text" +msgstr "textos" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.new" +msgstr "Novo %s" + +msgid "workspace.undo.entry.single.circle" +msgstr "círculo" + +msgid "workspace.undo.entry.single.component" +msgstr "componente" + +msgid "workspace.undo.entry.single.curve" +msgstr "curva" + +msgid "workspace.undo.entry.single.frame" +msgstr "prancheta" + +msgid "workspace.undo.entry.single.group" +msgstr "grupo" + +msgid "workspace.undo.entry.single.image" +msgstr "imagem" + +msgid "workspace.undo.entry.single.multiple" +msgstr "objeto" + +msgid "workspace.undo.entry.single.page" +msgstr "página" + +msgid "workspace.undo.entry.single.path" +msgstr "caminho" + +msgid "workspace.undo.entry.single.rect" +msgstr "retângulo" + +msgid "workspace.undo.entry.single.shape" +msgstr "forma" + +msgid "workspace.undo.entry.single.text" +msgstr "texto" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.title" +msgstr "Histórico" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.there-are-updates" +msgstr "Existem atualizações nas bibliotecas compartilhadas" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.update" +msgstr "Atualizar" + +msgid "workspace.viewport.click-to-close-path" +msgstr "Clique para fechar o caminho" \ No newline at end of file diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po index f12369e267..2dd4d6e5dd 100644 --- a/frontend/translations/ro.po +++ b/frontend/translations/ro.po @@ -1060,6 +1060,10 @@ msgstr "Serviciul nu este disponibil" msgid "labels.settings" msgstr "Setări" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Distribuie link" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Colecții distribuite" @@ -1464,10 +1468,6 @@ msgstr "Elimină link" msgid "viewer.header.share.subtitle" msgstr "Prin acest link se permite accesul public" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Distribuie link" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Afişează interacţiunile" diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po index 4d681bf2b1..6f0d9cc986 100644 --- a/frontend/translations/ru.po +++ b/frontend/translations/ru.po @@ -414,6 +414,10 @@ msgstr "Сохранить" msgid "labels.settings" msgstr "Параметры" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Поделиться ссылкой" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "" @@ -598,10 +602,6 @@ msgstr "Удалить ссылку" msgid "viewer.header.share.subtitle" msgstr "Любой, у кого есть ссылка будет иметь доступ" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Поделиться ссылкой" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "Показывать взаимодействия" diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index 55e5587d08..afaf332889 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -1,15 +1,15 @@ msgid "" msgstr "" -"PO-Revision-Date: 2021-06-01 00:38+0000\n" +"PO-Revision-Date: 2021-07-05 17:02+0000\n" "Last-Translator: Çağlar Yeşilyurt \n" -"Language-Team: Turkish \n" +"Language-Team: Turkish " +"\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -204,14 +204,30 @@ msgstr "%s dosyanın kopyasını oluştur" msgid "dashboard.empty-files" msgstr "Burada hiç dosyan yok" +msgid "dashboard.export-multi" +msgstr "%s dosyalarını dışarı aktar" + +msgid "dashboard.export-single" +msgstr "Dosyayı dışarı aktar" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Yazı tipi silindi" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Kurulu özel yazı tipiniz bulunmamaktadır." + #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" "Sadece kendinize ait veya Penpot'ta kullanılabilecek bir lisansa sahip olan " "yazi tiplerini yükleyebilirsiniz. [Penpot'un Kullanım Şartları] içindeki " -"İçerik hakları bölümünden detaylı bilgi alabilirsiniz (https://penpot.app/" -"terms.html). Ayrıca [yazı tipi lisanslama](https://www.typography.com/faq) " -"hakkında daha fazla bilgi almak isteyebilirsiniz." +"İçerik hakları bölümünden detaylı bilgi alabilirsiniz " +"(https://penpot.app/terms.html). Ayrıca [yazı tipi " +"lisanslama](https://www.typography.com/faq) hakkında daha fazla bilgi almak " +"isteyebilirsiniz." + +msgid "dashboard.import" +msgstr "Dosyaları içeri aktar" #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" @@ -229,6 +245,9 @@ msgstr "Paylaşılan Kitaplıklar" msgid "dashboard.loading-files" msgstr "dosyalarınız yükleniyor …" +msgid "dashboard.loading-fonts" +msgstr "yazı tipleriniz yükleniyor…" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.move-to" msgstr "Şuraya taşı" @@ -285,6 +304,9 @@ msgstr "%s üye" msgid "dashboard.open-in-new-tab" msgstr "Dosyayı yeni sekmede aç" +msgid "dashboard.options" +msgstr "Ayarlar" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "Parola değiştir" @@ -538,6 +560,16 @@ msgstr "Açıklama" msgid "feedback.discussions-go-to" msgstr "Tartışmalar bölümüne git" +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-subtitle1" +msgstr "Penpot takımı ortak iletişim forumuna katıl." + +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.discussions-subtitle2" +msgstr "" +"Soru sorabilir ve soruları cevaplayabilir, açık uçlu tartışmalar yapabilir " +"ve projeyi etkileyen kararları takip edebilirsin." + #: src/app/main/ui/settings/feedback.cljs msgid "feedback.discussions-title" msgstr "Takım tartışmaları" @@ -546,6 +578,12 @@ msgstr "Takım tartışmaları" msgid "feedback.subject" msgstr "Konu" +#: src/app/main/ui/settings/feedback.cljs +msgid "feedback.subtitle" +msgstr "" +"Lütfen bir sorun, fikir ya da kuşkunuzu açıklayarak e-postanızın nedenini " +"belirtin. Ekibimizin bir üyesi en kısa sürede yanıt verecektir." + #: src/app/main/ui/settings/feedback.cljs msgid "feedback.title" msgstr "E-posta" @@ -590,24 +628,148 @@ msgstr "Yükseklik" msgid "handoff.attributes.image.width" msgstr "Genişlik" +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.height" +msgstr "Yükseklik" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.left" +msgstr "Sol" + +#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.radius" +msgstr "Yarı Çap" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.rotation" +msgstr "Döndür" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.top" +msgstr "Üst" + +#: src/app/main/ui/handoff/attributes/layout.cljs +msgid "handoff.attributes.layout.width" +msgstr "Genişlik" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow" +msgstr "Gölge" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.blur" +msgstr "B" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-x" +msgstr "X" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.offset-y" +msgstr "Y" + +#: src/app/main/ui/handoff/attributes/shadow.cljs +msgid "handoff.attributes.shadow.shorthand.spread" +msgstr "Y" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke" +msgstr "Çerçeve" + +#, permanent +msgid "handoff.attributes.stroke.alignment.center" +msgstr "Merkezi" + +#, permanent +msgid "handoff.attributes.stroke.alignment.inner" +msgstr "İçinde" + +#, permanent +msgid "handoff.attributes.stroke.alignment.outer" +msgstr "Dışarıda" + +msgid "handoff.attributes.stroke.style.dotted" +msgstr "Noktalı" + +msgid "handoff.attributes.stroke.style.mixed" +msgstr "Karışık" + +msgid "handoff.attributes.stroke.style.none" +msgstr "Hiçbiri" + +msgid "handoff.attributes.stroke.style.solid" +msgstr "Düz" + +#: src/app/main/ui/handoff/attributes/stroke.cljs +msgid "handoff.attributes.stroke.width" +msgstr "Genişlik" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography" +msgstr "Tipografi" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-family" +msgstr "Font Ailesi" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-size" +msgstr "Font Boyutu" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.font-style" +msgstr "Font Stili" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.letter-spacing" +msgstr "Harf Aralığı" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.line-height" +msgstr "Satır Yüksekliği" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-decoration" +msgstr "Metin Süsleme" + msgid "handoff.attributes.typography.text-decoration.none" msgstr "Hiçbiri" +msgid "handoff.attributes.typography.text-decoration.strikethrough" +msgstr "Üstü Çizili" + +msgid "handoff.attributes.typography.text-decoration.underline" +msgstr "Altı Çizili" + +#: src/app/main/ui/handoff/attributes/text.cljs +msgid "handoff.attributes.typography.text-transform" +msgstr "Metin Dönüşümü" + msgid "handoff.attributes.typography.text-transform.lowercase" msgstr "Küçük Harf" msgid "handoff.attributes.typography.text-transform.none" msgstr "Hiçbiri" +msgid "handoff.attributes.typography.text-transform.titlecase" +msgstr "İlk Harfleri Büyük" + msgid "handoff.attributes.typography.text-transform.uppercase" msgstr "Büyük Harf" +#: src/app/main/ui/handoff/right_sidebar.cljs +msgid "handoff.tabs.code" +msgstr "Kod" + msgid "handoff.tabs.code.selected.circle" msgstr "Daire" msgid "handoff.tabs.code.selected.curve" msgstr "Eğri" +msgid "handoff.tabs.code.selected.frame" +msgstr "Çalışma yüzeyi" + msgid "handoff.tabs.code.selected.group" msgstr "Grup" @@ -640,6 +802,9 @@ msgstr "%s sürümünü görüyorsun" msgid "labels.accept" msgstr "Kabul et" +msgid "labels.add-custom-font" +msgstr "Özel yazı tipi ekle" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.admin" msgstr "Yönetici" @@ -648,6 +813,16 @@ msgstr "Yönetici" msgid "labels.all" msgstr "Hepsi" +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.desc-message" +msgstr "" +"Görünüşe göre biraz beklemen ve yeniden denemen gerekiyor; sunucularımızda " +"küçük bir bakım yapıyoruz." + +#: src/app/main/ui/static.cljs +msgid "labels.bad-gateway.main-message" +msgstr "Hatalı Ağ Geçidi" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.cancel" msgstr "İptal" @@ -678,6 +853,9 @@ msgstr "Yeni takım oluştur" msgid "labels.create-team.placeholder" msgstr "Yeni takım adı gir" +msgid "labels.custom-fonts" +msgstr "Özel Fontlar" + #: src/app/main/ui/settings/sidebar.cljs msgid "labels.dashboard" msgstr "Kontrol paneli" @@ -725,9 +903,15 @@ msgstr "Geri bildirim gönderildi" msgid "labels.font-family" msgstr "Font Ailesi" +msgid "labels.font-providers" +msgstr "Font sağlayıcısı" + msgid "labels.font-variant" msgstr "Stil" +msgid "labels.font-variants" +msgstr "Biçimler" + msgid "labels.fonts" msgstr "Fontlar" @@ -742,6 +926,9 @@ msgstr "Geri dön" msgid "labels.hide-resolved-comments" msgstr "Çözülmüş yorumları gizle" +msgid "labels.icons" +msgstr "Simgeler" + msgid "labels.images" msgstr "Görseller" @@ -754,6 +941,10 @@ msgstr "" "Kötü bir şey oldu. Lütfen işlemi yeniden deneyin ve sorun devam ederse " "destek ile iletişime geçin." +#: src/app/main/ui/static.cljs +msgid "labels.internal-error.main-message" +msgstr "İç Hata" + #: src/app/main/ui/settings/options.cljs msgid "labels.language" msgstr "Dil" @@ -762,6 +953,9 @@ msgstr "Dil" msgid "labels.logout" msgstr "Çıkış Yap" +msgid "labels.manage-fonts" +msgstr "Fontları yönet" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.members" msgstr "Üyeler" @@ -883,6 +1077,10 @@ msgstr "Hizmet Kullanılamıyor" msgid "labels.settings" msgstr "Ayarlar" +#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs +msgid "labels.share-prototype" +msgstr "Prototipi paylaş" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "Paylaşılan Kitaplıklar" @@ -903,1297 +1101,74 @@ msgstr "Çıkış yap" msgid "labels.update" msgstr "Güncelle" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.update-team" +msgstr "Takımı güncelle" + msgid "labels.upload" msgstr "Yükle" +msgid "labels.upload-custom-fonts" +msgstr "Özel yazı tipi yükle" + msgid "labels.uploading" msgstr "Yükleniyor…" -msgid "modals.delete-font.message" -msgstr "" -"Bu fontu silmek istediğine emin misin? Bir dosyada kullanılıyorsa " -"yüklenmeyecektir." +#: src/app/main/ui/dashboard/team.cljs +msgid "labels.viewer" +msgstr "Görüntüler" -msgid "modals.delete-font.title" -msgstr "Fontu sil" +#: src/app/main/ui/comments.cljs +msgid "labels.write-new-comment" +msgstr "Yeni yorum yaz" -#: src/app/main/ui/dashboard/fonts.cljs -msgid "title.dashboard.fonts" -msgstr "Fontlar - %s - Penpot" - -msgid "labels.manage-fonts" -msgstr "Fontları yönet" - -#: src/app/main/ui/static.cljs -msgid "labels.internal-error.main-message" -msgstr "İç Hata" - -msgid "labels.font-providers" -msgstr "Font sağlayıcısı" - -msgid "labels.custom-fonts" -msgstr "Özel Fontlar" - -#: src/app/main/ui/static.cljs -msgid "labels.bad-gateway.main-message" -msgstr "Hatalı Ağ Geçidi" - -#: src/app/main/ui/static.cljs -msgid "labels.bad-gateway.desc-message" -msgstr "" -"Görünüşe göre biraz beklemen ve yeniden denemen gerekiyor; sunucularımızda " -"küçük bir bakım yapıyoruz." - -#: src/app/main/ui/handoff/right_sidebar.cljs -msgid "handoff.tabs.code" -msgstr "Kod" - -msgid "handoff.attributes.typography.text-decoration.underline" -msgstr "Altı Çizili" - -msgid "handoff.attributes.typography.text-decoration.strikethrough" -msgstr "Üstü Çizili" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.line-height" -msgstr "Satır Yüksekliği" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.letter-spacing" -msgstr "Harf Aralığı" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-style" -msgstr "Font Stili" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-size" -msgstr "Font Boyutu" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.font-family" -msgstr "Font Ailesi" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography" -msgstr "Tipografi" - -#: src/app/main/ui/handoff/attributes/stroke.cljs -msgid "handoff.attributes.stroke.width" -msgstr "Genişlik" - -msgid "handoff.attributes.stroke.style.solid" -msgstr "Düz" - -msgid "handoff.attributes.stroke.style.none" -msgstr "Hiçbiri" - -msgid "handoff.attributes.stroke.style.mixed" -msgstr "Karışık" - -msgid "handoff.attributes.stroke.style.dotted" -msgstr "Noktalı" - -#, permanent -msgid "handoff.attributes.stroke.alignment.center" -msgstr "Merkezi" - -#, permanent -msgid "handoff.attributes.stroke.alignment.outer" -msgstr "Dışarıda" - -#, permanent -msgid "handoff.attributes.stroke.alignment.inner" -msgstr "İçinde" - -#: src/app/main/ui/handoff/attributes/stroke.cljs -msgid "handoff.attributes.stroke" -msgstr "Çerçeve" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.offset-y" -msgstr "Y" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.offset-x" -msgstr "X" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow" -msgstr "Gölge" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.width" -msgstr "Genişlik" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.top" -msgstr "Üst" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.rotation" -msgstr "Döndür" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.left" -msgstr "Sol" - -#: src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.height" -msgstr "Yükseklik" - -#: src/app/main/ui/settings/feedback.cljs -msgid "feedback.subtitle" -msgstr "" -"Lütfen bir sorun, fikir ya da kuşkunuzu açıklayarak e-postanızın nedenini " -"belirtin. Ekibimizin bir üyesi en kısa sürede yanıt verecektir." - -#: src/app/main/ui/settings/feedback.cljs -msgid "feedback.discussions-subtitle2" -msgstr "" -"Soru sorabilir ve soruları cevaplayabilir, açık uçlu tartışmalar yapabilir " -"ve projeyi etkileyen kararları takip edebilirsin." - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.typography" -msgstr "%s tipografi" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.libraries.text.multiple-typography-tooltip" -msgstr "Tüm tipografileri ayır" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.libraries.text.multiple-typography" -msgstr "Çoklu tipografiler" - -#: src/app/main/ui/workspace/colorpalette.cljs -msgid "workspace.libraries.colors.small-thumbnails" -msgstr "Küçük önizlemeler" - -#: src/app/main/ui/workspace/colorpalette.cljs -msgid "workspace.libraries.colors.big-thumbnails" -msgstr "Büyük önizlemeler" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.font-variant-id" -msgstr "Çeşit" - -#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.typography" -msgstr "Tipografiler" - -#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs -msgid "title.viewer" -msgstr "%s - Görünüm modu - Penpot" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.hint" -msgstr "" -"Paylaşılmış bir kütüphanedeki bileşeni güncellemek üzeresin. Onu kullanan " -"diğer dosyalar etkilenebilir." - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.accept" -msgstr "Bileşeni güncelle" - -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.navigate-to" -msgstr "Git" - -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.select-a-shape" -msgstr "" -"Diğer çalışma yüzeyine bağlantı taşımak için bir şekil, çalışma yüzeyi ya da " -"grup seçin." - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.direction-rtl" -msgstr "Sağdan sola" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.direction-ltr" -msgstr "Soldan sağa" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.titlecase" -msgstr "İlk Harfi Büyük" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.flip-vertical" -msgstr "Dikey ters çevir" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.flip-horizontal" -msgstr "Yatay ters çevir" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.go-to-edit" -msgstr "Düzenlemek için biçim kütüphane dosyasına gidin" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.hcenter" -msgstr "Yatay olarak ortaya hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.stretch" -msgstr "Ger" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.right" -msgstr "Sağ" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.left" -msgstr "Sol" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.center" -msgstr "Orta" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.bottom" -msgstr "Alt" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type" -msgstr "Tür" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.size" -msgstr "Boyut" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.set-default" -msgstr "Varsayılan olarak belirle" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.rows" -msgstr "Satırlar" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.margin" -msgstr "Kenar Boşluğu" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.height" -msgstr "Yükseklik" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.columns" -msgstr "Sütunlar" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.column" -msgstr "Sütunlar" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.auto" -msgstr "Otomatik" - -#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs -msgid "workspace.options.fill" -msgstr "Doldur" - -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.exporting-object" -msgstr "Dışarı aktarılıyor…" - -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs -msgid "workspace.options.export.suffix" -msgstr "Son ek" - -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.export-object" -msgstr "Şekli dışarı aktar" - -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs -msgid "workspace.options.export" -msgstr "Dışarı Aktar" - -#: src/app/main/ui/workspace/sidebar/options.cljs -msgid "workspace.options.design" -msgstr "Tasarım" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs -msgid "workspace.options.component" -msgstr "Bileşen" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.disable-snap-grid" -msgstr "Izgaraya tutturmayı kapat" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.disable-scale-text" -msgstr "Metin ölçeklendirmeyi kapat" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.disable-dynamic-alignment" -msgstr "Dinamik hizalamayı kapat" - -#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs -msgid "workspace.gradients.radial" -msgstr "Dairesel degrade" - -#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs -msgid "workspace.gradients.linear" -msgstr "Doğrusal degrade" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.create-group" -msgstr "Grup oluştur" - -#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.components" -msgstr "Bileşenler" - -#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.colors" -msgstr "Renkler" - -msgid "workspace.assets.box-filter-graphics" -msgstr "Grafikler" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.box-filter-all" -msgstr "Tüm varlıklar" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.assets" -msgstr "Varlıklar" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.vtop" -msgstr "Üste hizala" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.vdistribute" -msgstr "Dikeyde dağıt" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.hdistribute" -msgstr "Yatayda dağıt" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.edit-page" -msgstr "Sayfayı düzenle" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.dont-show-interactions" -msgstr "Etkileşimleri gösterme" - -#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs -msgid "viewer.frame-not-found" -msgstr "Çerçeve bulunmadı." - -#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs -msgid "viewer.empty-state" -msgstr "Sayfada çerçeve bulunmuyor." - -#: src/app/main/ui/workspace.cljs -msgid "title.workspace" -msgstr "%s - Penpot" - -#: src/app/main/ui/dashboard/fonts.cljs -msgid "title.dashboard.font-providers" -msgstr "Yazıtipi Sağlayıcıları - %s - Penpot" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.cancel" -msgstr "İptal" +#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs +msgid "media.loading" +msgstr "Resim yükleniyor…" #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.remove-shared-confirm.accept" -msgstr "Paylaşılmış Kütüphane olarak kaldır" +msgid "modals.add-shared-confirm.accept" +msgstr "Paylaşılmış Kütüphane olarak Ekle" -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.promote-owner-confirm.title" -msgstr "Sahip olarak terfi et" - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.promote-owner-confirm.message" -msgstr "Bu kullanıcıyı sahip olarak terfi etmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-confirm.title" -msgstr "Takımdan ayrılmak" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.hint2" -msgstr "Ayrılmadan önce terfi etmek için başka bir üye seçin" - -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.mixed" -msgstr "Karışık" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.screen" -msgstr "Ekran" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.saturation" -msgstr "Doygunluk" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.overlay" -msgstr "Üst katman" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.normal" -msgstr "Normal" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.color-burn" -msgstr "Renk yanması" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.curve" -msgstr "Eğri (%s)" - -msgid "workspace.path.actions.snap-nodes" -msgstr "Düğümleri tuttur (%s)" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.strikethrough" -msgstr "Üstü çizili" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.line-height" -msgstr "Satır yüksekliği" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.letter-spacing" -msgstr "Harf Aralıkları" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.grow-fixed" -msgstr "Sabit" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.grow-auto-width" -msgstr "Otomatik genişlik" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.grow-auto-height" -msgstr "Otomatik yükseklik" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.google" -msgstr "Google" - -msgid "workspace.options.text-options.decoration" -msgstr "Süsleme" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-top" -msgstr "Üste hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-right" -msgstr "Sağa hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-middle" -msgstr "Merkeze hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-left" -msgstr "Sola hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-justify" -msgstr "İki yana yasla" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-center" -msgstr "Ortaya hizala" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.align-bottom" -msgstr "Alta hizala" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.frame" -msgstr "Çalışma Yüzeyi (%s)" - -msgid "workspace.undo.entry.single.frame" -msgstr "çalışma yüzeyi" - -msgid "workspace.undo.entry.multiple.frame" -msgstr "çalışma yüzeyi" - -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.select-artboard" -msgstr "Çalışma yüzeyi seç" - -msgid "modals.leave-and-reassign.forbiden" +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.hint" msgstr "" -"Birisini takımın sahibi yapmadan takımı bırakamazsın. Takımı silmek " -"isteyebilirsin." +"Paylaşılmış Kütüphane olarak eklenince, bu dosya kütüphanesindeki varlıklar " +"diğer dosyalarınızdan da ulaşılabilecek." -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.delete-team-member-confirm.title" -msgstr "Takım üyesini sil" - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.delete-team-member-confirm.message" -msgstr "Bu üyeyi takımdan silmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.delete-team-member-confirm.accept" -msgstr "Üyeyi sil" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.delete-team-confirm.title" -msgstr "Takımın silinmesi" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.delete-team-confirm.message" -msgstr "" -"Bu takımı silmek istediğinden emin misin? Takımla ilişkili dosyalar ve " -"projeler kalıcı olarak silinecektir." - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.delete-team-confirm.accept" -msgstr "Takımı sil" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "modals.delete-project-confirm.title" -msgstr "Projeyi sil" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "modals.delete-project-confirm.message" -msgstr "Bu projeyi silmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/project_menu.cljs -msgid "modals.delete-project-confirm.accept" -msgstr "Projeyi sil" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs -msgid "modals.delete-page.title" -msgstr "Sayfayı sil" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs -msgid "modals.delete-page.body" -msgstr "Bu sayfayı silmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-multi-confirm.title" -msgstr "%s dosyanın silinmesi" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-multi-confirm.message" -msgstr "%s dosyayı silmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-multi-confirm.accept" -msgstr "Dosyalar sil" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-confirm.title" -msgstr "Dosya siliniyor" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-confirm.message" -msgstr "Bu dosyayı silmek istediğinden emin misin?" - -#: src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.delete-file-confirm.accept" -msgstr "Dosyayı sil" - -#: src/app/main/ui/comments.cljs -msgid "modals.delete-comment-thread.title" -msgstr "Konuşmayı sil" - -#: src/app/main/ui/comments.cljs -msgid "modals.delete-comment-thread.message" -msgstr "" -"Bu konuşmayı silmek istediğinden emin misin? Konudaki tüm yorumlar silinecek." - -#: src/app/main/ui/comments.cljs -msgid "modals.delete-comment-thread.accept" -msgstr "Konuşmayı sil" - -msgid "handoff.tabs.code.selected.frame" -msgstr "Çalışma yüzeyi" - -msgid "handoff.attributes.typography.text-transform.titlecase" -msgstr "İlk Harfleri Büyük" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-transform" -msgstr "Metin Dönüşümü" - -#: src/app/main/ui/handoff/attributes/text.cljs -msgid "handoff.attributes.typography.text-decoration" -msgstr "Metin Süsleme" - -#: src/app/main/ui/handoff/attributes/shadow.cljs -msgid "handoff.attributes.shadow.shorthand.spread" -msgstr "Y" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.vcenter" -msgstr "Dikey olarak ortaya hizala" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.vbottom" -msgstr "Alta hizala" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.hright" -msgstr "Sağa hizala" - -#: src/app/main/ui/workspace/sidebar/align.cljs -msgid "workspace.align.hleft" -msgstr "Sola hizala" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.sitemap" -msgstr "Site haritası" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.show-interactions-on-click" -msgstr "Tıklamada etkileşimleri göster" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.show-interactions" -msgstr "Etkileşimleri göster" - -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "Bağlantıyı paylaş" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.subtitle" -msgstr "Bağlantıya sahip herkes erişebilecek" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.remove-link" -msgstr "Bağlantıyı kaldır" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.placeholder" -msgstr "Paylaşım adresi burada görünecek" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.create-link" -msgstr "Bağlantı oluştur" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.copy-link" -msgstr "Bağlantıyı kopyala" - -#: src/app/main/ui/viewer/header.cljs -msgid "viewer.header.fullscreen" -msgstr "Tam Ekran" - -#: src/app/main/ui/dashboard/files.cljs -msgid "title.dashboard.files" -msgstr "%s - Penpot" - -#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs -msgid "settings.multiple" -msgstr "Karışık" - -#: src/app/main/ui/auth/recovery.cljs -msgid "profile.recovery.go-to-login" -msgstr "Giriş yap" +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.add-shared-confirm.message" +msgstr "Paylaşılmış Kütüphane olarak “%s” Ekle" #: src/app/main/ui/settings/change_email.cljs -msgid "notifications.validation-email-sent" -msgstr "" -"%s adresine doğrulama e-postası gönderildi. E-postalarınızı kontrol edin!" +msgid "modals.change-email.confirm-email" +msgstr "Yeni e-postayı doğrulayın" -#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs -msgid "notifications.profile-saved" -msgstr "Profil başarıyla kaydedildi!" +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.info" +msgstr "" +"“%s” e-posta adresinize kimliğinizi doğrulamak için bir e-posta " +"göndereceğiz." + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.new-email" +msgstr "Yeni e-posta" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.submit" +msgstr "E-postayı değiştir" + +#: src/app/main/ui/settings/change_email.cljs +msgid "modals.change-email.title" +msgstr "E-postanızı değiştirin" #: src/app/main/ui/settings/delete_account.cljs -msgid "notifications.profile-deletion-not-allowed" -msgstr "Profilini silemezsin. Önce takımlarını birine atamalsın." +msgid "modals.delete-account.cancel" +msgstr "İptal et ve hesabımı koru" -#: src/app/main/ui/dashboard/team.cljs -msgid "notifications.invitation-email-sent" -msgstr "Davet başarıyla iletildi" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "modals.update-remote-component.message" -msgstr "Paylaşılmış bir kütüphanede bir bileşen güncelle" - -#: src/app/main/ui/dashboard/team.cljs -msgid "title.team-settings" -msgstr "Ayarlar * %s - Penpot" - -#: src/app/main/ui/dashboard/team.cljs -msgid "title.team-members" -msgstr "Üyeler - %s - Penpot" - -#: src/app/main/ui/settings/profile.cljs -msgid "title.settings.profile" -msgstr "Profil - Penpot" - -#: src/app/main/ui/settings/password.cljs -msgid "title.settings.password" -msgstr "Parola - Penpot" - -#: src/app/main/ui/settings/options.cljs -msgid "title.settings.options" -msgstr "Ayarlar - Penpot" - -#: src/app/main/ui/settings/feedback.cljs -msgid "title.settings.feedback" -msgstr "Geri bildirimde bulun - Penpot" - -#: src/app/main/ui/dashboard/search.cljs -msgid "title.dashboard.search" -msgstr "Ara - %s - Penpot" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.no-libraries-need-sync" -msgstr "Güncelleme gerektiren Paylaşılmış Kütüphane bulunmuyor" - -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.title.group" -msgstr "Gölge grubu" - -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.title" -msgstr "Gölge" - -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.offsetx" -msgstr "X" - -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.go-main" -msgstr "Ana bileşen dosyasına git" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.front" -msgstr "En öne getir" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.forward" -msgstr "Öne getir" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.edit" -msgstr "Düzenle" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.duplicate" -msgstr "Çoğalt" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.delete" -msgstr "Sil" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.cut" -msgstr "Kes" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.create-component" -msgstr "Bileşen oluştur" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.copy" -msgstr "Kopyala" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.backward" -msgstr "En arkaya gönder" - -msgid "workspace.path.actions.move-nodes" -msgstr "Düğümleri taşı (%s)" - -msgid "workspace.path.actions.merge-nodes" -msgstr "Düğümleri birleştir (%s)" - -msgid "workspace.path.actions.make-curve" -msgstr "Eğriye (%s)" - -msgid "workspace.path.actions.make-corner" -msgstr "Köşeye (%s)" - -msgid "workspace.path.actions.join-nodes" -msgstr "Düğümleri birleştir (%s)" - -msgid "workspace.path.actions.draw-nodes" -msgstr "Düğüm çiz (%s)" - -msgid "workspace.path.actions.delete-node" -msgstr "Düğüm sil (%s)" - -msgid "workspace.options.text-options.vertical-align" -msgstr "Düşey hizalama" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.uppercase" -msgstr "Büyük Harf" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.underline" -msgstr "Altı Çizili" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -#, fuzzy -msgid "workspace.options.text-options.title-selection" -msgstr "Metin seçimi" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.title-group" -msgstr "Grup metni" - -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs -msgid "workspace.options.text-options.title" -msgstr "Metin" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.lowercase" -msgstr "Küçük harf" - -msgid "workspace.undo.entry.multiple.group" -msgstr "gruplar" - -msgid "workspace.undo.entry.multiple.curve" -msgstr "eğriler" - -msgid "workspace.undo.entry.multiple.component" -msgstr "bileşenler" - -msgid "workspace.undo.entry.multiple.color" -msgstr "renk varlıkları" - -msgid "workspace.undo.entry.multiple.circle" -msgstr "daireler" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.entry.move" -msgstr "Nesneler taşındı" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.entry.modify" -msgstr "%s düzenlendi" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.entry.delete" -msgstr "%s silindi" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.empty" -msgstr "Şu ana kadar değişim geçmişi yok" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.text" -msgstr "Metin (%s)" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.move" -msgstr "Taşı" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.image" -msgstr "Resim (%s)" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.ellipse" -msgstr "Elips (%s)" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.comments" -msgstr "Yorumlar (%s)" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.assets" -msgstr "Varlıklar(%s)" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs -msgid "workspace.sidebar.sitemap" -msgstr "Sayfalar" - -#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs -msgid "workspace.sidebar.options.svg-attrs.title" -msgstr "SVG Öznitelikleri İçeri Aktarıldı" - -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.sidebar.layers" -msgstr "Katmanlar (%s)" - -#: src/app/main/data/workspace/libraries.cljs -msgid "workspace.updates.there-are-updates" -msgstr "Paylaşılmış kütüphanelerde güncellemeler mevcut" - -#: src/app/main/data/workspace/libraries.cljs -msgid "workspace.updates.dismiss" -msgstr "Gözardı et" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.entry.unknown" -msgstr "%s üstündeki işlem" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.title" -msgstr "Geçmiş" - -msgid "workspace.undo.entry.single.text" -msgstr "metin" - -msgid "workspace.undo.entry.single.shape" -msgstr "şekil" - -msgid "workspace.undo.entry.single.rect" -msgstr "dikdörtgen" - -msgid "workspace.undo.entry.single.page" -msgstr "sayfa" - -msgid "workspace.undo.entry.single.multiple" -msgstr "nesne" - -msgid "workspace.undo.entry.single.media" -msgstr "grafik varlığı" - -msgid "workspace.undo.entry.single.image" -msgstr "resim" - -msgid "workspace.undo.entry.single.group" -msgstr "grup" - -msgid "workspace.undo.entry.single.curve" -msgstr "eğri" - -msgid "workspace.undo.entry.single.component" -msgstr "bileşen" - -msgid "workspace.undo.entry.single.color" -msgstr "renk varlığı" - -msgid "workspace.undo.entry.single.circle" -msgstr "daire" - -#: src/app/main/ui/workspace/sidebar/history.cljs -msgid "workspace.undo.entry.new" -msgstr "Yeni %s" - -msgid "workspace.undo.entry.multiple.text" -msgstr "metinler" - -msgid "workspace.undo.entry.multiple.shape" -msgstr "şekiller" - -msgid "workspace.undo.entry.multiple.rect" -msgstr "dikdörtgenler" - -msgid "workspace.undo.entry.multiple.page" -msgstr "sayfalar" - -msgid "workspace.undo.entry.multiple.multiple" -msgstr "nesneler" - -msgid "workspace.undo.entry.multiple.media" -msgstr "grafik varlığı" - -#: src/app/main/data/workspace/libraries.cljs -msgid "workspace.updates.update" -msgstr "Güncelle" - -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.dotted" -msgstr "Noktalı" - -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.dashed" -msgstr "Çizgili" - -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs -msgid "workspace.options.stroke.center" -msgstr "Merkez" - -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.blend-mode.color" -msgstr "Renk" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.square" -msgstr "Kare" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.row" -msgstr "Satırlar" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.width" -msgstr "Genişlik" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.use-default" -msgstr "Varsayılanı kullan" - -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs -msgid "workspace.options.grid.params.type.top" -msgstr "Üst" - -#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs -msgid "workspace.options.blur-options.title" -msgstr "Bulanıklık" - -msgid "workspace.options.blur-options.layer-blur" -msgstr "Katman" - -msgid "workspace.options.blur-options.background-blur" -msgstr "Arkaplan" - -msgid "workspace.library.own" -msgstr "Kütüphanelerim" - -msgid "workspace.library.libraries" -msgstr "Kütüphaneler" - -msgid "workspace.library.all" -msgstr "Tüm kütüphaneler" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.updates" -msgstr "GÜNCELLEMELER" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.update" -msgstr "Güncelle" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.search-shared-libraries" -msgstr "Paylaşılmış kütüphane ara" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.shared-libraries" -msgstr "PAYLAŞILMIŞ KÜTÜPHANELER" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.no-shared-libraries-available" -msgstr "Paylaşılmış Kütüphane bulunmuyor" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.no-matches-for" -msgstr "“%s“ için eşleşme bulunmadı" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.components" -msgstr "%s bileşen" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.library" -msgstr "KÜTÜPHANE" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.libraries" -msgstr "KÜTÜPHANELER" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.in-this-file" -msgstr "BU DOSYADAKİ KÜTÜPHANELER" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.graphics" -msgstr "%s grafik" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.file-library" -msgstr "Dosya kütüphanesi" - -#: src/app/main/ui/workspace/colorpicker.cljs -msgid "workspace.libraries.colors.save-color" -msgstr "Renk biçimini kaydet" - -#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs -msgid "workspace.libraries.colors.recent-colors" -msgstr "Son renkler" - -#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs -msgid "workspace.libraries.colors.file-library" -msgstr "Dosya kütüphanesi" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.colors" -msgstr "%s renk" - -#: src/app/main/ui/workspace/libraries.cljs -msgid "workspace.libraries.add" -msgstr "Ekle" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.viewer" -msgstr "Görünüm modu (%s)" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.unsaved" -msgstr "Kaydedilmemiş değişiklikler" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.saving" -msgstr "Kaydediliyor" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.saved" -msgstr "Kaydedildi" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.save-error" -msgstr "Kaydetmede hata" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.show-rules" -msgstr "Cetveli göster" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.show-palette" -msgstr "Renk paletini göster" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.show-layers" -msgstr "Katmanları göster" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.show-grid" -msgstr "Izgarayı göster" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.show-assets" -msgstr "Varlıkları göster" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.select-all" -msgstr "Tümünü seç" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.hide-rules" -msgstr "Cetveli gizle" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.hide-palette" -msgstr "Renk paletini gizle" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.hide-layers" -msgstr "Katmanları gizle" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.hide-grid" -msgstr "Izgaraları gizle" - -#: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.hide-assets" -msgstr "Varlıkları gizle" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.text-transform" -msgstr "Metin Dönüşümü" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.line-height" -msgstr "Satır Yüksekliği" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.letter-spacing" -msgstr "Harf Boşluğu" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.font-size" -msgstr "Boyut" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.assets.typography.font-id" -msgstr "Yazı tipi" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.shared" -msgstr "PAYLAŞILDI" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.selected-count" -msgid_plural "workspace.assets.selected-count" -msgstr[0] "Tek öge seçildi" -msgstr[1] "%s öge seçildi" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.search" -msgstr "Varlık ara" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.rename" -msgstr "Yeniden adlandır" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.not-found" -msgstr "Varlık bulunmadı" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.libraries" -msgstr "Kütüphaneler" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.group-name" -msgstr "Grup adı" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.group" -msgstr "Grup" - -#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.graphics" -msgstr "Grafikler" - -#: src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.file-library" -msgstr "Dosya kütüphanesi" - -#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.edit" -msgstr "Düzenle" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.duplicate" -msgstr "Çoğalt" - -#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs -msgid "workspace.assets.delete" -msgstr "Sil" - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.promote-owner-confirm.accept" -msgstr "Terfi et" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-confirm.message" -msgstr "Bu takımdan ayrılmak istediğinden emin misin?" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-confirm.accept" -msgstr "Takımdan ayrıl" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.title" -msgstr "Terfi etmek için bir üye seçin" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.select-memeber-to-promote" -msgstr "Terfi etmek için bir üye seçin" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.promote-and-leave" -msgstr "Terfi et ve ayrıl" - -#: src/app/main/ui/dashboard/sidebar.cljs -msgid "modals.leave-and-reassign.hint1" -msgstr "%s sahibisiniz." - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.invite-member.title" -msgstr "Takıma katılma daveti gönder" - -#: src/app/main/ui/dashboard/team.cljs -msgid "modals.invite-member-confirm.accept" -msgstr "Davet gönder" +#: src/app/main/ui/settings/delete_account.cljs +msgid "modals.delete-account.confirm" +msgstr "Evet, hesabımı sil" #: src/app/main/ui/settings/delete_account.cljs msgid "modals.delete-account.info" @@ -2203,229 +1178,1365 @@ msgstr "Hesabını silerek tüm projelerini ve arşivlerini kaybedeceksin." msgid "modals.delete-account.title" msgstr "Hesabını silmek istediğinden emin misin?" -#: src/app/main/ui/settings/delete_account.cljs -msgid "modals.delete-account.confirm" -msgstr "Evet, hesabımı sil" - -#: src/app/main/ui/settings/delete_account.cljs -msgid "modals.delete-account.cancel" -msgstr "İptal et ve hesabımı koru" - -#: src/app/main/ui/settings/change_email.cljs -msgid "modals.change-email.title" -msgstr "E-postanızı değiştirin" - -#: src/app/main/ui/settings/change_email.cljs -msgid "modals.change-email.submit" -msgstr "E-postayı değiştir" - -#: src/app/main/ui/settings/change_email.cljs -msgid "modals.change-email.new-email" -msgstr "Yeni e-posta" - -#: src/app/main/ui/settings/change_email.cljs -msgid "modals.change-email.info" -msgstr "" -"“%s” e-posta adresinize kimliğinizi doğrulamak için bir e-posta göndereceğiz." - -#: src/app/main/ui/settings/change_email.cljs -msgid "modals.change-email.confirm-email" -msgstr "Yeni e-postayı doğrulayın" - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.add-shared-confirm.message" -msgstr "Paylaşılmış Kütüphane olarak “%s” Ekle" - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.add-shared-confirm.hint" -msgstr "" -"Paylaşılmış Kütüphane olarak eklenince, bu dosya kütüphanesindeki varlıklar " -"diğer dosyalarınızdan da ulaşılabilecek." - -#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs -msgid "modals.add-shared-confirm.accept" -msgstr "Paylaşılmış Kütüphane olarak Ekle" - -#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs -msgid "media.loading" -msgstr "Resim yükleniyor…" +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.accept" +msgstr "Konuşmayı sil" #: src/app/main/ui/comments.cljs -msgid "labels.write-new-comment" -msgstr "Yeni yorum yaz" +msgid "modals.delete-comment-thread.message" +msgstr "" +"Bu konuşmayı silmek istediğinden emin misin? Konudaki tüm yorumlar " +"silinecek." + +#: src/app/main/ui/comments.cljs +msgid "modals.delete-comment-thread.title" +msgstr "Konuşmayı sil" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.accept" +msgstr "Dosyayı sil" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.message" +msgstr "Bu dosyayı silmek istediğinden emin misin?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-confirm.title" +msgstr "Dosya siliniyor" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "Dosyalar sil" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "%s dosyayı silmek istediğinden emin misin?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "%s dosyanın silinmesi" + +msgid "modals.delete-font.message" +msgstr "" +"Bu fontu silmek istediğine emin misin? Bir dosyada kullanılıyorsa " +"yüklenmeyecektir." + +msgid "modals.delete-font.title" +msgstr "Fontu sil" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.body" +msgstr "Bu sayfayı silmek istediğinden emin misin?" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "modals.delete-page.title" +msgstr "Sayfayı sil" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.accept" +msgstr "Projeyi sil" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.message" +msgstr "Bu projeyi silmek istediğinden emin misin?" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "modals.delete-project-confirm.title" +msgstr "Projeyi sil" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.accept" +msgstr "Takımı sil" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.message" +msgstr "" +"Bu takımı silmek istediğinden emin misin? Takımla ilişkili dosyalar ve " +"projeler kalıcı olarak silinecektir." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.delete-team-confirm.title" +msgstr "Takımın silinmesi" #: src/app/main/ui/dashboard/team.cljs -msgid "labels.viewer" -msgstr "Görüntüler" +msgid "modals.delete-team-member-confirm.accept" +msgstr "Üyeyi sil" -msgid "labels.upload-custom-fonts" -msgstr "Özel yazı tipi yükle" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.message" +msgstr "Bu üyeyi takımdan silmek istediğinden emin misin?" -#: src/app/main/ui/dashboard/team_form.cljs -msgid "labels.update-team" -msgstr "Takımı güncelle" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.delete-team-member-confirm.title" +msgstr "Takım üyesini sil" -msgid "labels.icons" -msgstr "Simgeler" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member-confirm.accept" +msgstr "Davet gönder" -#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs -msgid "handoff.attributes.layout.radius" -msgstr "Yarı Çap" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member.title" +msgstr "Takıma katılma daveti gönder" -#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs -msgid "title.default" -msgstr "Penpot * Takımlar için Özgür Tasarım" +msgid "modals.leave-and-reassign.forbiden" +msgstr "" +"Birisini takımın sahibi yapmadan takımı bırakamazsın. Takımı silmek " +"isteyebilirsin." -#: src/app/main/ui/dashboard/libraries.cljs -msgid "title.dashboard.shared-libraries" -msgstr "Paylaşılmış Kütüphaneler - %s - Penpot" +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint1" +msgstr "%s sahibisiniz." + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.hint2" +msgstr "Ayrılmadan önce terfi etmek için başka bir üye seçin" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.promote-and-leave" +msgstr "Terfi et ve ayrıl" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.select-memeber-to-promote" +msgstr "Terfi etmek için bir üye seçin" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-and-reassign.title" +msgstr "Terfi etmek için bir üye seçin" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.accept" +msgstr "Takımdan ayrıl" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.message" +msgstr "Bu takımdan ayrılmak istediğinden emin misin?" + +#: src/app/main/ui/dashboard/sidebar.cljs +msgid "modals.leave-confirm.title" +msgstr "Takımdan ayrılmak" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.accept" +msgstr "Terfi et" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.message" +msgstr "Bu kullanıcıyı sahip olarak terfi etmek istediğinden emin misin?" + +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.promote-owner-confirm.title" +msgstr "Sahip olarak terfi et" + +#: src/app/main/ui/workspace/header.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.remove-shared-confirm.accept" +msgstr "Paylaşılmış Kütüphane olarak kaldır" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.accept" +msgstr "Bileşeni güncelle" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.cancel" +msgstr "İptal" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.hint" +msgstr "" +"Paylaşılmış bir kütüphanedeki bileşeni güncellemek üzeresin. Onu kullanan " +"diğer dosyalar etkilenebilir." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component.message" +msgstr "Paylaşılmış bir kütüphanede bir bileşen güncelle" + +#: src/app/main/ui/dashboard/team.cljs +msgid "notifications.invitation-email-sent" +msgstr "Davet başarıyla iletildi" + +#: src/app/main/ui/settings/delete_account.cljs +msgid "notifications.profile-deletion-not-allowed" +msgstr "Profilini silemezsin. Önce takımlarını birine atamalsın." + +#: src/app/main/ui/settings/profile.cljs, src/app/main/ui/settings/options.cljs +msgid "notifications.profile-saved" +msgstr "Profil başarıyla kaydedildi!" + +#: src/app/main/ui/settings/change_email.cljs +msgid "notifications.validation-email-sent" +msgstr "%s adresine doğrulama e-postası gönderildi. E-postalarınızı kontrol edin!" + +#: src/app/main/ui/auth/recovery.cljs +msgid "profile.recovery.go-to-login" +msgstr "Giriş yap" + +#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "settings.multiple" +msgstr "Karışık" + +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.font-providers" +msgstr "Yazıtipi Sağlayıcıları - %s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.fonts" +msgstr "Fontlar - %s - Penpot" #: src/app/main/ui/dashboard/projects.cljs msgid "title.dashboard.projects" msgstr "Projeler - %s - Penpot" +#: src/app/main/ui/dashboard/search.cljs +msgid "title.dashboard.search" +msgstr "Ara - %s - Penpot" + +#: src/app/main/ui/dashboard/libraries.cljs +msgid "title.dashboard.shared-libraries" +msgstr "Paylaşılmış Kütüphaneler - %s - Penpot" + +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot * Takımlar için Özgür Tasarım" + +#: src/app/main/ui/settings/feedback.cljs +msgid "title.settings.feedback" +msgstr "Geri bildirimde bulun - Penpot" + +#: src/app/main/ui/settings/options.cljs +msgid "title.settings.options" +msgstr "Ayarlar - Penpot" + +#: src/app/main/ui/settings/password.cljs +msgid "title.settings.password" +msgstr "Parola - Penpot" + +#: src/app/main/ui/settings/profile.cljs +msgid "title.settings.profile" +msgstr "Profil - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-members" +msgstr "Üyeler - %s - Penpot" + +#: src/app/main/ui/dashboard/team.cljs +msgid "title.team-settings" +msgstr "Ayarlar * %s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "title.viewer" +msgstr "%s - Görünüm modu - Penpot" + +#: src/app/main/ui/workspace.cljs +msgid "title.workspace" +msgstr "%s - Penpot" + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.empty-state" +msgstr "Sayfada çerçeve bulunmuyor." + +#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs +msgid "viewer.frame-not-found" +msgstr "Çerçeve bulunmadı." + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.dont-show-interactions" +msgstr "Etkileşimleri gösterme" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "Dosyayı düzenle" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-page" +msgstr "Sayfayı düzenle" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.fullscreen" +msgstr "Tam Ekran" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.interactions" +msgstr "Etkileşimler" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.copy-link" +msgstr "Bağlantıyı kopyala" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.create-link" +msgstr "Bağlantı oluştur" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.placeholder" +msgstr "Paylaşım adresi burada görünecek" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.remove-link" +msgstr "Bağlantıyı kaldır" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.share.subtitle" +msgstr "Bağlantıya sahip herkes erişebilecek" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions" +msgstr "Etkileşimleri göster" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.show-interactions-on-click" +msgstr "Tıklamada etkileşimleri göster" + +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.sitemap" +msgstr "Site haritası" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hcenter" +msgstr "Yatay olarak ortaya hizala" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hdistribute" +msgstr "Yatayda dağıt" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hleft" +msgstr "Sola hizala" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.hright" +msgstr "Sağa hizala" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vbottom" +msgstr "Alta hizala" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vcenter" +msgstr "Dikey olarak ortaya hizala" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vdistribute" +msgstr "Dikeyde dağıt" + +#: src/app/main/ui/workspace/sidebar/align.cljs +msgid "workspace.align.vtop" +msgstr "Üste hizala" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.assets" +msgstr "Varlıklar" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.box-filter-all" +msgstr "Tüm varlıklar" + +msgid "workspace.assets.box-filter-graphics" +msgstr "Grafikler" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.colors" +msgstr "Renkler" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.components" +msgstr "Bileşenler" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "Grup oluştur" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.delete" +msgstr "Sil" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.duplicate" +msgstr "Çoğalt" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.edit" +msgstr "Düzenle" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.file-library" +msgstr "Dosya kütüphanesi" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.graphics" +msgstr "Grafikler" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group" +msgstr "Grup" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group-name" +msgstr "Grup adı" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.libraries" +msgstr "Kütüphaneler" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.not-found" +msgstr "Varlık bulunmadı" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename" +msgstr "Yeniden adlandır" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename-group" +msgstr "Grubu yeniden isimlendir" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.search" +msgstr "Varlık ara" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.selected-count" +msgid_plural "workspace.assets.selected-count" +msgstr[0] "Tek öge seçildi" +msgstr[1] "%s öge seçildi" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.shared" +msgstr "PAYLAŞILDI" + +#: src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.typography" +msgstr "Tipografiler" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-id" +msgstr "Yazı tipi" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-size" +msgstr "Boyut" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.font-variant-id" +msgstr "Çeşit" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.go-to-edit" +msgstr "Düzenlemek için biçim kütüphane dosyasına gidin" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.letter-spacing" +msgstr "Harf Boşluğu" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.line-height" +msgstr "Satır Yüksekliği" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.assets.typography.text-transform" +msgstr "Metin Dönüşümü" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.ungroup" +msgstr "Grubu dağıt" + +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.linear" +msgstr "Doğrusal degrade" + +#: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs +msgid "workspace.gradients.radial" +msgstr "Dairesel degrade" + #: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.enable-snap-grid" -msgstr "Izgaraya tuttur" +msgid "workspace.header.menu.disable-dynamic-alignment" +msgstr "Dinamik hizalamayı kapat" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-scale-text" +msgstr "Metin ölçeklendirmeyi kapat" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-snap-grid" +msgstr "Izgaraya tutturmayı kapat" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-dynamic-alignment" +msgstr "Dinamik hizalamayı etkinleştir" #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.menu.enable-scale-text" msgstr "Metin ölçeklendirmeyi etkinleştir" #: src/app/main/ui/workspace/header.cljs -msgid "workspace.header.menu.enable-dynamic-alignment" -msgstr "Dinamik hizalamayı etkinleştir" +msgid "workspace.header.menu.enable-snap-grid" +msgstr "Izgaraya tuttur" -#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.size" -msgstr "Boyut" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-assets" +msgstr "Varlıkları gizle" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.title.multiple" -msgstr "Gölge seçimi" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-grid" +msgstr "Izgaraları gizle" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.spread" -msgstr "Yayılma" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-layers" +msgstr "Katmanları gizle" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.offsety" -msgstr "Y" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-palette" +msgstr "Renk paletini gizle" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.inner-shadow" -msgstr "İç gölge" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.hide-rules" +msgstr "Cetveli gizle" -#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs -msgid "workspace.options.shadow-options.drop-shadow" -msgstr "Kabartı gölgesi" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.select-all" +msgstr "Tümünü seç" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-assets" +msgstr "Varlıkları göster" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-grid" +msgstr "Izgarayı göster" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-layers" +msgstr "Katmanları göster" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-palette" +msgstr "Renk paletini göster" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.show-rules" +msgstr "Cetveli göster" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.save-error" +msgstr "Kaydetmede hata" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saved" +msgstr "Kaydedildi" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.saving" +msgstr "Kaydediliyor" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.unsaved" +msgstr "Kaydedilmemiş değişiklikler" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.viewer" +msgstr "Görünüm modu (%s)" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.add" +msgstr "Ekle" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.colors" +msgstr "%s renk" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.big-thumbnails" +msgstr "Büyük önizlemeler" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.file-library" +msgstr "Dosya kütüphanesi" + +#: src/app/main/ui/workspace/colorpicker/libraries.cljs, src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.recent-colors" +msgstr "Son renkler" + +#: src/app/main/ui/workspace/colorpicker.cljs +msgid "workspace.libraries.colors.save-color" +msgstr "Renk biçimini kaydet" + +#: src/app/main/ui/workspace/colorpalette.cljs +msgid "workspace.libraries.colors.small-thumbnails" +msgstr "Küçük önizlemeler" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.components" +msgstr "%s bileşen" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.file-library" +msgstr "Dosya kütüphanesi" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.graphics" +msgstr "%s grafik" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.in-this-file" +msgstr "BU DOSYADAKİ KÜTÜPHANELER" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.libraries" +msgstr "KÜTÜPHANELER" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.library" +msgstr "KÜTÜPHANE" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-libraries-need-sync" +msgstr "Güncelleme gerektiren Paylaşılmış Kütüphane bulunmuyor" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-matches-for" +msgstr "“%s“ için eşleşme bulunmadı" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.no-shared-libraries-available" +msgstr "Paylaşılmış Kütüphane bulunmuyor" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.search-shared-libraries" +msgstr "Paylaşılmış kütüphane ara" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.shared-libraries" +msgstr "PAYLAŞILMIŞ KÜTÜPHANELER" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography" +msgstr "Çoklu tipografiler" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.libraries.text.multiple-typography-tooltip" +msgstr "Tüm tipografileri ayır" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.typography" +msgstr "%s tipografi" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.update" +msgstr "Güncelle" + +#: src/app/main/ui/workspace/libraries.cljs +msgid "workspace.libraries.updates" +msgstr "GÜNCELLEMELER" + +msgid "workspace.library.all" +msgstr "Tüm kütüphaneler" + +msgid "workspace.library.libraries" +msgstr "Kütüphaneler" + +msgid "workspace.library.own" +msgstr "Kütüphanelerim" + +msgid "workspace.options.blur-options.background-blur" +msgstr "Arkaplan" + +msgid "workspace.options.blur-options.layer-blur" +msgstr "Katman" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +msgid "workspace.options.blur-options.title" +msgstr "Bulanıklık" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs +msgid "workspace.options.component" +msgstr "Bileşen" #: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.rotation" -msgstr "Döndür" +msgid "workspace.options.constraints" +msgstr "Kısıtlamalar" #: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.radius.single-corners" -msgstr "Tek köşe" +msgid "workspace.options.constraints.bottom" +msgstr "Alt" #: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.radius.all-corners" -msgstr "Tüm köşeler" +msgid "workspace.options.constraints.center" +msgstr "Merkez" -msgid "workspace.options.radius" -msgstr "Yarı çap" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.fix-when-scrolling" +msgstr "Kaydırırken sabit" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.left" +msgstr "Sol" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.leftright" +msgstr "Sol ve Sağ" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.right" +msgstr "Sağ" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.scale" +msgstr "Ölçeklendir" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.top" +msgstr "Üst" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.topbottom" +msgstr "Üst ve Alt" #: src/app/main/ui/workspace/sidebar/options.cljs -msgid "workspace.options.prototype" -msgstr "Prototip" +msgid "workspace.options.design" +msgstr "Tasarım" -#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs -msgid "workspace.options.position" -msgstr "Konum" +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export" +msgstr "Dışarı Aktar" -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.none" -msgstr "Hiç biri" +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.export-object" +msgstr "Şekli dışarı aktar" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +msgid "workspace.options.export.suffix" +msgstr "Son ek" + +#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs +msgid "workspace.options.exporting-object" +msgstr "Dışarı aktarılıyor…" + +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +msgid "workspace.options.fill" +msgstr "Doldur" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.auto" +msgstr "Otomatik" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.column" +msgstr "Sütunlar" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.columns" +msgstr "Sütunlar" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.height" +msgstr "Yükseklik" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.margin" +msgstr "Kenar Boşluğu" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.rows" +msgstr "Satırlar" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.set-default" +msgstr "Varsayılan olarak belirle" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.size" +msgstr "Boyut" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type" +msgstr "Tür" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.bottom" +msgstr "Alt" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.center" +msgstr "Orta" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.left" +msgstr "Sol" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.right" +msgstr "Sağ" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.stretch" +msgstr "Ger" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.type.top" +msgstr "Üst" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.use-default" +msgstr "Varsayılanı kullan" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.params.width" +msgstr "Genişlik" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.row" +msgstr "Satırlar" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +msgid "workspace.options.grid.square" +msgstr "Kare" #: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.title.multiple" -msgstr "Seçili katmanlar" +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "Renk" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-burn" +msgstr "Renk yanması" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.normal" +msgstr "Normal" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.overlay" +msgstr "Üst katman" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.saturation" +msgstr "Doygunluk" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.screen" +msgstr "Ekran" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title" +msgstr "Katman" #: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs msgid "workspace.options.layer-options.title.group" msgstr "Katman grubu" #: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs -msgid "workspace.options.layer-options.title" -msgstr "Katman" +msgid "workspace.options.layer-options.title.multiple" +msgstr "Seçili katmanlar" -#: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.sidebar.history" -msgstr "Geçmiş (%s)" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.navigate-to" +msgstr "Git" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.update-main" -msgstr "Ana bileşeni güncelle" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.none" +msgstr "Hiç biri" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.position" +msgstr "Konum" + +#: src/app/main/ui/workspace/sidebar/options.cljs +msgid "workspace.options.prototype" +msgstr "Prototip" + +msgid "workspace.options.radius" +msgstr "Yarı çap" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.all-corners" +msgstr "Tüm köşeler" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.radius.single-corners" +msgstr "Tek köşe" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.rotation" +msgstr "Döndür" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-a-shape" +msgstr "" +"Diğer çalışma yüzeyine bağlantı taşımak için bir şekil, çalışma yüzeyi ya " +"da grup seçin." + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.select-artboard" +msgstr "Çalışma yüzeyi seç" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.drop-shadow" +msgstr "Kabartı gölgesi" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.inner-shadow" +msgstr "İç gölge" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsetx" +msgstr "X" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.offsety" +msgstr "Y" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.spread" +msgstr "Yayılma" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title" +msgstr "Gölge" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.group" +msgstr "Gölge grubu" + +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +msgid "workspace.options.shadow-options.title.multiple" +msgstr "Gölge seçimi" + +#: src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.size" +msgstr "Boyut" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke" +msgstr "Kontur" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.center" +msgstr "Merkez" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dashed" +msgstr "Çizgili" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.dotted" +msgstr "Noktalı" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.inner" +msgstr "İçinde" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.mixed" +msgstr "Karışık" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.outer" +msgstr "Dışında" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +msgid "workspace.options.stroke.solid" +msgstr "Katı" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-bottom" +msgstr "Alta hizala" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-center" +msgstr "Ortaya hizala" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-justify" +msgstr "İki yana yasla" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-left" +msgstr "Sola hizala" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-middle" +msgstr "Merkeze hizala" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-right" +msgstr "Sağa hizala" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.align-top" +msgstr "Üste hizala" + +msgid "workspace.options.text-options.decoration" +msgstr "Süsleme" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-ltr" +msgstr "Soldan sağa" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-rtl" +msgstr "Sağdan sola" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.google" +msgstr "Google" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-height" +msgstr "Otomatik yükseklik" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-auto-width" +msgstr "Otomatik genişlik" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.grow-fixed" +msgstr "Sabit" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.letter-spacing" +msgstr "Harf Aralıkları" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.line-height" +msgstr "Satır yüksekliği" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.lowercase" +msgstr "Küçük harf" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.none" +msgstr "Hiçbiri" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.strikethrough" +msgstr "Üstü çizili" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title" +msgstr "Metin" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title-group" +msgstr "Grup metni" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.title-selection" +msgstr "Metin seçimi" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.titlecase" +msgstr "İlk Harfi Büyük" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.underline" +msgstr "Altı Çizili" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +msgid "workspace.options.text-options.uppercase" +msgstr "Büyük Harf" + +msgid "workspace.options.text-options.vertical-align" +msgstr "Düşey hizalama" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.use-play-button" +msgstr "Prototip görünümünü çalıştırmak için başlıktaki oynatma düğmesini kullan." + +msgid "workspace.path.actions.add-node" +msgstr "Düğüm ekle (%s)" + +msgid "workspace.path.actions.delete-node" +msgstr "Düğüm sil (%s)" + +msgid "workspace.path.actions.draw-nodes" +msgstr "Düğüm çiz (%s)" + +msgid "workspace.path.actions.join-nodes" +msgstr "Düğümleri birleştir (%s)" + +msgid "workspace.path.actions.make-corner" +msgstr "Köşeye (%s)" + +msgid "workspace.path.actions.make-curve" +msgstr "Eğriye (%s)" + +msgid "workspace.path.actions.merge-nodes" +msgstr "Düğümleri birleştir (%s)" + +msgid "workspace.path.actions.move-nodes" +msgstr "Düğümleri taşı (%s)" + +msgid "workspace.path.actions.separate-nodes" +msgstr "Düğümleri ayır (%s)" + +msgid "workspace.path.actions.snap-nodes" +msgstr "Düğümleri tuttur (%s)" #: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.unlock" -msgstr "Çöz" +msgid "workspace.shape.menu.back" +msgstr "Arkaya gönder" #: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.ungroup" -msgstr "Grubu dağıt" +msgid "workspace.shape.menu.backward" +msgstr "En arkaya gönder" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy" +msgstr "Kopyala" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.create-component" +msgstr "Bileşen oluştur" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.cut" +msgstr "Kes" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.delete" +msgstr "Sil" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.duplicate" +msgstr "Çoğalt" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.edit" +msgstr "Düzenle" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-horizontal" +msgstr "Yatay ters çevir" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.flip-vertical" +msgstr "Dikey ters çevir" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.forward" +msgstr "Öne getir" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.front" +msgstr "En öne getir" #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.show-main" -msgstr "Ana bileşeni göster" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.show" -msgstr "Göster" - -#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.paste" -msgstr "Yapıştır" - -#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.mask" -msgstr "Maskele" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.lock" -msgstr "Kilitle" - -#: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.hide" -msgstr "Gizle" +msgid "workspace.shape.menu.go-main" +msgstr "Ana bileşen dosyasına git" #: src/app/main/ui/workspace/context_menu.cljs msgid "workspace.shape.menu.group" msgstr "Grup" #: src/app/main/ui/workspace/context_menu.cljs -msgid "workspace.shape.menu.back" -msgstr "Arkaya gönder" +msgid "workspace.shape.menu.hide" +msgstr "Gizle" -msgid "workspace.path.actions.separate-nodes" -msgstr "Düğümleri ayır (%s)" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.lock" +msgstr "Kilitle" -msgid "workspace.path.actions.add-node" -msgstr "Düğüm ekle (%s)" +#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.mask" +msgstr "Maskele" -#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs -msgid "workspace.options.use-play-button" -msgstr "" -"Prototip görünümünü çalıştırmak için başlıktaki oynatma düğmesini kullan." +#: src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.paste" +msgstr "Yapıştır" -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs -msgid "workspace.options.text-options.none" -msgstr "Hiçbiri" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show" +msgstr "Göster" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.show-main" +msgstr "Ana bileşeni göster" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.ungroup" +msgstr "Grubu dağıt" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.unlock" +msgstr "Çöz" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.update-main" +msgstr "Ana bileşeni güncelle" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.history" +msgstr "Geçmiş (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.sidebar.layers" +msgstr "Katmanlar (%s)" + +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs +msgid "workspace.sidebar.options.svg-attrs.title" +msgstr "SVG Öznitelikleri İçeri Aktarıldı" + +#: src/app/main/ui/workspace/sidebar/sitemap.cljs +msgid "workspace.sidebar.sitemap" +msgstr "Sayfalar" + +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.sitemap" +msgstr "Site haritası" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.assets" +msgstr "Varlıklar(%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.color-palette" +msgstr "Renk Paketi (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.comments" +msgstr "Yorumlar (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.curve" +msgstr "Eğri (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.ellipse" +msgstr "Elips (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.frame" +msgstr "Çalışma Yüzeyi (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.image" +msgstr "Resim (%s)" + +#: src/app/main/ui/workspace/left_toolbar.cljs +msgid "workspace.toolbar.move" +msgstr "Taşı" #: src/app/main/ui/workspace/left_toolbar.cljs msgid "workspace.toolbar.rect" msgstr "Dikdörtgen (%s)" #: src/app/main/ui/workspace/left_toolbar.cljs -msgid "workspace.toolbar.color-palette" -msgstr "Renk Paketi (%s)" +msgid "workspace.toolbar.text" +msgstr "Metin (%s)" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.empty" +msgstr "Şu ana kadar değişim geçmişi yok" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.delete" +msgstr "%s silindi" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.modify" +msgstr "%s düzenlendi" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.move" +msgstr "Nesneler taşındı" + +msgid "workspace.undo.entry.multiple.circle" +msgstr "daireler" + +msgid "workspace.undo.entry.multiple.color" +msgstr "renk varlıkları" + +msgid "workspace.undo.entry.multiple.component" +msgstr "bileşenler" + +msgid "workspace.undo.entry.multiple.curve" +msgstr "eğriler" + +msgid "workspace.undo.entry.multiple.frame" +msgstr "çalışma yüzeyi" + +msgid "workspace.undo.entry.multiple.group" +msgstr "gruplar" + +msgid "workspace.undo.entry.multiple.media" +msgstr "grafik varlığı" + +msgid "workspace.undo.entry.multiple.multiple" +msgstr "nesneler" + +msgid "workspace.undo.entry.multiple.page" +msgstr "sayfalar" + +msgid "workspace.undo.entry.multiple.rect" +msgstr "dikdörtgenler" + +msgid "workspace.undo.entry.multiple.shape" +msgstr "şekiller" + +msgid "workspace.undo.entry.multiple.text" +msgstr "metinler" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.new" +msgstr "Yeni %s" + +msgid "workspace.undo.entry.single.circle" +msgstr "daire" + +msgid "workspace.undo.entry.single.color" +msgstr "renk varlığı" + +msgid "workspace.undo.entry.single.component" +msgstr "bileşen" + +msgid "workspace.undo.entry.single.curve" +msgstr "eğri" + +msgid "workspace.undo.entry.single.frame" +msgstr "çalışma yüzeyi" + +msgid "workspace.undo.entry.single.group" +msgstr "grup" + +msgid "workspace.undo.entry.single.image" +msgstr "resim" + +msgid "workspace.undo.entry.single.media" +msgstr "grafik varlığı" + +msgid "workspace.undo.entry.single.multiple" +msgstr "nesne" + +msgid "workspace.undo.entry.single.page" +msgstr "sayfa" + +msgid "workspace.undo.entry.single.rect" +msgstr "dikdörtgen" + +msgid "workspace.undo.entry.single.shape" +msgstr "şekil" + +msgid "workspace.undo.entry.single.text" +msgstr "metin" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.entry.unknown" +msgstr "%s üstündeki işlem" + +#: src/app/main/ui/workspace/sidebar/history.cljs +msgid "workspace.undo.title" +msgstr "Geçmiş" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.dismiss" +msgstr "Gözardı et" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.there-are-updates" +msgstr "Paylaşılmış kütüphanelerde güncellemeler mevcut" + +#: src/app/main/data/workspace/libraries.cljs +msgid "workspace.updates.update" +msgstr "Güncelle" \ No newline at end of file diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po index 37b1d70051..71d16f01c7 100644 --- a/frontend/translations/zh_CN.po +++ b/frontend/translations/zh_CN.po @@ -1,10 +1,15 @@ msgid "" msgstr "" +"PO-Revision-Date: 2021-07-30 16:34+0000\n" +"Last-Translator: Maemolee \n" +"Language-Team: Chinese (Simplified) " +"\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 4.7.2-dev\n" #: src/app/main/ui/auth/register.cljs msgid "auth.already-have-account" @@ -66,10 +71,18 @@ msgstr "使用Github登录" msgid "auth.login-with-gitlab-submit" msgstr "使用Gitlab登录" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-google-submit" +msgstr "使用Google登录" + #: src/app/main/ui/auth/login.cljs msgid "auth.login-with-ldap-submit" msgstr "使用LDAP登录" +#: src/app/main/ui/auth/login.cljs +msgid "auth.login-with-oidc-submit" +msgstr "使用OpenID (SSO)登录" + #: src/app/main/ui/auth/recovery.cljs msgid "auth.new-password" msgstr "输入新的密码" @@ -136,7 +149,11 @@ msgstr "创建账号" #: src/app/main/ui/auth.cljs msgid "auth.sidebar-tagline" -msgstr "设计与原型的开源解决方案" +msgstr "设计与原型的开源解决方案。" + +#: src/app/main/ui/auth/register.cljs +msgid "auth.terms-privacy-agreement" +msgstr "创建账号意味着您认可我们的服务条例和隐私政策。" #: src/app/main/ui/auth/register.cljs msgid "auth.verification-email-sent" @@ -150,6 +167,10 @@ msgstr "添加为共享库" msgid "dashboard.change-email" msgstr "修改电子邮件" +#: src/app/main/data/dashboard.cljs, src/app/main/data/dashboard.cljs +msgid "dashboard.copy-suffix" +msgstr "(拷贝)" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.create-new-team" msgstr "+ 创建新团队" @@ -165,10 +186,45 @@ msgstr "删除团队" msgid "dashboard.draft-title" msgstr "草稿" +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate" +msgstr "复制" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.duplicate-multi" +msgstr "复制 %s 个文件" + #: src/app/main/ui/dashboard/grid.cljs msgid "dashboard.empty-files" msgstr "暂无文档" +msgid "dashboard.export-multi" +msgstr "导出 %s 个文件" + +msgid "dashboard.export-single" +msgstr "导出文件" + +msgid "dashboard.fonts.deleted-placeholder" +msgstr "字体已删除" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "您仍然没有已安装得字体。" + +#, markdown +msgid "dashboard.fonts.hero-text1" +msgstr "" +"你在此上传的任何网络字体文件,将会被添加至本团队下文件的字体属性中的可用字体族列表中。拥有相同字体族名称的字体文件,将会按照字体族进行分组。你可以上传以" +"下格式的字体文件:**TTF,OTF和WOFF**(你只需要上传其中一种即可)。" + +#, markdown +msgid "dashboard.fonts.hero-text2" +msgstr "" +"你应当只向Penpot上传你所拥有的字体,或是你持有使用许可的字体。点击[Penpot服务条例](https://penpot.app/terms." +"html)查看更多有关内容权利的章节。或许你也想了解一下[字体许可授权] (https://www.typography.com/faq)。" + +msgid "dashboard.import" +msgstr "导入文件" + #: src/app/main/ui/dashboard/team.cljs msgid "dashboard.invite-profile" msgstr "邀请加入团队" @@ -185,14 +241,37 @@ msgstr "共享库" msgid "dashboard.loading-files" msgstr "正在加载文档…" +msgid "dashboard.loading-fonts" +msgstr "正在加载字体…" + +#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to" +msgstr "移动到" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-multi" +msgstr "移动 %s 个文件到" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.move-to-other-team" +msgstr "移动到其他团队" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/files.cljs msgid "dashboard.new-file" msgstr "+ 新文档" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-file-prefix" +msgstr "新建文件" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.new-project" msgstr "+ 新项目" +#: src/app/main/data/dashboard.cljs +msgid "dashboard.new-project-prefix" +msgstr "新建项目" + #: src/app/main/ui/dashboard/search.cljs msgid "dashboard.no-matches-for" msgstr "没有找到“%s”的匹配项" @@ -217,10 +296,21 @@ msgstr "已经成功保存密码!" msgid "dashboard.num-of-members" msgstr "成员%s人" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.open-in-new-tab" +msgstr "在新标签页打开文件" + +msgid "dashboard.options" +msgstr "选项" + #: src/app/main/ui/settings/password.cljs msgid "dashboard.password-change" msgstr "修改密码" +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.pin-unpin" +msgstr "钉到侧边栏/取消钉住" + #: src/app/main/ui/dashboard/projects.cljs msgid "dashboard.projects-title" msgstr "项目" @@ -243,7 +333,7 @@ msgstr "搜索…" #: src/app/main/ui/dashboard/search.cljs msgid "dashboard.searching-for" -msgstr "正在搜索“%s”" +msgstr "正在搜索“%s”…" #: src/app/main/ui/settings/options.cljs msgid "dashboard.select-ui-language" @@ -257,6 +347,34 @@ msgstr "选择界面主题" msgid "dashboard.show-all-files" msgstr "显示全部文档" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-delete-file" +msgstr "成功删除了文件" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-delete-project" +msgstr "成功删除了项目" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-duplicate-file" +msgstr "成功创建了文件副本" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-duplicate-project" +msgstr "成功创建了项目副本" + +#: src/app/main/ui/dashboard/grid.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-file" +msgstr "成功移动了文件" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "dashboard.success-move-files" +msgstr "成功移动了文件" + +#: src/app/main/ui/dashboard/project_menu.cljs +msgid "dashboard.success-move-project" +msgstr "成功移动了项目" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "dashboard.switch-team" msgstr "切换团队" @@ -318,8 +436,9 @@ msgid "ds.confirm-title" msgstr "你确定?" #: src/app/main/ui/dashboard/grid.cljs +#, fuzzy msgid "ds.updated-at" -msgstr "更新了:%s" +msgstr "最后更新:%s" #: src/app/main/data/workspace.cljs msgid "errors.clipboard-not-implemented" @@ -331,11 +450,11 @@ msgstr "电子邮件已被占用" #: src/app/main/ui/auth/verify_token.cljs msgid "errors.email-already-validated" -msgstr "电子邮件已经验证通过" +msgstr "电子邮件已经验证通过。" #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs msgid "errors.email-has-permanent-bounces" -msgstr "电子邮件“%s”收到了非常多的永久退信报告" +msgstr "电子邮件“%s”收到了非常多的永久退信报告。" #: src/app/main/ui/settings/change_email.cljs msgid "errors.email-invalid-confirmation" @@ -349,6 +468,10 @@ msgstr "发生了某种错误。" msgid "errors.google-auth-not-enabled" msgstr "后端禁用了Google授权" +#: src/app/main/ui/components/color_input.cljs +msgid "errors.invalid-color" +msgstr "无效的颜色" + #: src/app/main/ui/auth/login.cljs msgid "errors.ldap-disabled" msgstr "仅用了LDAP授权。" @@ -377,11 +500,11 @@ msgstr "无法连接到后端服务器。" #: src/app/main/ui/settings/password.cljs msgid "errors.password-invalid-confirmation" -msgstr "确认密码必须保持一致。" +msgstr "确认密码必须保持一致" #: src/app/main/ui/settings/password.cljs msgid "errors.password-too-short" -msgstr "密码最少需要8位字符。" +msgstr "密码最少需要8位字符" #: src/app/main/ui/auth/recovery_request.cljs, src/app/main/ui/settings/change_email.cljs, src/app/main/ui/dashboard/team.cljs msgid "errors.profile-is-muted" @@ -391,13 +514,20 @@ msgstr "你设置了邮件免打扰(报告垃圾邮件或者多次退信)。 msgid "errors.registration-disabled" msgstr "当前禁止注册。" +msgid "errors.terms-privacy-agreement-invalid" +msgstr "你必须接受我们的使用条例和隐私政策。" + +#: src/app/main/ui/auth/verify_token.cljs +msgid "errors.token-expired" +msgstr "令牌已过期" + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "发生了意料之外的错误。" #: src/app/main/ui/auth/verify_token.cljs msgid "errors.unexpected-token" -msgstr "未知的TOKEN。" +msgstr "未知的令牌" #: src/app/main/ui/auth/login.cljs msgid "errors.wrong-credentials" @@ -665,6 +795,9 @@ msgstr "你正在查看%s版本" msgid "labels.accept" msgstr "接受" +msgid "labels.add-custom-font" +msgstr "添加自定义字体" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.admin" msgstr "管理员" @@ -699,10 +832,21 @@ msgstr "确认密码" msgid "labels.content" msgstr "内容" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "labels.create" +msgstr "创建" + #: src/app/main/ui/dashboard/team_form.cljs, src/app/main/ui/dashboard/team_form.cljs msgid "labels.create-team" msgstr "创建新团队" +#: src/app/main/ui/dashboard/team_form.cljs +msgid "labels.create-team.placeholder" +msgstr "输入新的团队名称" + +msgid "labels.custom-fonts" +msgstr "自定义字体" + #: src/app/main/ui/settings/sidebar.cljs msgid "labels.dashboard" msgstr "面板" @@ -719,6 +863,10 @@ msgstr "删除该评论" msgid "labels.delete-comment-thread" msgstr "删除该讨论串" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "labels.delete-multi-files" +msgstr "删除%s个文件" + #: src/app/main/ui/dashboard/projects.cljs, src/app/main/ui/dashboard/sidebar.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/files.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "labels.drafts" msgstr "草稿" @@ -743,6 +891,18 @@ msgstr "反馈被禁止" msgid "labels.feedback-sent" msgstr "反馈已发出" +msgid "labels.font-family" +msgstr "字体族" + +msgid "labels.font-providers" +msgstr "字体提供者" + +msgid "labels.font-variants" +msgstr "样式" + +msgid "labels.fonts" +msgstr "字体" + #: src/app/main/ui/workspace/header.cljs, src/app/main/ui/settings/sidebar.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.give-feedback" msgstr "提交反馈" @@ -760,6 +920,9 @@ msgstr "图标" msgid "labels.images" msgstr "图片" +msgid "labels.installed-fonts" +msgstr "已安装字体" + #: src/app/main/ui/static.cljs msgid "labels.internal-error.desc-message" msgstr "发生了一些不妙的事。请尝试重新操作。如果问题仍然存在,请联系我们以取得支持。" @@ -776,6 +939,9 @@ msgstr "语言" msgid "labels.logout" msgstr "登出" +msgid "labels.manage-fonts" +msgstr "管理字体" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/sidebar.cljs msgid "labels.members" msgstr "成员" @@ -810,6 +976,11 @@ msgid_plural "labels.num-of-files" msgstr[0] "1 个文档" msgstr[1] "共 %s 个文档" +msgid "labels.num-of-frames" +msgid_plural "labels.num-of-frames" +msgstr[0] "1个画板" +msgstr[1] "%s个画板" + #: src/app/main/ui/dashboard/team.cljs msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" @@ -824,6 +995,9 @@ msgstr "旧密码" msgid "labels.only-yours" msgstr "仅你的" +msgid "labels.or" +msgstr "或" + #: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs msgid "labels.owner" msgstr "所有者" @@ -874,6 +1048,9 @@ msgstr "角色" msgid "labels.save" msgstr "保存" +msgid "labels.search-font" +msgstr "搜索字体" + #: src/app/main/ui/settings/feedback.cljs msgid "labels.send" msgstr "发送" @@ -894,6 +1071,9 @@ msgstr "服务不可用" msgid "labels.settings" msgstr "设置" +msgid "labels.share-prototype" +msgstr "分享原型" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "labels.shared-libraries" msgstr "共享库" @@ -918,6 +1098,15 @@ msgstr "更新" msgid "labels.update-team" msgstr "更新团队" +msgid "labels.upload" +msgstr "上传" + +msgid "labels.upload-custom-fonts" +msgstr "上传自定义字体" + +msgid "labels.uploading" +msgstr "正在上传…" + #: src/app/main/ui/dashboard/team.cljs msgid "labels.viewer" msgstr "查看者" @@ -1002,6 +1191,30 @@ msgstr "你确定想要删除这个文档?" msgid "modals.delete-file-confirm.title" msgstr "正在删除文档" +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.accept" +msgstr "删除文件" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.message" +msgstr "你确定要删除这%s个文件吗?" + +#: src/app/main/ui/dashboard/file_menu.cljs +msgid "modals.delete-file-multi-confirm.title" +msgstr "正在删除%s个文件" + +msgid "modals.delete-font-variant.message" +msgstr "你确定要删除这个字体样式吗?如果它被用在某个文件里的话,将无法加载。" + +msgid "modals.delete-font-variant.title" +msgstr "正在删除字体样式" + +msgid "modals.delete-font.message" +msgstr "你确定要删除这个字体吗?如果它被用在某个文件里的话,将无法加载。" + +msgid "modals.delete-font.title" +msgstr "正在删除字体" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs msgid "modals.delete-page.body" msgstr "你确定想要删除这个页面?" @@ -1046,17 +1259,24 @@ msgstr "你确定想要从团队中删除这个成员?" msgid "modals.delete-team-member-confirm.title" msgstr "删除团队成员" +#: src/app/main/ui/dashboard/team.cljs +msgid "modals.invite-member-confirm.accept" +msgstr "发送邀请" + #: src/app/main/ui/dashboard/team.cljs msgid "modals.invite-member.title" msgstr "邀请加入团队" +msgid "modals.leave-and-reassign.forbiden" +msgstr "如果不能推选另一个成员作为团队所有者,你就无法离开团队。你或许想要删除该团队。" + #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-and-reassign.hint1" msgstr "你是%s的所有者。" #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-and-reassign.hint2" -msgstr "请在退出前,从其他成员中选择一位晋升。" +msgstr "请在退出前,从其他成员中选择一位晋升" #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-and-reassign.promote-and-leave" @@ -1076,7 +1296,7 @@ msgstr "退出团队" #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-confirm.message" -msgstr "选择一位成员晋升" +msgstr "你确定要离开本团队吗?" #: src/app/main/ui/dashboard/sidebar.cljs msgid "modals.leave-confirm.title" @@ -1122,6 +1342,10 @@ msgstr "你即将更新共享库中的一个组件。这可能会对使用该组 msgid "modals.update-remote-component.message" msgstr "更新共享库中的一个组件" +#: src/app/main/ui/dashboard/team.cljs +msgid "notifications.invitation-email-sent" +msgstr "成功发送邀请" + #: src/app/main/ui/settings/delete_account.cljs msgid "notifications.profile-deletion-not-allowed" msgstr "你无法删除你的个人资料。请先转让你的团队。" @@ -1132,7 +1356,7 @@ msgstr "个人资料保存成功!" #: src/app/main/ui/settings/change_email.cljs msgid "notifications.validation-email-sent" -msgstr "验证邮件已发至%s。请检查电子邮箱。" +msgstr "验证邮件已发至%s。请检查电子邮箱!" #: src/app/main/ui/auth/recovery.cljs msgid "profile.recovery.go-to-login" @@ -1142,6 +1366,18 @@ msgstr "去登录" msgid "settings.multiple" msgstr "混合" +#: src/app/main/ui/dashboard/files.cljs +msgid "title.dashboard.files" +msgstr "%s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.font-providers" +msgstr "字体提供者 - %s - Penpot" + +#: src/app/main/ui/dashboard/fonts.cljs +msgid "title.dashboard.fonts" +msgstr "字体 - %s - Penpot" + #: src/app/main/ui/dashboard/projects.cljs msgid "title.dashboard.projects" msgstr "项目 - %s - Penpot" @@ -1154,6 +1390,10 @@ msgstr "搜索 - %s - Penpot" msgid "title.dashboard.shared-libraries" msgstr "共享库 - %s - Penpot" +#: src/app/main/ui/auth/verify_token.cljs, src/app/main/ui/auth.cljs +msgid "title.default" +msgstr "Penpot - 面向团队,设计自由" + #: src/app/main/ui/settings/feedback.cljs msgid "title.settings.feedback" msgstr "提交反馈 - Penpot" @@ -1182,6 +1422,10 @@ msgstr "设置 - %s - Penpot" msgid "title.viewer" msgstr "%s - 预览模式)- Penpot" +#: src/app/main/ui/workspace.cljs +msgid "title.workspace" +msgstr "%s - Penpot" + #: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs msgid "viewer.empty-state" msgstr "该页面上未找到任何画框。" @@ -1194,6 +1438,10 @@ msgstr "画框未找到。" msgid "viewer.header.dont-show-interactions" msgstr "不显示交互" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.edit-file" +msgstr "编辑文件" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.edit-page" msgstr "编辑页面" @@ -1202,6 +1450,10 @@ msgstr "编辑页面" msgid "viewer.header.fullscreen" msgstr "全屏" +#: src/app/main/ui/viewer/header.cljs +msgid "viewer.header.interactions" +msgstr "交互" + #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.share.copy-link" msgstr "复制链接" @@ -1222,10 +1474,6 @@ msgstr "移除链接" msgid "viewer.header.share.subtitle" msgstr "任何人都可以通过本链接访问" -#: src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs, src/app/main/ui/viewer/header.cljs -msgid "viewer.header.share.title" -msgstr "分享链接" - #: src/app/main/ui/viewer/header.cljs msgid "viewer.header.show-interactions" msgstr "显示交互" @@ -1289,6 +1537,14 @@ msgstr "颜色" msgid "workspace.assets.components" msgstr "组件" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group" +msgstr "创建组" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.create-group-hint" +msgstr "这些物件将按照“组名/物件名”的格式自动命名" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs, src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.delete" msgstr "删除" @@ -1309,6 +1565,14 @@ msgstr "文档库" msgid "workspace.assets.graphics" msgstr "图形" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group" +msgstr "组" + +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.group-name" +msgstr "组名" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.libraries" msgstr "库" @@ -1321,10 +1585,20 @@ msgstr "未找到素材" msgid "workspace.assets.rename" msgstr "重命名" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.rename-group" +msgstr "重命名组" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.search" msgstr "搜索素材" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.selected-count" +msgid_plural "workspace.assets.selected-count" +msgstr[0] "已选中%s个物件" +msgstr[1] "已选中%s个物件" + #: src/app/main/ui/workspace/sidebar/assets.cljs msgid "workspace.assets.shared" msgstr "共享的" @@ -1365,6 +1639,10 @@ msgstr "Ag" msgid "workspace.assets.typography.text-transform" msgstr "文本变换" +#: src/app/main/ui/workspace/sidebar/assets.cljs +msgid "workspace.assets.ungroup" +msgstr "解组" + #: src/app/main/data/workspace/libraries.cljs, src/app/main/ui/components/color_bullet.cljs msgid "workspace.gradients.linear" msgstr "线性渐变" @@ -1377,6 +1655,10 @@ msgstr "放射渐变" msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "禁用动态对齐" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.disable-scale-text" +msgstr "禁用缩放文本" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.menu.disable-snap-grid" msgstr "禁用吸附到网格" @@ -1385,6 +1667,10 @@ msgstr "禁用吸附到网格" msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "启用动态对齐" +#: src/app/main/ui/workspace/header.cljs +msgid "workspace.header.menu.enable-scale-text" +msgstr "启用缩放文本" + #: src/app/main/ui/workspace/header.cljs msgid "workspace.header.menu.enable-snap-grid" msgstr "吸附到网格" @@ -1583,6 +1869,46 @@ msgstr "画布背景" msgid "workspace.options.component" msgstr "组件" +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints" +msgstr "约束" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.bottom" +msgstr "底部" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.center" +msgstr "中心" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.fix-when-scrolling" +msgstr "滚动时保持固定" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.left" +msgstr "左侧" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.leftright" +msgstr "左右两侧" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.right" +msgstr "右侧" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.scale" +msgstr "缩放" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.top" +msgstr "顶部" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +msgid "workspace.options.constraints.topbottom" +msgstr "顶部与底部" + #: src/app/main/ui/workspace/sidebar/options.cljs msgid "workspace.options.design" msgstr "设计" @@ -1699,6 +2025,82 @@ msgstr "编组填充" msgid "workspace.options.group-stroke" msgstr "编组边框" +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color" +msgstr "颜色" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-burn" +msgstr "颜色加深" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.color-dodge" +msgstr "颜色减淡" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.darken" +msgstr "变暗" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.difference" +msgstr "差值" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.exclusion" +msgstr "排除" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hard-light" +msgstr "强光" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.hue" +msgstr "色相" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.lighten" +msgstr "变亮" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.luminosity" +msgstr "亮度" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.multiply" +msgstr "正片叠底" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.normal" +msgstr "正常" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.overlay" +msgstr "叠加" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.saturation" +msgstr "饱和度" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.screen" +msgstr "滤色" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.blend-mode.soft-light" +msgstr "柔光" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title" +msgstr "图层" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.group" +msgstr "图层成组" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +msgid "workspace.options.layer-options.title.multiple" +msgstr "已选中的图层" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.navigate-to" msgstr "导航到" @@ -1853,6 +2255,14 @@ msgstr "顶部对齐" msgid "workspace.options.text-options.decoration" msgstr "装饰" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-ltr" +msgstr "从左到右" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs +msgid "workspace.options.text-options.direction-rtl" +msgstr "从右到左" + #: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs msgid "workspace.options.text-options.google" msgstr "谷歌" @@ -1927,6 +2337,28 @@ msgstr "垂直对齐" msgid "workspace.options.use-play-button" msgstr "点击页面顶端的播放按钮预览原型。" +msgid "workspace.path.actions.add-node" +msgstr "添加节点(%s)" + +msgid "workspace.path.actions.delete-node" +msgstr "删除节点(%s)" + +msgid "workspace.path.actions.draw-nodes" +msgstr "绘制节点(%s)" + +#, fuzzy +msgid "workspace.path.actions.join-nodes" +msgstr "连接节点(%s)" + +msgid "workspace.path.actions.merge-nodes" +msgstr "合并节点(%s)" + +msgid "workspace.path.actions.move-nodes" +msgstr "移动节点(%s)" + +msgid "workspace.path.actions.separate-nodes" +msgstr "拆分节点(%s)" + #: src/app/main/ui/workspace/context_menu.cljs msgid "workspace.shape.menu.back" msgstr "移至底层" @@ -2039,6 +2471,10 @@ msgstr "历史(%s)" msgid "workspace.sidebar.layers" msgstr "图层(%s)" +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs +msgid "workspace.sidebar.options.svg-attrs.title" +msgstr "已导入SVG属性" + #: src/app/main/ui/workspace/sidebar/sitemap.cljs msgid "workspace.sidebar.sitemap" msgstr "页面" diff --git a/version.txt b/version.txt index a303e786bf..1d16151a4e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.7.4-alpha +1.8.0-alpha