0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-13 07:21:40 -05:00

Merge pull request #4793 from penpot/alotor-plugins-2

 Add some missing text properties in plugins
This commit is contained in:
Andrey Antukh 2024-06-25 07:47:34 +02:00 committed by GitHub
commit 8296e72887
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 336 additions and 150 deletions

View file

@ -7,6 +7,7 @@
(ns app.common.types.color
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.schema :as sm]
[app.common.schema.openapi :as-alias oapi]
[app.common.text :as txt]
@ -14,7 +15,8 @@
[app.common.types.color.gradient :as-alias color-gradient]
[app.common.types.color.gradient.stop :as-alias color-gradient-stop]
[app.common.uuid :as uuid]
[clojure.test.check.generators :as tgen]))
[clojure.test.check.generators :as tgen]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMAS
@ -383,3 +385,121 @@
(and (some? (:color c1))
(some? (:color c2))
(= (:color c1) (:color c2)))))
(defn stroke->color-att
[stroke file-id shared-libs]
(let [color-file-id (:stroke-color-ref-file stroke)
color-id (:stroke-color-ref-id stroke)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:stroke-color stroke))) (not (nil? (:stroke-color-gradient stroke))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:id color-id
:file-id color-file-id
:gradient (:stroke-color-gradient stroke)})
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:gradient (:stroke-color-gradient stroke)}))]
(when has-color?
{:attrs attrs
:prop :stroke
:shape-id (:shape-id stroke)
:index (:index stroke)})))
(defn shadow->color-att
[shadow file-id shared-libs]
(let [color-file-id (dm/get-in shadow [:color :file-id])
color-id (dm/get-in shadow [:color :id])
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:id color-id
:file-id (dm/get-in shadow [:color :file-id])
:gradient (dm/get-in shadow [:color :gradient])})
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:gradient (dm/get-in shadow [:color :gradient])}))]
{:attrs attrs
:prop :shadow
:shape-id (:shape-id shadow)
:index (:index shadow)}))
(defn text->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
{:attrs attrs
:prop :content
:shape-id (:shape-id fill)
:index (:index fill)}))
(defn treat-node
[node shape-id]
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))
(defn extract-text-colors
[text file-id shared-libs]
(let [content (txt/node-seq txt/is-text-node? (:content text))
content-filtered (map :fills content)
indexed (mapcat #(treat-node % (:id text)) content-filtered)]
(map #(text->color-att % file-id shared-libs) indexed)))
(defn fill->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:fill-color fill))) (not (nil? (:fill-color-gradient fill))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
(when has-color?
{:attrs attrs
:prop :fill
:shape-id (:shape-id fill)
:index (:index fill)})))
(defn extract-all-colors
[shapes file-id shared-libs]
(reduce
(fn [list shape]
(let [fill-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:fills shape))
stroke-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:strokes shape))
shadow-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:shadow shape))]
(if (= :text (:type shape))
(-> list
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)
(into (extract-text-colors shape file-id shared-libs)))
(-> list
(into (map #(fill->color-att % file-id shared-libs)) fill-obj)
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)))))
[]
shapes))

View file

