0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-25 07:58:49 -05:00

Merge pull request #2443 from penpot/palba-figma-plugin

🎉 Basic changes to use penpot as a library
This commit is contained in:
Andrey Antukh 2022-10-18 09:07:33 +02:00 committed by GitHub
commit a59ca5b781
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 96 additions and 43 deletions

View file

@ -21,6 +21,7 @@
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[clojure.spec.alpha :as spec]
[cuerdas.core :as str]))
(def root-frame uuid/zero)
@ -52,9 +53,18 @@
(when fail-on-spec?
(us/verify ::pcs/change change))
(let [valid? (us/valid? ::pcs/change change)]
(let [valid? (us/valid? ::pcs/change change)
explain (spec/explain-str ::pcs/change change)]
#?(:cljs
(when-not valid? (.warn js/console "Invalid shape" (clj->js change))))
(when-not valid?
(do
(.warn js/console "Invalid shape" (clj->js change))
(.warn js/console explain)))
:clj
(when-not valid?
(do
(prn "Invalid shape" change)
(prn explain))))
(cond-> file
valid?

View file

@ -13,9 +13,10 @@
funcool/tubax {:mvn/version "2021.05.20-0"}
funcool/rumext
{:git/tag "v2.0"
:git/sha "fc617a8"
:git/url "https://github.com/funcool/rumext.git"}
{:git/tag "v2.1"
:git/sha "6343102"
:git/url "https://github.com/funcool/rumext.git"
}
instaparse/instaparse {:mvn/version "1.4.12"}
garden/garden {:git/url "https://github.com/noprompt/garden"

View file

@ -53,7 +53,7 @@
:createFile app.libs.file-builder/create-file-export}}}
:compiler-options
{:output-feature-set :es8
{:output-feature-set :es2020
:output-wrapper false
:warnings {:fn-deprecated false}}

View file

@ -8,6 +8,11 @@
(:require
[app.common.data :as d]
[app.common.file-builder :as fb]
[app.common.uuid :as uuid]
[app.util.dom :as dom]
[app.util.zip :as uz]
[app.worker.export :as e]
[beicon.core :as rx]
[cuerdas.core :as str]))
(defn parse-data [data]
@ -22,6 +27,50 @@
key (-> key d/name str/kebab keyword)]
[key value])) $)))
(defn export-file
[file]
(let [file (assoc file
:name (:name file)
:file-name (:name file)
:is-shared false)
files-stream (->> (rx/of {(:id file) file})
(rx/share))
manifest-stream
(->> files-stream
(rx/map #(e/create-manifest (uuid/next) (:id file) :all %))
(rx/map (fn [a]
(vector "manifest.json" a))))
render-stream
(->> files-stream
(rx/flat-map vals)
(rx/flat-map e/process-pages)
(rx/observe-on :async)
(rx/flat-map e/get-page-data)
(rx/share))
pages-stream
(->> render-stream
(rx/map e/collect-page))]
(rx/merge
(->> render-stream
(rx/map #(hash-map
:type :progress
:file (:id file)
:data (str "Render " (:file-name %) " - " (:name %)))))
(->> (rx/merge
manifest-stream
pages-stream)
(rx/reduce conj [])
(rx/with-latest-from files-stream)
(rx/flat-map (fn [[data _]]
(->> (uz/compress-files data)
(rx/map #(vector file %)))))))))
(deftype File [^:mutable file]
Object
@ -50,6 +99,13 @@
(closeGroup [_]
(set! file (fb/close-group file)))
(addBool [_ data]
(set! file (fb/add-bool file (parse-data data)))
(str (:last-id file)))
(closeBool [_]
(set! file (fb/close-bool file)))
(createRect [_ data]
(set! file (fb/create-rect file (parse-data data)))
(str (:last-id file)))
@ -78,7 +134,15 @@
(set! file (fb/close-svg-raw file)))
(asMap [_]
(clj->js file)))
(clj->js file))
(export [_]
(->> (export-file file)
(rx/subs
(fn [value]
(when (not (contains? value :type))
(let [[file export-blob] value]
(dom/trigger-download (:name file) export-blob))))))))
(defn create-file-export [^string name]
(File. (fb/create-file name)))

View file

@ -7,6 +7,7 @@
(ns app.main.broadcast
"BroadcastChannel API."
(:require
[app.common.exceptions :as ex]
[app.common.transit :as t]
[beicon.core :as rx]
[potok.core :as ptk]))
@ -18,9 +19,12 @@
(def ^:const default-topic "penpot")
;; The main broadcast channel instance, used for emit data
;; If used as a library may be we can't access js/BroadcastChannel.
;; and even if it exists we can receive an exception like:
;; Failed to construct 'BroadcastChannel': Can't create BroadcastChannel in an opaque origin
(defonce default-channel
(when (exists? js/BroadcastChannel)
(js/BroadcastChannel. default-topic)))
(ex/ignoring (js/BroadcastChannel. default-topic))))
(defonce stream
(if (exists? js/BroadcastChannel)

View file

@ -7,9 +7,7 @@
(ns app.main.ui.hooks
"A collection of general purpose react hooks."
(:require
[app.common.data.macros :as dm]
[app.common.pages :as cp]
[app.common.uuid :as uuid]
[app.main.broadcast :as mbc]
[app.main.data.shortcuts :as dsc]
[app.main.refs :as refs]
@ -22,11 +20,6 @@
[goog.functions :as f]
[rumext.v2 :as mf]))
(defn use-id
"Get a stable id value across rerenders."
[]
(mf/use-memo #(dm/str (uuid/next))))
(defn use-rxsub
[ob]
(let [[state reset-state!] (mf/useState #(if (satisfies? IDeref ob) @ob nil))]
@ -253,24 +246,6 @@
(mf/set-ref-val! ref val))
(mf/ref-val ref)))
(defn- ssr?
"Checks if the current environment is run under a SSR context"
[]
(try
(not js/window)
(catch :default _e
;; When exception accessing window we're in ssr
true)))
(defn use-effect-ssr
"Use effect that handles SSR"
[deps effect-fn]
(if (ssr?)
(let [ret (effect-fn)]
(when (fn? ret) (ret)))
(mf/use-effect deps effect-fn)))
(defn with-focus-objects
([objects]
(let [focus (mf/deref refs/workspace-focus-selected)]
@ -298,7 +273,7 @@
localStorage. And it will keep watching events with type equals to
`key` for new values."
[key default]
(let [id (use-id)
(let [id (mf/use-id)
state (mf/use-state (get @storage key default))
stream (mf/with-memo [id]
(->> mbc/stream
@ -311,7 +286,6 @@
(swap! storage assoc key @state))
(use-stream stream (partial reset! state))
state))
(defonce ^:private intersection-subject (rx/subject))

View file

@ -19,7 +19,7 @@
uri-data (mf/use-ref {})
state (mf/use-state 0)]
(hooks/use-effect-ssr
(mf/use-ssr-effect
(mf/deps embed? urls)
(fn []
(let [;; When not active the embedding we return the URI

View file

@ -80,7 +80,7 @@
(obj/set! "height" height))])
(when has-image?
[:image {:href (get embed uri uri)
[:image {:href (or (:data-uri shape) (get embed uri uri))
:preserveAspectRatio "none"
:width width
:height height}])]])])))))

View file

@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.main.ui.context :as muc]
[app.main.ui.hooks :as h]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.export :as ed]
[app.main.ui.shapes.fills :as fills]
@ -56,7 +55,7 @@
disable-shadows? (unchecked-get props "disable-shadows?")
type (:type shape)
render-id (h/use-id)
render-id (mf/use-id)
filter-id (dm/str "filter_" render-id)
styles (-> (obj/create)
(obj/set! "pointerEvents" pointer-events)

View file

@ -9,7 +9,6 @@
[app.common.data :as d]
[app.common.pages.helpers :as cph]
[app.main.fonts :as fonts]
[app.main.ui.hooks :as hooks]
[app.main.ui.shapes.embed :as embed]
[app.util.object :as obj]
[beicon.core :as rx]
@ -32,7 +31,7 @@
(let [fonts-css-ref (mf/use-ref "")
redraw (mf/use-state 0)]
(hooks/use-effect-ssr
(mf/use-ssr-effect
(mf/deps fonts)
(fn []
(let [sub

View file

@ -6,6 +6,7 @@
(ns app.util.storage
(:require
[app.common.exceptions :as ex]
[app.common.transit :as t]
[app.util.globals :as g]
[app.util.timers :as tm]))
@ -38,7 +39,8 @@
{}
(range len)))))
(defonce storage (atom (load (unchecked-get g/global "localStorage"))))
;; Using ex/ignoring because can receive a DOMException like this when importing the code as a library:
;; Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.
(defonce storage (atom (load (ex/ignoring (unchecked-get g/global "localStorage")))))
(add-watch storage :persistence #(persist js/localStorage %3 %4))