diff --git a/backend/scripts/repl b/backend/scripts/repl index 3c8b1a6c6..1f434abcf 100755 --- a/backend/scripts/repl +++ b/backend/scripts/repl @@ -2,7 +2,7 @@ export PENPOT_ASSERTS_ENABLED=true -export OPTIONS="-A:jmx-remote:dev -J-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -J-Xms512m -J-Xmx512m -J-Dlog4j2.configurationFile=log4j2-devenv.xml"; +export OPTIONS="-A:jmx-remote:dev -J-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -J-Dlog4j2.configurationFile=log4j2-devenv.xml -J-XX:+UseZGC -J-XX:ConcGCThreads=1 -J-XX:-OmitStackTraceInFastThrow -J-Xms50m -J-Xmx512m"; # export OPTIONS="$OPTIONS -J-XX:+UnlockDiagnosticVMOptions"; # export OPTIONS="$OPTIONS -J-XX:-TieredCompilation -J-XX:CompileThreshold=10000"; diff --git a/backend/src/app/migrations/sql/0053-add-team-font-variant-table.sql b/backend/src/app/migrations/sql/0053-add-team-font-variant-table.sql index 423ed2556..e03d1da99 100644 --- a/backend/src/app/migrations/sql/0053-add-team-font-variant-table.sql +++ b/backend/src/app/migrations/sql/0053-add-team-font-variant-table.sql @@ -1,20 +1,43 @@ CREATE TABLE team_font_variant ( id uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE, - profile_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL, + team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE DEFERRABLE, + profile_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL DEFERRABLE, created_at timestamptz NOT NULL DEFAULT now(), modified_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz NULL DEFAULT NULL, - font_id text NOT NULL, + font_id uuid NOT NULL, font_family text NOT NULL, font_weight smallint NOT NULL, font_style text NOT NULL, - otf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL, - ttf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL, - woff1_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL, - woff2_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL + otf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE, + ttf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE, + woff1_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE, + woff2_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE ); + +CREATE INDEX team_font_variant_team_id_font_id_idx + ON team_font_variant (team_id, font_id); + +CREATE INDEX team_font_variant_profile_id_idx + ON team_font_variant (profile_id); + +CREATE INDEX team_font_variant_otf_file_id_idx + ON team_font_variant (otf_file_id); + +CREATE INDEX team_font_variant_ttf_file_id_idx + ON team_font_variant (ttf_file_id); + +CREATE INDEX team_font_variant_woff1_file_id_idx + ON team_font_variant (woff1_file_id); + +CREATE INDEX team_font_variant_woff2_file_id_idx + ON team_font_variant (woff2_file_id); + +ALTER TABLE team_font_variant + ALTER COLUMN font_family SET STORAGE external, + ALTER COLUMN font_style SET STORAGE external; + diff --git a/backend/src/app/rpc/mutations/fonts.clj b/backend/src/app/rpc/mutations/fonts.clj index e5194174f..ca1d2263e 100644 --- a/backend/src/app/rpc/mutations/fonts.clj +++ b/backend/src/app/rpc/mutations/fonts.clj @@ -16,20 +16,20 @@ [app.util.services :as sv] [app.util.time :as dt] [app.worker :as wrk] - [clojure.spec.alpha :as s] - [cuerdas.core :as str])) + [clojure.spec.alpha :as s])) (declare create-font-variant) (def valid-weight #{100 200 300 400 500 600 700 800 900 950}) (def valid-style #{"normal" "italic"}) +(s/def ::id ::us/uuid) (s/def ::profile-id ::us/uuid) (s/def ::team-id ::us/uuid) (s/def ::name ::us/not-empty-string) (s/def ::weight valid-weight) (s/def ::style valid-style) -(s/def ::font-id (s/and ::us/string #(str/starts-with? % "custom-"))) +(s/def ::font-id ::us/uuid) (s/def ::content-type ::media/font-content-type) (s/def ::data (s/map-of ::us/string any?)) @@ -76,29 +76,57 @@ :otf-file-id (:id otf) :ttf-file-id (:id ttf)}))) -;; --- UPDATE FONT VARIANT +;; --- UPDATE FONT FAMILY -(s/def ::update-font-variant - (s/keys :req-un [::profile-id ::team-id ::id ::font-family ::font-id])) +(s/def ::update-font + (s/keys :req-un [::profile-id ::team-id ::id ::name])) -(sv/defmethod ::update-font-variant - [{:keys [pool] :as cfg} {:keys [id team-id profile-id font-family font-id] :as params}] +(def sql:update-font + "update team_font_variant + set font_family = ? + where team_id = ? + and font_id = ?") + +(sv/defmethod ::update-font + [{:keys [pool] :as cfg} {:keys [team-id profile-id id name] :as params}] (db/with-atomic [conn pool] (teams/check-edition-permissions! conn profile-id team-id) - (db/update! conn :team-font-variant - {:font-family font-family - :font-id font-id} - {:id id - :team-id team-id}) + (db/exec-one! conn [sql:update-font name team-id id]) nil)) +;; --- DELETE FONT + +(s/def ::delete-font + (s/keys :req-un [::profile-id ::team-id ::id])) + +(sv/defmethod ::delete-font + [{:keys [pool] :as cfg} {:keys [id team-id profile-id] :as params}] + (db/with-atomic [conn pool] + (teams/check-edition-permissions! conn profile-id team-id) + + (let [items (db/query conn :team-font-variant + {:font-id id :team-id team-id} + {:for-update true})] + (doseq [item items] + ;; Schedule object deletion + (wrk/submit! {::wrk/task :delete-object + ::wrk/delay cf/deletion-delay + ::wrk/conn conn + :id (:id item) + :type :team-font-variant})) + + (db/update! conn :team-font-variant + {:deleted-at (dt/now)} + {:font-id id :team-id team-id}) + nil))) + ;; --- DELETE FONT VARIANT (s/def ::delete-font-variant (s/keys :req-un [::profile-id ::team-id ::id])) (sv/defmethod ::delete-font-variant - [{:keys [pool] :as cfg} {:keys [id team-id profile-id font-family font-id] :as params}] + [{:keys [pool] :as cfg} {:keys [id team-id profile-id] :as params}] (db/with-atomic [conn pool] (teams/check-edition-permissions! conn profile-id team-id) @@ -111,6 +139,5 @@ (db/update! conn :team-font-variant {:deleted-at (dt/now)} - {:id id - :team-id team-id}) + {:id id :team-id team-id}) nil)) diff --git a/backend/tests/app/tests/test_services_fonts.clj b/backend/tests/app/tests/test_services_fonts.clj index 50ee24abf..86836b71e 100644 --- a/backend/tests/app/tests/test_services_fonts.clj +++ b/backend/tests/app/tests/test_services_fonts.clj @@ -22,6 +22,7 @@ (let [prof (th/create-profile* 1 {:is-active true}) team-id (:default-team-id prof) proj-id (:default-project-id prof) + font-id (uuid/custom 10 1) ttfdata (-> (io/resource "app/tests/_files/font-1.ttf") (fs/slurp-bytes)) @@ -29,7 +30,7 @@ params {::th/type :create-font-variant :profile-id (:id prof) :team-id team-id - :font-id "custom-somefont" + :font-id font-id :font-family "somefont" :font-weight 400 :font-style "normal" @@ -56,6 +57,7 @@ (let [prof (th/create-profile* 1 {:is-active true}) team-id (:default-team-id prof) proj-id (:default-project-id prof) + font-id (uuid/custom 10 1) data (-> (io/resource "app/tests/_files/font-1.woff") (fs/slurp-bytes)) @@ -63,7 +65,7 @@ params {::th/type :create-font-variant :profile-id (:id prof) :team-id team-id - :font-id "custom-somefont" + :font-id font-id :font-family "somefont" :font-weight 400 :font-style "normal" diff --git a/common/app/common/media.cljc b/common/app/common/media.cljc index 108c21b10..cbf1fb826 100644 --- a/common/app/common/media.cljc +++ b/common/app/common/media.cljc @@ -69,17 +69,17 @@ (defn parse-font-weight [variant] (cond - (re-seq #"(?i)(?:hairline|thin)" variant) 100 - (re-seq #"(?i)(?:extra light|ultra light)" variant) 200 - (re-seq #"(?i)(?:light)" variant) 300 - (re-seq #"(?i)(?:normal|regular)" variant) 400 - (re-seq #"(?i)(?:medium)" variant) 500 - (re-seq #"(?i)(?:semi bold|demi bold)" variant) 600 - (re-seq #"(?i)(?:bold)" variant) 700 - (re-seq #"(?i)(?:extra bold|ultra bold)" variant) 800 - (re-seq #"(?i)(?:black|heavy)" variant) 900 - (re-seq #"(?i)(?:extra black|ultra black)" variant) 950 - :else 400)) + (re-seq #"(?i)(?:hairline|thin)" variant) 100 + (re-seq #"(?i)(?:extra\s*light|ultra\s*light)" variant) 200 + (re-seq #"(?i)(?:light)" variant) 300 + (re-seq #"(?i)(?:normal|regular)" variant) 400 + (re-seq #"(?i)(?:medium)" variant) 500 + (re-seq #"(?i)(?:semi\s*bold|demi\s*bold)" variant) 600 + (re-seq #"(?i)(?:extra\s*bold|ultra\s*bold)" variant) 800 + (re-seq #"(?i)(?:bold)" variant) 700 + (re-seq #"(?i)(?:extra\s*black|ultra\s*black)" variant) 950 + (re-seq #"(?i)(?:black|heavy)" variant) 900 + :else 400)) (defn parse-font-style [variant] diff --git a/frontend/resources/images/features/custom-fonts.gif b/frontend/resources/images/features/custom-fonts.gif new file mode 100644 index 000000000..191dcf36c Binary files /dev/null and b/frontend/resources/images/features/custom-fonts.gif differ diff --git a/frontend/resources/images/features/performance.gif b/frontend/resources/images/features/performance.gif new file mode 100644 index 000000000..afd4c582a Binary files /dev/null and b/frontend/resources/images/features/performance.gif differ diff --git a/frontend/resources/images/features/scale-text.gif b/frontend/resources/images/features/scale-text.gif new file mode 100644 index 000000000..bc56494a6 Binary files /dev/null and b/frontend/resources/images/features/scale-text.gif differ diff --git a/frontend/resources/images/features/shapes-to-path.gif b/frontend/resources/images/features/shapes-to-path.gif new file mode 100644 index 000000000..cea102568 Binary files /dev/null and b/frontend/resources/images/features/shapes-to-path.gif differ diff --git a/frontend/resources/styles/common/dependencies/colors.scss b/frontend/resources/styles/common/dependencies/colors.scss index bd995bc8c..5a9c19c4d 100644 --- a/frontend/resources/styles/common/dependencies/colors.scss +++ b/frontend/resources/styles/common/dependencies/colors.scss @@ -23,6 +23,7 @@ $color-info: #59b9e2; $color-ocean: #4285f4; $color-component: #76B0B8; $color-component-highlight: #00E0FF; +$color-pink: #feecfc; // Gray scale $color-gray-10: #E3E3E3; diff --git a/frontend/resources/styles/main/partials/dashboard-fonts.scss b/frontend/resources/styles/main/partials/dashboard-fonts.scss index 864f17e87..0f61a12a3 100644 --- a/frontend/resources/styles/main/partials/dashboard-fonts.scss +++ b/frontend/resources/styles/main/partials/dashboard-fonts.scss @@ -30,8 +30,13 @@ align-items: center; padding: 0px $big; - > div { - width: 30%; + > .family { + min-width: 200px; + width: 200px; + } + + > .variants { + padding-left: 12px; } .search-input { @@ -50,20 +55,18 @@ } } - .fonts-group { - margin-top: $big; - } - .font-item { + margin-top: $big; color: $color-gray-40; font-size: $fs14; background-color: $color-white; display: flex; min-width: 1000px; width: 100%; - height: 97px; + min-height: 97px; align-items: center; padding: $big; + justify-content: space-between; &:not(:first-child) { border-top: 1px solid $color-gray-10; @@ -77,14 +80,52 @@ font-size: $fs12; } - > div { - width: 30%; + > .family { + min-width: 200px; + width: 200px; } - .variant { - font-size: $fs14; + > .filenames { + min-width: 200px; } + > .variants { + font-size: $fs14; + display: flex; + flex-wrap: wrap; + flex-grow: 1; + + .variant { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + cursor: pointer; + + .icon { + display: flex; + height: 16px; + width: 16px; + margin-left: 6px; + align-items: center; + svg { + fill: transparent; + width: 12px; + height: 12px; + transform: rotate(45deg); + } + } + + &:hover { + .icon svg { + fill: $color-gray-30; + } + } + + } + } + + .filenames { display: flex; flex-direction: column; @@ -117,7 +158,6 @@ } } - .dashboard-fonts-upload { max-width: 1000px; width: 100%; @@ -164,4 +204,31 @@ color: $color-gray-40; } } + + .fonts-placeholder { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-width: 1000px; + width: 100%; + height: 161px; + + border: 1px dashed $color-gray-20; + margin-top: 16px; + + + .icon { + svg { + fill: $color-gray-40; + width: 32px; + height: 32px; + } + } + + .label { + color: $color-gray-40; + font-size: $fs14; + } + } } diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index d67985d15..bb0b12751 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -382,7 +382,7 @@ .modal-left { align-items: center; - background-color: $color-primary; + background-color: $color-pink; border-top-left-radius: 5px; border-bottom-left-radius: 5px; display: flex; @@ -391,6 +391,10 @@ overflow: hidden; padding: $x-big; width: 230px; + + &.welcome { + padding: 0; + } } .modal-right { @@ -498,6 +502,7 @@ color: $color-black; flex: 1; flex-direction: column; + overflow: visible; padding: $x-big 40px; text-align: center; diff --git a/frontend/shadow-cljs.edn b/frontend/shadow-cljs.edn index b668fa2d4..d4b2b0a2e 100644 --- a/frontend/shadow-cljs.edn +++ b/frontend/shadow-cljs.edn @@ -1,6 +1,6 @@ {:http {:port 3448} :nrepl {:port 3447} - :jvm-opts ["-Xmx600m" "-Xms50m" "-XX:+UseSerialGC"] + :jvm-opts ["-Xmx700m" "-Xms100m" "-XX:+UseSerialGC" "-XX:-OmitStackTraceInFastThrow"] :dev-http {8888 "classpath:public"} :source-paths ["src", "vendor", "resources", "../common", "tests", "dev"] diff --git a/frontend/src/app/main/data/dashboard/fonts.cljs b/frontend/src/app/main/data/dashboard/fonts.cljs deleted file mode 100644 index c1b76c6ab..000000000 --- a/frontend/src/app/main/data/dashboard/fonts.cljs +++ /dev/null @@ -1,94 +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.data.dashboard.fonts - (:require - [app.common.exceptions :as ex] - [app.common.data :as d] - [app.common.media :as cm] - [app.common.spec :as us] - [app.common.uuid :as uuid] - [app.main.repo :as rp] - [app.util.time :as dt] - [app.util.timers :as ts] - [app.main.data.messages :as dm] - [app.util.webapi :as wa] - [app.util.object :as obj] - [app.util.transit :as t] - [beicon.core :as rx] - [cljs.spec.alpha :as s] - [cuerdas.core :as str] - [potok.core :as ptk])) - -(defn fetch-fonts - [{:keys [id] :as team}] - (ptk/reify ::fetch-fonts - ptk/WatchEvent - (watch [_ state stream] - (->> (rp/query! :team-font-variants {:team-id id}) - (rx/map (fn [items] - #(assoc % :dashboard-fonts (d/index-by :id items)))))))) - -(defn add-font - [font] - (ptk/reify ::add-font - ptk/UpdateEvent - (update [_ state] - (update state :dashboard-fonts assoc (:id font) font)))) - - -(defn update-font - [{:keys [id font-family] :as font}] - (ptk/reify ::update-font - ptk/UpdateEvent - (update [_ state] - (let [font (assoc font :font-id (str "custom-" (str/slug font-family)))] - (update state :dashboard-fonts assoc id font))) - - ptk/WatchEvent - (watch [_ state stream] - (let [font (get-in state [:dashboard-fonts id])] - (->> (rp/mutation! :update-font-variant font) - (rx/ignore)))))) - -(defn delete-font - [{:keys [id] :as font}] - (ptk/reify ::delete-font - ptk/UpdateEvent - (update [_ state] - (update state :dashboard-fonts dissoc id)) - - ptk/WatchEvent - (watch [_ state stream] - (let [params (select-keys font [:id :team-id])] - (->> (rp/mutation! :delete-font-variant params) - (rx/ignore)))))) - -;; (defn upload-font -;; [{:keys [id] :as font}] -;; (ptk/reify ::upload-font -;; ptk/WatchEvent -;; (watch [_ state stream] -;; (let [{:keys [on-success on-error] -;; :or {on-success identity -;; on-error rx/throw}} (meta params)] -;; (->> (rp/mutation! :create-font-variant font) -;; (rx/tap on-success) -;; (rx/catch on-error)))))) - -;; (defn add-font -;; "Add fonts to the state in a pending to upload state." -;; [font] -;; (ptk/reify ::add-font -;; ptk/UpdateEvent -;; (update [_ state] -;; (let [id (uuid/next) -;; font (-> font -;; (assoc :created-at (dt/now)) -;; (assoc :id id) -;; (assoc :status :draft))] -;; (js/console.log (clj->js font)) -;; (assoc-in state [:dashboard-fonts id] font))))) diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index 641f91044..899285654 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -6,42 +6,68 @@ (ns app.main.data.fonts (:require + ["opentype.js" :as ot] + [app.common.data :as d] + [app.common.spec :as us] [app.common.media :as cm] + [app.common.uuid :as uuid] [app.main.fonts :as fonts] [app.main.repo :as rp] [app.util.i18n :as i18n :refer [tr]] + [app.util.logging :as log] [beicon.core :as rx] [cljs.spec.alpha :as s] + [app.util.webapi :as wa] [cuerdas.core :as str] [potok.core :as ptk])) -(defn prepare-font-variant - [item] - {:id (str (:font-style item) "-" (:font-weight item)) - :name (str (cm/font-weight->name (:font-weight item)) " " - (str/capital (:font-style item))) - :style (:font-style item) - :weight (str (:font-weight item)) - ::fonts/woff1-file-id (:woff1-file-id item) - ::fonts/woff2-file-id (:woff2-file-id item) - ::fonts/ttf-file-id (:ttf-file-id item) - ::fonts/otf-file-id (:otf-file-id item)}) - -(defn prepare-font - [[id [item :as items]]] - {:id id - :name (:font-family item) - :family (:font-family item) - :variants (mapv prepare-font-variant items)}) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; General purpose events & IMPL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn team-fonts-loaded [fonts] - (ptk/reify ::team-fonts-loaded - ptk/EffectEvent - (effect [_ state stream] - (let [fonts (->> (group-by :font-id fonts) - (mapv prepare-font))] - (fonts/register! :custom fonts))))) + (letfn [;; Prepare font to the internal font database format. + (prepare-font [[id [item :as items]]] + {:id id + :name (:font-family item) + :family (:font-family item) + :variants (->> items + (map prepare-font-variant) + (sort-by variant-sort-fn) + (vec))}) + + (variant-sort-fn [item] + [(:weight item) + (if (= "normal" (:style item)) 1 2)]) + + (prepare-font-variant [item] + {:id (str (:font-style item) "-" (:font-weight item)) + :name (str (cm/font-weight->name (:font-weight item)) + (when (not= "normal" (:font-style item)) + (str " " (str/capital (:font-style item))))) + :style (:font-style item) + :weight (str (:font-weight item)) + ::fonts/woff1-file-id (:woff1-file-id item) + ::fonts/woff2-file-id (:woff2-file-id item) + ::fonts/ttf-file-id (:ttf-file-id item) + ::fonts/otf-file-id (:otf-file-id item)}) + + (adapt-font-id [variant] + (update variant :font-id #(str "custom-" %)))] + + (ptk/reify ::team-fonts-loaded + ptk/UpdateEvent + (update [_ state] + (assoc state :dashboard-fonts (d/index-by :id fonts))) + + ptk/EffectEvent + (effect [_ state stream] + (let [fonts (->> fonts + (map adapt-font-id) + (group-by :font-id) + (mapv prepare-font))] + (fonts/register! :custom fonts)))))) (defn load-team-fonts [team-id] @@ -51,7 +77,168 @@ (->> (rp/query :team-font-variants {:team-id team-id}) (rx/map team-fonts-loaded))))) +(defn process-upload + "Given a seq of blobs and the team id, creates a ready-to-use fonts + map with temporal ID's associated to each font entry." + [blobs team-id] + (letfn [(prepare [{:keys [font type name data] :as params}] + (let [family (or (.getEnglishName ^js font "preferredFamily") + (.getEnglishName ^js font "fontFamily")) + variant (or (.getEnglishName ^js font "preferredSubfamily") + (.getEnglishName ^js font "fontSubfamily"))] + {:content {:data (js/Uint8Array. data) + :name name + :type type} + :font-family family + :font-weight (cm/parse-font-weight variant) + :font-style (cm/parse-font-style variant)})) -(defn get-fonts - [backend] - (get @fonts/fonts backend [])) + (join [res {:keys [content] :as font}] + (let [key-fn (juxt :font-family :font-weight :font-style) + existing (d/seek #(= (key-fn font) (key-fn %)) (vals res))] + (if existing + (update res + (:id existing) + (fn [existing] + (-> existing + (update :data assoc (:type content) (:data content)) + (update :names conj (:name content))))) + (let [tmp-id (uuid/next)] + (assoc res tmp-id + (-> font + (assoc :id tmp-id) + (assoc :team-id team-id) + (assoc :names #{(:name content)}) + (assoc :data {(:type content) + (:data content)}) + (dissoc :content))))))) + + (parse-mtype [mtype] + (case mtype + "application/vnd.oasis.opendocument.formula-template" "font/otf" + mtype)) + + (parse-font [{:keys [data] :as params}] + (try + (assoc params :font (ot/parse data)) + (catch :default e + (log/warn :msg (str/fmt "skiping file %s, unsupported format" (:name params))) + nil))) + + (read-blob [blob] + (->> (wa/read-file-as-array-buffer blob) + (rx/map (fn [data] + {:data data + :name (.-name blob) + :type (parse-mtype (.-type blob))}))))] + + (->> (rx/from blobs) + (rx/mapcat read-blob) + (rx/map parse-font) + (rx/filter some?) + (rx/map prepare) + (rx/reduce join {})))) + +(defn- calculate-family-to-id-mapping + [existing] + (reduce #(assoc %1 (:font-family %2) (:font-id %2)) {} (vals existing))) + +(defn merge-and-group-fonts + "Function responsible to merge (and apropriatelly group) incoming + fonts (processed by `process-upload`) into existing fonts + in local state, preserving correct font-id references." + [current-fonts installed-fonts incoming-fonts] + (loop [famdb (-> (merge current-fonts installed-fonts) + (calculate-family-to-id-mapping)) + items (vals incoming-fonts) + result current-fonts] + (if-let [{:keys [id font-family] :as item} (first items)] + (let [font-id (or (get famdb font-family) + (uuid/next)) + font (assoc item :font-id font-id)] + (recur (assoc famdb font-family font-id) + (rest items) + (assoc result id font))) + result))) + +(defn rename-and-regroup + "Function responsible to rename a font in a local state and properly + regroup it to the apropriate `font-id` having in account current + fonts and installed fonts." + [current-fonts id name installed-fonts] + (let [famdb (-> (merge current-fonts installed-fonts) + (calculate-family-to-id-mapping)) + font-id (or (get famdb name) + (uuid/next))] + (update current-fonts id (fn [font] + (-> font + (assoc :name name) + (assoc :font-id font-id)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Dashboard related events +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn add-font + [font] + (ptk/reify ::add-font + ptk/UpdateEvent + (update [_ state] + (update state :dashboard-fonts assoc (:id font) font)))) + +(defn update-font + [{:keys [id name] :as params}] + (us/assert ::us/uuid id) + (us/assert ::us/not-empty-string name) + (ptk/reify ::update-font + ptk/UpdateEvent + (update [_ state] + ;; Update all variants that has the same font-id with the new + ;; name in the local state. + (update state :dashboard-fonts + (fn [fonts] + (d/mapm (fn [_ font] + (cond-> font + (= id (:font-id font)) + (assoc :font-family name))) + fonts)))) + + ptk/WatchEvent + (watch [_ state stream] + (let [team-id (:current-team-id state)] + (->> (rp/mutation! :update-font {:id id :name name :team-id team-id}) + (rx/ignore)))))) + +(defn delete-font + "Delete all variants related to the provided `font-id`." + [font-id] + (us/assert ::us/uuid font-id) + (ptk/reify ::delete-font + ptk/UpdateEvent + (update [_ state] + (update state :dashboard-fonts + (fn [variants] + (d/removem (fn [[id variant]] + (= (:font-id variant) font-id)) variants)))) + ptk/WatchEvent + (watch [_ state stream] + (let [team-id (:current-team-id state)] + (->> (rp/mutation! :delete-font {:id font-id :team-id team-id}) + (rx/ignore)))))) + +(defn delete-font-variant + [id] + (us/assert ::us/uuid id) + (ptk/reify ::delete-font-variants + ptk/UpdateEvent + (update [_ state] + (update state :dashboard-fonts + (fn [variants] + (d/removem (fn [[_ variant]] + (= (:id variant) id)) + variants)))) + ptk/WatchEvent + (watch [_ state stream] + (let [team-id (:current-team-id state)] + (->> (rp/mutation! :delete-font-variant {:id id :team-id team-id}) + (rx/ignore)))))) diff --git a/frontend/src/app/main/fonts.cljs b/frontend/src/app/main/fonts.cljs index 3c4799f5b..396eb329e 100644 --- a/frontend/src/app/main/fonts.cljs +++ b/frontend/src/app/main/fonts.cljs @@ -56,10 +56,6 @@ (vec) (reset! fonts)))) -(defn- remove-fonts - [db backend] - (reduce-kv #(cond-> %1 (= backend (:backend %3)) (dissoc %2)) db db)) - (defn register! [backend fonts] (swap! fontsdb @@ -96,7 +92,6 @@ (unchecked-set node "type" "text/css") node)) - (defn- create-style-element [css] (let [node (.createElement js/document "style")] @@ -166,9 +161,9 @@ url(%(otf-uri)s) format('otf'); }") -(defn- font-id->uri - [font-id] - (str (u/join cf/public-uri "assets/by-id/" font-id))) +(defn- asset-id->uri + [asset-id] + (str (u/join cf/public-uri "assets/by-id/" asset-id))) (defn generate-custom-font-variant-css [family variant] @@ -176,10 +171,10 @@ {:family family :style (:style variant) :weight (:weight variant) - :woff2-uri (font-id->uri (::woff2-file-id variant)) - :woff1-uri (font-id->uri (::woff1-file-id variant)) - :ttf-uri (font-id->uri (::ttf-file-id variant)) - :otf-uri (font-id->uri (::otf-file-id variant))})) + :woff2-uri (asset-id->uri (::woff2-file-id variant)) + :woff1-uri (asset-id->uri (::woff1-file-id variant)) + :ttf-uri (asset-id->uri (::ttf-file-id variant)) + :otf-uri (asset-id->uri (::otf-file-id variant))})) (defn- generate-custom-font-css [{:keys [family variants] :as font}] @@ -190,7 +185,7 @@ (defmethod load-font :custom [{:keys [id family variants ::on-loaded] :as font}] (when (exists? js/window) - (js/console.log "[debug:fonts]: loading google font" id) + (js/console.log "[debug:fonts]: loading custom font" id) (let [css (generate-custom-font-css font)] (add-font-css! css)))) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 422a3734a..601b89ecc 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -145,7 +145,7 @@ :dashboard-team-settings) [:* #_[:div.modal-wrapper - [:& app.main.ui.onboarding/release-notes-modal {:version "1.5"}]] + [:& app.main.ui.onboarding/release-notes-modal {:version "1.6"}]] [:& dashboard {:route route}]] :viewer diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 565d1ce51..241f7c2b2 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -6,11 +6,11 @@ (ns app.main.ui.dashboard.fonts (:require - ["opentype.js" :as ot] + [app.common.data :as d] [app.common.media :as cm] [app.common.uuid :as uuid] [app.main.data.dashboard :as dd] - [app.main.data.dashboard.fonts :as df] + [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.context-menu :refer [context-menu]] @@ -64,56 +64,23 @@ [:h1 (tr "labels.fonts")]] [:nav #_[:ul - [:li {:class (when (= section :fonts) "active")} - [:a {:on-click go-fonts} (tr "labels.custom-fonts")]] - [:li {:class (when (= section :providers) "active")} - [:a {:on-click go-providers} (tr "labels.font-providers")]]]] + [:li {:class (when (= section :fonts) "active")} + [:a {:on-click go-fonts} (tr "labels.custom-fonts")]] + [:li {:class (when (= section :providers) "active")} + [:a {:on-click go-providers} (tr "labels.font-providers")]]]] [:div]])) -(defn- prepare-fonts - [blobs] - (letfn [(prepare [{:keys [font type name data] :as params}] - (let [family (or (.getEnglishName ^js font "preferredFamily") - (.getEnglishName ^js font "fontFamily")) - variant (or (.getEnglishName ^js font "preferredSubfamily") - (.getEnglishName ^js font "fontSubfamily"))] - {:content {:data (js/Uint8Array. data) - :name name - :type type} - :font-id (str "custom-" (str/slug family)) - :font-family family - :font-weight (cm/parse-font-weight variant) - :font-style (cm/parse-font-style variant)})) - - (parse-mtype [mtype] - (case mtype - "application/vnd.oasis.opendocument.formula-template" "font/otf" - mtype)) - - (parse-font [{:keys [data] :as params}] - (try - (assoc params :font (ot/parse data)) - (catch :default e - (log/warn :msg (str/fmt "skiping file %s, unsupported format" (:name params))) - nil))) - - (read-blob [blob] - (->> (wa/read-file-as-array-buffer blob) - (rx/map (fn [data] - {:data data - :name (.-name blob) - :type (parse-mtype (.-type blob))}))))] - - (->> (rx/from blobs) - (rx/mapcat read-blob) - (rx/map parse-font) - (rx/filter some?) - (rx/map prepare)))) +(mf/defc font-variant-display-name + [{:keys [variant]}] + [:* + [:span (cm/font-weight->name (:font-weight variant))] + (when (not= "normal" (:font-style variant)) + [:span " " (str/capital (:font-style variant))])]) (mf/defc fonts-upload - [{:keys [team] :as props}] - (let [fonts (mf/use-state {}) + [{:keys [team installed-fonts] :as props}] + (let [fonts (mf/use-state {}) input-ref (mf/use-ref) uploading (mf/use-state #{}) @@ -126,19 +93,11 @@ on-selected (mf/use-callback - (mf/deps team) + (mf/deps team installed-fonts) (fn [blobs] - (->> (prepare-fonts blobs) - (rx/subs (fn [{:keys [content] :as font}] - (let [key (font-key-fn font)] - (swap! fonts update key - (fn [val] - (-> (or val font) - (assoc :team-id (:id team)) - (update :id #(or % (uuid/next))) - (update :data assoc (:type content) (:data content)) - (update :names (fnil conj #{}) (:name content)) - (dissoc :content)))))) + (->> (df/process-upload blobs (:id team)) + (rx/subs (fn [result] + (swap! fonts df/merge-and-group-fonts installed-fonts result)) (fn [error] (js/console.error "error" error)))))) @@ -146,22 +105,26 @@ (mf/use-callback (mf/deps team) (fn [item] - (let [key (font-key-fn item)] - (swap! uploading conj (:id item)) - (->> (rp/mutation! :create-font-variant item) - (rx/delay-at-least 2000) - (rx/subs (fn [font] - (swap! fonts dissoc key) - (swap! uploading disj (:id item)) - (st/emit! (df/add-font font))) - (fn [error] - (js/console.log "error" error))))))) + (swap! uploading conj (:id item)) + (->> (rp/mutation! :create-font-variant item) + (rx/delay-at-least 2000) + (rx/subs (fn [font] + (swap! fonts dissoc (:id item)) + (swap! uploading disj (:id item)) + (st/emit! (df/add-font font))) + (fn [error] + (js/console.log "error" error)))))) + + on-blur-name + (fn [id event] + (let [name (dom/get-target-val event)] + (swap! fonts df/rename-and-regroup id name installed-fonts))) on-delete (mf/use-callback (mf/deps team) - (fn [item] - (swap! fonts dissoc (font-key-fn item))))] + (fn [{:keys [id] :as item}] + (swap! fonts dissoc id)))] [:div.dashboard-fonts-upload [:div.dashboard-fonts-hero @@ -177,7 +140,7 @@ [:div.btn-primary {:on-click on-click} - [:span "Add custom font"] + [:span (tr "labels.add-custom-font")] [:& file-uploader {:input-id "font-upload" :accept cm/str-font-types :multi true @@ -190,11 +153,11 @@ [:div.font-item.table-row {:key (:id item)} [:div.table-field.family [:input {:type "text" + :on-blur #(on-blur-name (:id item) %) :default-value (:font-family item)}]] - [:div.table-field.variant - [:span (cm/font-weight->name (:font-weight item))] - (when (not= "normal" (:font-style item)) - [:span " " (str/capital (:font-style item))])] + [:div.table-field.variants + [:span.label + [:& font-variant-display-name {:variant item}]]] [:div.table-field.filenames (for [item (:names item)] [:span item])] @@ -210,56 +173,67 @@ [:span.icon.close {:on-click #(on-delete item)} i/close]]]))]])) (mf/defc installed-font - [{:keys [font] :as props}] - (let [open-menu? (mf/use-state false) + [{:keys [font-id variants] :as props}] + (let [font (first variants) + + variants (sort-by (fn [item] + [(:font-weight item) + (if (= "normal" (:font-style item)) 1 2)]) + variants) + + open-menu? (mf/use-state false) edit? (mf/use-state false) state (mf/use-var (:font-family font)) on-change - (mf/use-callback - (mf/deps font) - (fn [event] - (reset! state (dom/get-target-val event)))) + (fn [event] + (reset! state (dom/get-target-val event))) on-save - (mf/use-callback - (mf/deps font) - (fn [event] - (let [font (assoc font :font-family @state)] - (st/emit! (df/update-font font)) - (reset! edit? false)))) + (fn [event] + (let [font-family @state] + (st/emit! (df/update-font + {:id font-id + :name font-family})) + (reset! edit? false))) on-key-down - (mf/use-callback - (mf/deps font) - (fn [event] - (when (kbd/enter? event) - (on-save event)))) + (fn [event] + (when (kbd/enter? event) + (on-save event))) on-cancel - (mf/use-callback - (mf/deps font) - (fn [event] - (reset! edit? false) - (reset! state (:font-family font)))) + (fn [event] + (reset! edit? false) + (reset! state (:font-family font))) - delete-fn - (mf/use-callback - (mf/deps font) - (st/emitf (df/delete-font font))) + delete-font-fn + (fn [] (st/emit! (df/delete-font font-id))) + + delete-variant-fn + (fn [id] (st/emit! (df/delete-font-variant id))) on-delete - (mf/use-callback - (mf/deps font) - (st/emitf (modal/show - {:type :confirm - :title (tr "modals.delete-font.title") - :message (tr "modals.delete-font.message") - :accept-label (tr "labels.delete") - :on-accept delete-fn})))] + (fn [] + (st/emit! (modal/show + {:type :confirm + :title (tr "modals.delete-font.title") + :message (tr "modals.delete-font.message") + :accept-label (tr "labels.delete") + :on-accept (fn [props] + (delete-font-fn))}))) + on-delete-variant + (fn [id] + (st/emit! (modal/show + {:type :confirm + :title (tr "modals.delete-font-variant.title") + :message (tr "modals.delete-font-variant.message") + :accept-label (tr "labels.delete") + :on-accept (fn [props] + (delete-variant-fn id))})))] - [:div.font-item.table-row {:key (:id font)} + [:div.font-item.table-row [:div.table-field.family (if @edit? [:input {:type "text" @@ -268,10 +242,14 @@ :on-change on-change}] [:span (:font-family font)])] - [:div.table-field.variant - [:span (cm/font-weight->name (:font-weight font))] - (when (not= "normal" (:font-style font)) - [:span " " (str/capital (:font-style font))])] + [:div.table-field.variants + (for [item variants] + [:div.variant + [:span.label + [:& font-variant-display-name {:variant item}]] + [:span.icon.close + {:on-click #(on-delete-variant (:id item))} + i/plus]])] [:div] @@ -281,7 +259,7 @@ {:disabled (str/blank? @state) :on-click on-save :class (dom/classnames :btn-disabled (str/blank? @state))} - "Save"] + (tr "labels.save")] [:span.icon.close {:on-click on-cancel} i/close]] [:div.table-field.options @@ -313,41 +291,45 @@ [:h3 (tr "labels.installed-fonts")] [:div.installed-fonts-header [:div.table-field.family (tr "labels.font-family")] - [:div.table-field.variant (tr "labels.font-variant")] + [:div.table-field.variants (tr "labels.font-variants")] [:div] [:div.table-field.search-input [:input {:placeholder (tr "labels.search-font") :default-value "" :on-change on-change }]]] - (for [[font-id fonts] (->> fonts - (filter matches?) - (group-by :font-id))] - [:div.fonts-group - (for [font (sort-by (juxt :font-weight :font-style) fonts)] - [:& installed-font {:key (:id font) :font font}])])])) + (cond + (seq fonts) + (for [[font-id variants] (->> (vals fonts) + (filter matches?) + (group-by :font-id))] + [:& installed-font {:key (str font-id) + :font-id font-id + :variants variants}]) + + (nil? fonts) + [:div.fonts-placeholder + [:div.icon i/loader] + [:div.label (tr "dashboard.loading-fonts")]] + + :else + [:div.fonts-placeholder + [:div.icon i/text] + [:div.label (tr "dashboard.fonts.empty-placeholder")]])])) (mf/defc fonts-page [{:keys [team] :as props}] - (let [fonts-map (mf/deref refs/dashboard-fonts) - fonts (vals fonts-map)] - - (mf/use-effect - (mf/deps team) - (st/emitf (df/fetch-fonts team))) - + (let [fonts (mf/deref refs/dashboard-fonts)] [:* [:& header {:team team :section :fonts}] [:section.dashboard-container.dashboard-fonts - [:& fonts-upload {:team team}] + [:& fonts-upload {:team team :installed-fonts fonts}] + [:& installed-fonts {:team team :fonts fonts}]]])) - (when fonts - [:& installed-fonts {:team team - :fonts fonts}])]])) (mf/defc font-providers-page [{:keys [team] :as props}] [:* [:& header {:team team :section :providers}] [:section.dashboard-container - [:span "hello world font providers"]]]) + [:span "font providers"]]]) diff --git a/frontend/src/app/main/ui/onboarding.cljs b/frontend/src/app/main/ui/onboarding.cljs index a14f22a9f..307915145 100644 --- a/frontend/src/app/main/ui/onboarding.cljs +++ b/frontend/src/app/main/ui/onboarding.cljs @@ -34,7 +34,7 @@ (mf/defc onboarding-start [{:keys [next] :as props}] [:div.modal-container.onboarding - [:div.modal-left + [:div.modal-left.welcome [:img {:src "images/login-on.jpg" :border "0" :alt "Penpot"}]] [:div.modal-right [:div.modal-title @@ -296,7 +296,7 @@ (defmethod render-release-notes "0.0" [params] - (render-release-notes (assoc params :version "1.5"))) + (render-release-notes (assoc params :version "1.6"))) (defmethod render-release-notes "1.4" [{:keys [slide klass next finish navigate version]}] @@ -474,3 +474,101 @@ {: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}]]]]]]))) 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 5329aa494..e0649d7b6 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 @@ -310,7 +310,16 @@ [:div.row-flex [:div.input-select.font-option {:on-click #(reset! open-selector? true)} - (:name font)]] + (cond + (= :multiple font-id) + "--" + + (some? font) + (:name font) + + :else + (tr "dashboard.fonts.deleted-placeholder"))]] + [:div.row-flex (let [size-options [8 9 10 11 12 14 18 24 36 48 72] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 9d50b7b8d..a0cd82b37 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -206,6 +206,12 @@ msgstr "Duplicate %s files" msgid "dashboard.empty-files" msgstr "You still have no files here" +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Font deleted" + +msgid "dashboard.fonts.empty-placeholder" +msgstr "You still have no custom fonts installed." + #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -239,6 +245,9 @@ msgstr "Shared Libraries" msgid "dashboard.loading-files" msgstr "loading your files …" +msgid "dashboard.loading-fonts" +msgstr "loading your fonts …" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.move-to" msgstr "Move to" @@ -790,6 +799,9 @@ msgstr "You are seeing version %s" msgid "labels.accept" msgstr "Accept" +msgid "labels.add-custom-font" +msgstr "Add custom font" + #: 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 "Admin" @@ -891,8 +903,8 @@ msgstr "Font Family" msgid "labels.font-providers" msgstr "Font providers" -msgid "labels.font-variant" -msgstr "Style" +msgid "labels.font-variants" +msgstr "Styles" msgid "labels.fonts" msgstr "Fonts" @@ -1189,6 +1201,14 @@ msgstr "Are you sure you want to delete %s files?" msgid "modals.delete-file-multi-confirm.title" msgstr "Deleting %s files" +msgid "modals.delete-font-variant.message" +msgstr "" +"Are you sure you want to delete this font style? It will not load if is " +"used in a file." + +msgid "modals.delete-font-variant.title" +msgstr "Deleting font style" + msgid "modals.delete-font.message" msgstr "" "Are you sure you want to delete this font? It will not load if is used in a " diff --git a/frontend/translations/es.po b/frontend/translations/es.po index eb9ae497c..7063b5649 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -210,6 +210,12 @@ msgstr "Duplicar %s archivos" msgid "dashboard.empty-files" msgstr "Todavía no hay ningún archivo aquí" +msgid "dashboard.fonts.deleted-placeholder" +msgstr "Fuente eliminada." + +msgid "dashboard.fonts.empty-placeholder" +msgstr "Aun no tienes fuentes personalizadas." + #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -243,6 +249,9 @@ msgstr "Bibliotecas Compartidas" msgid "dashboard.loading-files" msgstr "cargando tus ficheros …" +msgid "dashboard.loading-fonts" +msgstr "cargando tus fuentes …" + #: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs msgid "dashboard.move-to" msgstr "Mover a" @@ -796,6 +805,9 @@ msgstr "Estás viendo la versión %s" msgid "labels.accept" msgstr "Aceptar" +msgid "labels.add-custom-font" +msgstr "Añadir fuentes 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 "Administración" @@ -893,8 +905,8 @@ msgstr "Familia de fuente" msgid "labels.font-providers" msgstr "Proveedores de fuentes" -msgid "labels.font-variant" -msgstr "Estilo" +msgid "labels.font-variants" +msgstr "Estilos" msgid "labels.fonts" msgstr "Fuentes" @@ -1191,10 +1203,18 @@ msgstr "¿Seguro que quieres eliminar %s archivos?" msgid "modals.delete-file-multi-confirm.title" msgstr "Eliminando %s archivos" +msgid "modals.delete-font-variant.message" +msgstr "" +"Estas seguro de querer eliminar esta estilo de fuente? Dejara de cargar si " +"es usada en algun fichero." + +msgid "modals.delete-font-variant.title" +msgstr "Eliminando estilo de fuente" + msgid "modals.delete-font.message" msgstr "" -"Are you sure you want to delete this font? It will not load if is used in a " -"file." +"Estas seguro de querer eliminar esta fuente? Dejara de cargar si es usada " +"en algun fichero." msgid "modals.delete-font.title" msgstr "Eliminando fuente"