@ -1281,18 +1281,20 @@
ptk/WatchEvent
(watch [_ state _]
(let [features (features/get-team-enabled-features state)]
(rx/merge
(->> (rp/cmd! :link-file-to-library {:file-id file-id :library-id library-id})
(rx/ignore))
(->> (rp/cmd! :get-file {:id library-id :features features})
(rx/merge-map fpmap/resolve-file)
(rx/map (fn [file]
(fn [state]
(assoc-in state [:workspace-libraries library-id] file)))))
(->> (rp/cmd! :get-file-object-thumbnails {:file-id library-id :tag "component"})
(rx/map (fn [thumbnails]
(fn [state]
(update state :workspace-thumbnails merge thumbnails))))))))))
(rx/concat
(rx/merge
(->> (rp/cmd! :link-file-to-library {:file-id file-id :library-id library-id})
(rx/ignore))
(->> (rp/cmd! :get-file {:id library-id :features features})
(rx/merge-map fpmap/resolve-file)
(rx/map (fn [file]
(fn [state]
(assoc-in state [:workspace-libraries library-id] file)))))
(->> (rp/cmd! :get-file-object-thumbnails {:file-id library-id :tag "component"})
(rx/map (fn [thumbnails]
(fn [state]
(update state :workspace-thumbnails merge thumbnails))))))
(rx/of (ptk/reify ::attach-library-finished)))))))
(defn unlink-file-from-library
[file-id library-id]

View file

@ -9,143 +9,24 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.main.data.workspace.colors :as dc]
[app.main.data.workspace.selection :as dws]
[app.main.store :as st]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
[app.util.i18n :as i18n :refer [tr]]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(defn fill->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:fill-color fill))) (not (nil? (:fill-color-gradient fill))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
(when has-color?
{:attrs attrs
:prop :fill
:shape-id (:shape-id fill)
:index (:index fill)})))
(defn stroke->color-att
[stroke file-id shared-libs]
(let [color-file-id (:stroke-color-ref-file stroke)
color-id (:stroke-color-ref-id stroke)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:stroke-color stroke))) (not (nil? (:stroke-color-gradient stroke))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:id color-id
:file-id color-file-id
:gradient (:stroke-color-gradient stroke)})
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:gradient (:stroke-color-gradient stroke)}))]
(when has-color?
{:attrs attrs
:prop :stroke
:shape-id (:shape-id stroke)
:index (:index stroke)})))
(defn shadow->color-att
[shadow file-id shared-libs]
(let [color-file-id (dm/get-in shadow [:color :file-id])
color-id (dm/get-in shadow [:color :id])
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:id color-id
:file-id (dm/get-in shadow [:color :file-id])
:gradient (dm/get-in shadow [:color :gradient])})
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:gradient (dm/get-in shadow [:color :gradient])}))]
{:attrs attrs
:prop :shadow
:shape-id (:shape-id shadow)
:index (:index shadow)}))
(defn text->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
{:attrs attrs
:prop :content
:shape-id (:shape-id fill)
:index (:index fill)}))
(defn treat-node
[node shape-id]
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))
(defn extract-text-colors
[text file-id shared-libs]
(let [content (txt/node-seq txt/is-text-node? (:content text))
content-filtered (map :fills content)
indexed (mapcat #(treat-node % (:id text)) content-filtered)]
(map #(text->color-att % file-id shared-libs) indexed)))
(defn- extract-all-colors
[shapes file-id shared-libs]
(reduce
(fn [list shape]
(let [fill-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:fills shape))
stroke-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:strokes shape))
shadow-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:shadow shape))]
(if (= :text (:type shape))
(-> list
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)
(into (extract-text-colors shape file-id shared-libs)))
(-> list
(into (map #(fill->color-att % file-id shared-libs)) fill-obj)
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)))))
[]
shapes))
(defn- prepare-colors
[shapes file-id shared-libs]
(let [data (into [] (remove nil? (extract-all-colors shapes file-id shared-libs)))
(let [data (into [] (remove nil? (ctc/extract-all-colors shapes file-id shared-libs)))
grouped-colors (group-by :attrs data)
all-colors (distinct (mapv :attrs data))
tmp (group-by #(some? (:id %)) all-colors)
library-colors (get tmp true)
colors (get tmp false)]
{:grouped-colors grouped-colors
:all-colors all-colors
:colors colors

View file

@ -7,11 +7,14 @@
(ns app.plugins.api
"RPC for plugins runtime."
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as cb]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.record :as cr]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.main.data.changes :as ch]
@ -28,6 +31,7 @@
[app.plugins.user :as user]
[app.plugins.utils :as u]
[app.plugins.viewport :as viewport]
[app.util.code-gen :as cg]
[app.util.object :as obj]
[beicon.v2.core :as rx]
[promesa.core :as p]))
@ -83,6 +87,36 @@
(let [selection (get-in @st/state [:workspace-local :selected])]
(apply array (sequence (map (partial shape/shape-proxy $plugin)) selection))))
(getColors
[_ shapes]
(let [objects (u/locate-objects)
shapes (->> shapes
(map #(obj/get % "$id"))
(mapcat #(cfh/get-children-with-self objects %)))
file-id (:current-file-id @st/state)
shared-libs (:workspace-libraries @st/state)
colors
(apply
array
(->> (ctc/extract-all-colors shapes file-id shared-libs)
(group-by :attrs)
(map (fn [[color attrs]]
(let [shapes-info (apply array (map (fn [{:keys [prop shape-id index]}]
#js {:property (d/name prop)
:index index
:shapeId (str shape-id)}) attrs))
color (u/to-js color)]
(obj/set! color "shapeInfo" shapes-info)
color)))))]
colors))
(changeColor
[_ _shapes _old-color _new-color]
;; TODO
)
(getRoot
[_]
(shape/shape-proxy $plugin uuid/zero))
@ -244,7 +278,54 @@
(let [ids (into #{} (map #(obj/get % "$id")) shapes)
id-ret (atom nil)]
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret}))
(shape/shape-proxy $plugin @id-ret))))))
(shape/shape-proxy $plugin @id-ret)))))
(generateMarkup
[_ shapes options]
(let [type (d/nilv (obj/get options "type") "html")]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :generateMarkup-shapes shapes)
(and (some? type) (not (contains? #{"html" "svg"} type)))
(u/display-not-valid :generateMarkup-type type)
:else
(let [objects (u/locate-objects)
shapes (into [] (map u/proxy->shape) shapes)]
(cg/generate-markup-code objects type shapes)))))
(generateStyle
[_ shapes options]
(let [type (d/nilv (obj/get options "type") "css")
prelude? (d/nilv (obj/get options "withPrelude") false)
children? (d/nilv (obj/get options "includeChildren") true)]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :generateStyle-shapes shapes)
(and (some? type) (not (contains? #{"css"} type)))
(u/display-not-valid :generateStyle-type type)
(and (some? prelude?) (not (boolean? prelude?)))
(u/display-not-valid :generateStyle-withPrelude prelude?)
(and (some? children?) (not (boolean? children?)))
(u/display-not-valid :generateStyle-includeChildren children?)
:else
(let [objects (u/locate-objects)
shapes
(->> (into #{} (map u/proxy->shape) shapes)
(cfh/clean-loops objects))
shapes-with-children
(if children?
(->> shapes
(mapcat #(cfh/get-children-with-self objects (:id %))))
shapes)]
(cg/generate-style-code
objects type shapes shapes-with-children {:with-prelude? prelude?}))))))
(defn create-context
[plugin-id]

View file

@ -20,10 +20,14 @@
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.texts :as dwt]
[app.main.repo :as rp]
[app.main.store :as st]
[app.plugins.shape :as shape]
[app.plugins.utils :as u]
[app.util.object :as obj]))
[app.util.object :as obj]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]
[promesa.core :as p]))
(declare lib-color-proxy)
(declare lib-typography-proxy)
@ -744,7 +748,7 @@
(cr/add-properties!
(Library. plugin-id file-id)
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
{:name "$file" :enumerable false :get (constantly file-id)}
{:name "$id" :enumerable false :get (constantly file-id)}
{:name "id"
:get #(-> % u/proxy->file :id str)}
@ -780,10 +784,43 @@
(deftype PenpotLibrarySubcontext [$plugin]
Object
(find
[_ _name])
(availableLibraries
[_]
(let [team-id (:current-team-id @st/state)]
(p/create
(fn [resolve reject]
(let [current-libs (into #{} (map first) (get @st/state :workspace-libraries))]
(->> (rp/cmd! :get-team-shared-files {:team-id team-id})
(rx/map (fn [result]
(->> result
(filter #(not (contains? current-libs (:id %))))
(map
(fn [{:keys [id name library-summary]}]
#js {:id (dm/str id)
:name name
:numColors (-> library-summary :colors :count)
:numComponents (-> library-summary :components :count)
:numTypographies (-> library-summary :typographies :count)}))
(apply array))))
(rx/subs! resolve reject)))))))
(find [_]))
(connectLibrary
[_ library-id]
(p/create
(fn [resolve reject]
(cond
(not (string? library-id))
(do (u/display-not-valid :connectLibrary library-id)
(reject nil))
:else
(let [file-id (:current-file-id @st/state)
library-id (uuid/uuid library-id)]
(->> st/stream
(rx/filter (ptk/type? ::dwl/attach-library-finished))
(rx/take 1)
(rx/subs! #(resolve (library-proxy $plugin library-id)) reject))
(st/emit! (dwl/link-file-to-library file-id library-id))))))))
(defn library-subcontext
[plugin-id]

View file

@ -229,6 +229,19 @@
:else
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
{:name "align"
:get #(let [range-data
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-align) mixed-value))
:set
(fn [_ value]
(cond
(not (string? value))
(u/display-not-valid :text-align value)
:else
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
{:name "fills"
:get #(let [range-data
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
@ -1253,7 +1266,55 @@
(u/display-not-valid :textTransform value)
:else
(st/emit! (dwt/update-attrs id {:text-transform value})))))}))
(st/emit! (dwt/update-attrs id {:text-transform value})))))}
{:name "textDecoration"
:get #(-> % u/proxy->shape text-props :text-decoration)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(cond
(not (string? value))
(u/display-not-valid :textDecoration value)
:else
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
{:name "direction"
:get #(-> % u/proxy->shape text-props :text-direction)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(cond
(not (string? value))
(u/display-not-valid :textDecoration value)
:else
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
{:name "align"
:get #(-> % u/proxy->shape text-props :text-align)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(cond
(not (string? value))
(u/display-not-valid :align value)
:else
(st/emit! (dwt/update-attrs id {:text-align value})))))}
{:name "verticalAlign"
:get #(-> % u/proxy->shape text-props :vertical-align)
:set
(fn [self value]
(let [id (obj/get self "$id")]
(cond
(not (string? value))
(u/display-not-valid :verticalAlign value)
:else
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}))
(cond-> (or (cfh/path-shape? data) (cfh/bool-shape? data))
(crc/add-properties!

View file

@ -32,8 +32,10 @@
(dm/get-in (locate-file file-id) [:data :pages-index id]))
(defn locate-objects
[file-id page-id]
(:objects (locate-page file-id page-id)))
([]
(locate-objects (:current-file-id @st/state) (:current-page-id @st/state)))
([file-id page-id]
(:objects (locate-page file-id page-id))))
(defn locate-shape
[file-id page-id id]

View file

@ -19,8 +19,10 @@
(generate-markup objects shapes)))
(defn generate-style-code
[objects type root-shapes all-shapes]
(let [generate-style
(case type
"css" css/generate-style)]
(generate-style objects root-shapes all-shapes)))
([objects type root-shapes all-shapes]
(generate-style-code objects type root-shapes all-shapes nil))
([objects type root-shapes all-shapes options]
(let [generate-style
(case type
"css" css/generate-style)]
(generate-style objects root-shapes all-shapes options))))

View file

@ -300,10 +300,10 @@ body {
(defn generate-style
([objects root-shapes all-shapes]
(generate-style objects root-shapes all-shapes nil))
([objects root-shapes all-shapes options]
([objects root-shapes all-shapes {:keys [with-prelude?] :or {with-prelude? true} :as options}]
(let [options (assoc options :root-shapes (into #{} (map :id) root-shapes))]
(dm/str
prelude
(if with-prelude? prelude "")
(->> all-shapes
(keep #(get-shape-css-selector % objects options))
(str/join "\n\n"))))))