mirror of
https://github.com/penpot/penpot.git
synced 2025-01-02 04:40:09 -05:00
🎉 Add svg export.
This commit is contained in:
parent
17bea924b2
commit
17813e5090
18 changed files with 950 additions and 226 deletions
|
@ -15,10 +15,12 @@
|
|||
"puppeteer": "^4.0.1",
|
||||
"puppeteer-cluster": "^0.21.0",
|
||||
"raw-body": "^2.4.1",
|
||||
"svgo": "^1.3.2",
|
||||
"xml-js": "^1.6.11",
|
||||
"xregexp": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"shadow-cljs": "^2.10.13",
|
||||
"shadow-cljs": "^2.10.17",
|
||||
"source-map-support": "^0.5.19"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
:or {user-agent USER-AGENT
|
||||
scale 1}}]
|
||||
(let [[width height] viewport]
|
||||
(.emulate page #js {:viewport #js {:width width
|
||||
:height height
|
||||
:deviceScaleFactor scale}
|
||||
:userAgent user-agent})))
|
||||
(.emulate ^js page #js {:viewport #js {:width width
|
||||
:height height
|
||||
:deviceScaleFactor scale}
|
||||
:userAgent user-agent})))
|
||||
|
||||
(defn navigate!
|
||||
([page url] (navigate! page url nil))
|
||||
|
@ -36,10 +36,12 @@
|
|||
|
||||
(defn screenshot
|
||||
([frame] (screenshot frame nil))
|
||||
([frame {:keys [full-page? omit-background?]
|
||||
([frame {:keys [full-page? omit-background? type]
|
||||
:or {full-page? false
|
||||
type "png"
|
||||
omit-background? false}}]
|
||||
(.screenshot ^js frame #js {:fullPage full-page?
|
||||
:type (name type)
|
||||
:omitBackground omit-background?})))
|
||||
|
||||
(defn eval!
|
||||
|
@ -50,6 +52,10 @@
|
|||
[frame selector]
|
||||
(.$ ^js frame selector))
|
||||
|
||||
(defn select-all
|
||||
[frame selector]
|
||||
(.$$ ^js frame selector))
|
||||
|
||||
(defn set-cookie!
|
||||
[page {:keys [key value domain]}]
|
||||
(.setCookie ^js page #js {:name key
|
||||
|
|
|
@ -1,139 +1,20 @@
|
|||
(ns app.http
|
||||
(:require
|
||||
[promesa.core :as p]
|
||||
[app.http.export :refer [export-handler]]
|
||||
[app.http.impl :as impl]
|
||||
[lambdaisland.glogi :as log]
|
||||
[app.browser :as bwr]
|
||||
[app.http.bitmap-export :refer [bitmap-export-handler]]
|
||||
[app.util.transit :as t]
|
||||
[reitit.core :as r]
|
||||
[cuerdas.core :as str]
|
||||
["koa" :as koa]
|
||||
["http" :as http]
|
||||
["inflation" :as inflate]
|
||||
["raw-body" :as raw-body])
|
||||
(:import
|
||||
goog.Uri))
|
||||
|
||||
(defn query-params
|
||||
"Given goog.Uri, read query parameters into Clojure map."
|
||||
[^goog.Uri uri]
|
||||
(let [q (.getQueryData uri)]
|
||||
(->> q
|
||||
(.getKeys)
|
||||
(map (juxt keyword #(.get q %)))
|
||||
(into {}))))
|
||||
|
||||
(defn- match
|
||||
[router ctx]
|
||||
(let [uri (.parse Uri (unchecked-get ctx "originalUrl"))]
|
||||
(when-let [match (r/match-by-path router (.getPath uri))]
|
||||
(assoc match :query-params (query-params uri)))))
|
||||
|
||||
(defn- handle-error
|
||||
[error request]
|
||||
(let [{:keys [type message code] :as data} (ex-data error)]
|
||||
(cond
|
||||
(= :validation type)
|
||||
(let [header (get-in request [:headers "accept"])]
|
||||
(if (and (str/starts-with? header "text/html")
|
||||
(= :spec-validation (:code data)))
|
||||
{:status 400
|
||||
:headers {"content-type" "text/html"}
|
||||
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}
|
||||
{:status 400
|
||||
:headers {"x-metadata" (t/encode data)}
|
||||
:body ""}))
|
||||
|
||||
:else
|
||||
(do
|
||||
(log/error :msg "Unexpected error"
|
||||
:error error)
|
||||
{:status 500
|
||||
:headers {"x-metadata" (t/encode {:type :unexpected
|
||||
:message (ex-message error)})}
|
||||
:body ""}))))
|
||||
|
||||
|
||||
(defn- handle-response
|
||||
[ctx {:keys [body headers status] :or {headers {} status 200}}]
|
||||
(run! (fn [[k v]] (.set ^js ctx k v)) headers)
|
||||
(set! (.-body ^js ctx) body)
|
||||
(set! (.-status ^js ctx) status)
|
||||
nil)
|
||||
|
||||
(defn- parse-headers
|
||||
[ctx]
|
||||
(let [orig (unchecked-get ctx "headers")]
|
||||
(persistent!
|
||||
(reduce #(assoc! %1 %2 (unchecked-get orig %2))
|
||||
(transient {})
|
||||
(js/Object.keys orig)))))
|
||||
|
||||
(def parse-body?
|
||||
#{"POST" "PUT" "DELETE"})
|
||||
|
||||
(defn- parse-body
|
||||
[ctx]
|
||||
(let [headers (unchecked-get ctx "headers")
|
||||
ctype (unchecked-get headers "content-type")]
|
||||
(when (parse-body? (.-method ^js ctx))
|
||||
(-> (inflate (.-req ^js ctx))
|
||||
(raw-body #js {:limit "5mb" :encoding "utf8"})
|
||||
(p/then (fn [data]
|
||||
(cond-> data
|
||||
(= ctype "application/transit+json")
|
||||
(t/decode))))))))
|
||||
|
||||
(defn- wrap-handler
|
||||
[f extra]
|
||||
(fn [ctx]
|
||||
(p/let [cookies (unchecked-get ctx "cookies")
|
||||
headers (parse-headers ctx)
|
||||
body (parse-body ctx)
|
||||
request (assoc extra
|
||||
:method (str/lower (unchecked-get ctx "method"))
|
||||
:body body
|
||||
:ctx ctx
|
||||
:headers headers
|
||||
:cookies cookies)]
|
||||
(-> (p/do! (f request))
|
||||
(p/then (fn [rsp]
|
||||
(when (map? rsp)
|
||||
(handle-response ctx rsp))))
|
||||
(p/catch (fn [err]
|
||||
(->> (handle-error err request)
|
||||
(handle-response ctx))))))))
|
||||
[promesa.core :as p]
|
||||
[reitit.core :as r]))
|
||||
|
||||
(def routes
|
||||
[["/export"
|
||||
["/bitmap" {:handler bitmap-export-handler}]]])
|
||||
|
||||
(defn- router-handler
|
||||
[router]
|
||||
(fn [{:keys [ctx body] :as request}]
|
||||
(let [route (match router ctx)
|
||||
params (merge {}
|
||||
(:query-params route)
|
||||
(:path-params route)
|
||||
(when (map? body) body))
|
||||
request (assoc request
|
||||
:route route
|
||||
:params params)
|
||||
|
||||
handler (get-in route [:data :handler])]
|
||||
(if (and route handler)
|
||||
(handler request)
|
||||
{:status 404
|
||||
:body "Not found"}))))
|
||||
[["/export" {:handler export-handler}]])
|
||||
|
||||
(defn start!
|
||||
[extra]
|
||||
(log/info :msg "starting http server" :port 6061)
|
||||
(let [router (r/router routes)
|
||||
instance (doto (new koa)
|
||||
(.use (-> (router-handler router)
|
||||
(wrap-handler extra))))
|
||||
server (.createServer http (.callback instance))]
|
||||
(let [router (r/router routes)
|
||||
handler (impl/handler router extra)
|
||||
server (impl/server handler)]
|
||||
(.listen server 6061)
|
||||
(p/resolved server)))
|
||||
|
||||
|
@ -143,4 +24,3 @@
|
|||
(.close server (fn []
|
||||
(log/info :msg "shutdown http server")
|
||||
(resolve))))))
|
||||
|
||||
|
|
|
@ -1,47 +1,14 @@
|
|||
(ns app.http.bitmap-export
|
||||
(ns app.http.export
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[app.browser :as bwr]
|
||||
[app.config :as cfg]
|
||||
[app.http.export-bitmap :as bitmap]
|
||||
[app.http.export-svg :as svg]
|
||||
[app.zipfile :as zip]
|
||||
[lambdaisland.glogi :as log]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[uxbox.common.exceptions :as exc :include-macros true]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.spec :as us])
|
||||
(:import
|
||||
goog.Uri))
|
||||
|
||||
(defn- screenshot-object
|
||||
[browser {:keys [page-id object-id token scale suffix type]}]
|
||||
(letfn [(handle [page]
|
||||
(let [path (str "/render-object/" page-id "/" object-id)
|
||||
uri (doto (Uri. (:public-uri cfg/config))
|
||||
(.setPath "/")
|
||||
(.setFragment path))
|
||||
cookie {:domain (str (.getDomain uri)
|
||||
":"
|
||||
(.getPort uri))
|
||||
:key "auth-token"
|
||||
:value token}]
|
||||
(log/info :uri (.toString uri))
|
||||
(screenshot page (.toString uri) cookie)))
|
||||
|
||||
(screenshot [page uri cookie]
|
||||
(p/do!
|
||||
(bwr/emulate! page {:viewport [1920 1080]
|
||||
:scale scale})
|
||||
(bwr/set-cookie! page cookie)
|
||||
(bwr/navigate! page uri)
|
||||
(bwr/eval! page (js* "() => document.body.style.background = 'transparent'"))
|
||||
(p/let [dom (bwr/select page "#screenshot")]
|
||||
(case type
|
||||
:png (bwr/screenshot dom {:omit-background? true :type type})
|
||||
:jpeg (bwr/screenshot dom {:omit-background? false :type type})))))]
|
||||
|
||||
(bwr/exec! browser handle)))
|
||||
[uxbox.common.spec :as us]))
|
||||
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::page-id ::us/uuid)
|
||||
|
@ -49,25 +16,23 @@
|
|||
(s/def ::scale ::us/number)
|
||||
(s/def ::suffix ::us/string)
|
||||
(s/def ::type ::us/keyword)
|
||||
|
||||
(s/def ::suffix string?)
|
||||
(s/def ::scale number?)
|
||||
(s/def ::export
|
||||
(s/keys :req-un [::type ::suffix ::scale]))
|
||||
|
||||
(s/def ::export (s/keys :req-un [::type ::suffix ::scale]))
|
||||
(s/def ::exports (s/coll-of ::export :kind vector?))
|
||||
|
||||
(s/def ::bitmap-handler-params
|
||||
(s/def ::handler-params
|
||||
(s/keys :req-un [::page-id ::object-id ::name ::exports]))
|
||||
|
||||
(declare handle-single-export)
|
||||
(declare handle-multiple-export)
|
||||
(declare perform-bitmap-export)
|
||||
(declare perform-export)
|
||||
(declare attach-filename)
|
||||
|
||||
(defn bitmap-export-handler
|
||||
(defn export-handler
|
||||
[{:keys [params browser cookies] :as request}]
|
||||
(let [{:keys [exports page-id object-id name]} (us/conform ::bitmap-handler-params params)
|
||||
(let [{:keys [exports page-id object-id name]} (us/conform ::handler-params params)
|
||||
token (.get ^js cookies "auth-token")]
|
||||
(case (count exports)
|
||||
0 (exc/raise :type :validation :code :missing-exports)
|
||||
|
@ -87,20 +52,19 @@
|
|||
:page-id page-id
|
||||
:object-id object-id)) exports)))))
|
||||
|
||||
|
||||
(defn handle-single-export
|
||||
(defn- handle-single-export
|
||||
[{:keys [browser]} params]
|
||||
(p/let [result (perform-bitmap-export browser params)]
|
||||
(p/let [result (perform-export browser params)]
|
||||
{:status 200
|
||||
:body (:content result)
|
||||
:headers {"content-type" (:mime-type result)
|
||||
"content-length" (:length result)}}))
|
||||
|
||||
(defn handle-multiple-export
|
||||
(defn- handle-multiple-export
|
||||
[{:keys [browser]} exports]
|
||||
(let [proms (->> exports
|
||||
(attach-filename)
|
||||
(map (partial perform-bitmap-export browser)))]
|
||||
(map (partial perform-export browser)))]
|
||||
(-> (p/all proms)
|
||||
(p/then (fn [results]
|
||||
(reduce #(zip/add! %1 (:filename %2) (:content %2)) (zip/create) results)))
|
||||
|
@ -109,18 +73,24 @@
|
|||
:headers {"content-type" "application/zip"}
|
||||
:body (.generateNodeStream ^js fzip)})))))
|
||||
|
||||
(defn- perform-export
|
||||
[browser params]
|
||||
(case (:type params)
|
||||
:png (bitmap/export browser params)
|
||||
:jpeg (bitmap/export browser params)
|
||||
:svg (svg/export browser params)))
|
||||
|
||||
(defn- find-filename-candidate
|
||||
[params used]
|
||||
(loop [index 0]
|
||||
(let [candidate (str (str/slug (:name params))
|
||||
(if (not (str/blank? (:suffix params "")))
|
||||
(:suffix params "")
|
||||
(when (pos? index)
|
||||
(str "-" (inc index))))
|
||||
(str/trim (str/blank? (:suffix params "")))
|
||||
(when (pos? index)
|
||||
(str "-" (inc index)))
|
||||
(case (:type params)
|
||||
:png ".png"
|
||||
:png ".png"
|
||||
:jpeg ".jpg"
|
||||
:svg ".svg"))]
|
||||
:svg ".svg"))]
|
||||
(if (contains? used candidate)
|
||||
(recur (inc index))
|
||||
candidate))))
|
||||
|
@ -138,15 +108,3 @@
|
|||
(recur (next exports)
|
||||
(conj used candidate)
|
||||
(conj result export))))))
|
||||
|
||||
(defn- perform-bitmap-export
|
||||
[browser params]
|
||||
(p/let [content (screenshot-object browser params)]
|
||||
{:content content
|
||||
:filename (or (:filename params)
|
||||
(find-filename-candidate params #{}))
|
||||
:length (alength content)
|
||||
:mime-type (case (:type params)
|
||||
:png "image/png"
|
||||
:jpeg "image/jpeg")}))
|
||||
|
73
exporter/src/app/http/export_bitmap.cljs
Normal file
73
exporter/src/app/http/export_bitmap.cljs
Normal file
|
@ -0,0 +1,73 @@
|
|||
(ns app.http.export-bitmap
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[app.browser :as bwr]
|
||||
[app.config :as cfg]
|
||||
[lambdaisland.glogi :as log]
|
||||
[cljs.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.common.exceptions :as exc :include-macros true]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.spec :as us])
|
||||
(:import
|
||||
goog.Uri))
|
||||
|
||||
(defn- screenshot-object
|
||||
[browser {:keys [page-id object-id token scale suffix type]}]
|
||||
(letfn [(handle [page]
|
||||
(let [path (str "/render-object/" page-id "/" object-id)
|
||||
uri (doto (Uri. (:public-uri cfg/config))
|
||||
(.setPath "/")
|
||||
(.setFragment path))
|
||||
cookie {:domain (str (.getDomain uri)
|
||||
":"
|
||||
(.getPort uri))
|
||||
:key "auth-token"
|
||||
:value token}]
|
||||
(log/info :uri (.toString uri))
|
||||
(screenshot page (.toString uri) cookie)))
|
||||
|
||||
(screenshot [page uri cookie]
|
||||
(p/do!
|
||||
(bwr/emulate! page {:viewport [1920 1080]
|
||||
:scale scale})
|
||||
(bwr/set-cookie! page cookie)
|
||||
(bwr/navigate! page uri)
|
||||
(bwr/eval! page (js* "() => document.body.style.background = 'transparent'"))
|
||||
(p/let [dom (bwr/select page "#screenshot")]
|
||||
(case type
|
||||
:png (bwr/screenshot dom {:omit-background? true :type type})
|
||||
:jpeg (bwr/screenshot dom {:omit-background? false :type type})))))]
|
||||
|
||||
(bwr/exec! browser handle)))
|
||||
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::suffix ::us/string)
|
||||
(s/def ::type #{:jpeg :png})
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::object-id ::us/uuid)
|
||||
(s/def ::scale ::us/number)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::filename ::us/string)
|
||||
|
||||
(s/def ::export-params
|
||||
(s/keys :req-un [::name ::suffix ::type ::object-id ::page-id ::scale ::token]
|
||||
:opt-un [::filename]))
|
||||
|
||||
(defn export
|
||||
[browser params]
|
||||
(us/assert ::export-params params)
|
||||
(p/let [content (screenshot-object browser params)]
|
||||
{:content content
|
||||
:filename (or (:filename params)
|
||||
(str (str/slug (:name params))
|
||||
(str/trim (:suffix params ""))
|
||||
(case (:type params)
|
||||
:png ".png"
|
||||
:jpeg ".jpg")))
|
||||
:length (alength content)
|
||||
:mime-type (case (:type params)
|
||||
:png "image/png"
|
||||
:jpeg "image/jpeg")}))
|
||||
|
271
exporter/src/app/http/export_svg.cljs
Normal file
271
exporter/src/app/http/export_svg.cljs
Normal file
|
@ -0,0 +1,271 @@
|
|||
(ns app.http.export-svg
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[clojure.walk :as walk]
|
||||
[app.browser :as bwr]
|
||||
[app.config :as cfg]
|
||||
[lambdaisland.glogi :as log]
|
||||
[cljs.spec.alpha :as s]
|
||||
[promesa.core :as p]
|
||||
[uxbox.common.exceptions :as exc :include-macros true]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.spec :as us]
|
||||
["xml-js" :as xml]
|
||||
["child_process" :as chp]
|
||||
["os" :as os]
|
||||
["path" :as path]
|
||||
["fs" :as fs])
|
||||
(:import
|
||||
goog.Uri))
|
||||
|
||||
(def default-svgo-plugins
|
||||
#js [#js {:convertStyleToAttrs false}])
|
||||
|
||||
(defn- create-tmpdir!
|
||||
[prefix]
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(fs/mkdtemp (path/join (os/tmpdir) prefix)
|
||||
(fn [err dir]
|
||||
(if err
|
||||
(reject err)
|
||||
(resolve dir)))))))
|
||||
|
||||
(defn- write-file!
|
||||
[fpath content]
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(fs/writeFile fpath content (fn [err]
|
||||
(if err
|
||||
(reject err)
|
||||
(resolve nil)))))))
|
||||
(defn- read-file
|
||||
[fpath]
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(fs/readFile fpath (fn [err content]
|
||||
(if err
|
||||
(reject err)
|
||||
(resolve content)))))))
|
||||
|
||||
(defn- run-cmd!
|
||||
[cmd]
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(log/info :fn :run-cmd :cmd cmd)
|
||||
(chp/exec cmd #js {:encoding "buffer"}
|
||||
(fn [error stdout stderr]
|
||||
;; (log/info :fn :run-cmd :stdout stdout)
|
||||
(if error
|
||||
(reject error)
|
||||
(resolve stdout)))))))
|
||||
|
||||
(defn- rmdir!
|
||||
[path]
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(fs/rmdir path #js {:recursive true}
|
||||
(fn [err]
|
||||
(if err
|
||||
(reject err)
|
||||
(resolve nil)))))))
|
||||
|
||||
(defn- parse-xml
|
||||
[data]
|
||||
(js->clj (xml/xml2js data)))
|
||||
|
||||
(defn- encode-xml
|
||||
[data]
|
||||
(xml/js2xml (clj->js data)))
|
||||
|
||||
(defn- render-object
|
||||
[browser {:keys [page-id object-id token scale suffix type]}]
|
||||
(letfn [(render-in-page [page {:keys [uri cookie] :as rctx}]
|
||||
(p/do!
|
||||
(bwr/emulate! page {:viewport [1920 1080]
|
||||
:scale 2})
|
||||
(bwr/set-cookie! page cookie)
|
||||
(bwr/navigate! page uri)
|
||||
(bwr/eval! page (js* "() => document.body.style.background = 'transparent'"))
|
||||
page))
|
||||
|
||||
(convert-to-ppm [pngpath]
|
||||
(log/info :fn :convert-to-ppm)
|
||||
(let [basepath (path/dirname pngpath)
|
||||
ppmpath (path/join basepath "origin.ppm")]
|
||||
(-> (run-cmd! (str "convert " pngpath " " ppmpath))
|
||||
(p/then (constantly ppmpath)))))
|
||||
|
||||
(trace-color-mask [pbmpath]
|
||||
(log/info :fn :trace-color-mask :pbmpath pbmpath)
|
||||
(let [basepath (path/dirname pbmpath)
|
||||
basename (path/basename pbmpath ".pbm")
|
||||
svgpath (path/join basepath (str basename ".svg"))]
|
||||
(-> (run-cmd! (str "potrace --flat -b svg " pbmpath " -o " svgpath))
|
||||
(p/then (constantly svgpath)))))
|
||||
|
||||
(generate-color-mask [ppmpath color]
|
||||
(log/info :fn :generate-color-mask :ppmpath ppmpath :color color)
|
||||
(let [basepath (path/dirname ppmpath)
|
||||
pbmpath (path/join basepath (str "mask-" (subs color 1) ".pbm"))]
|
||||
(-> (run-cmd! (str/format "ppmcolormask \"%s\" %s" color ppmpath))
|
||||
(p/then (fn [stdout]
|
||||
(-> (write-file! pbmpath stdout)
|
||||
(p/then (constantly pbmpath)))))
|
||||
(p/then trace-color-mask)
|
||||
(p/then clean-svg)
|
||||
(p/then (fn [svgpath]
|
||||
(p/let [data (read-file svgpath)
|
||||
data (parse-xml data)
|
||||
data (get-in data ["elements" 0])]
|
||||
{:svgpath svgpath
|
||||
:color color
|
||||
:svgdata data}))))))
|
||||
|
||||
(join-color-layers [layers]
|
||||
(log/info :fn :join-color-layers :layers (map :svgpath layers))
|
||||
(loop [main (-> (:svgdata (first layers))
|
||||
(assoc "elements" []))
|
||||
layers (seq layers)]
|
||||
(if (nil? layers)
|
||||
main
|
||||
(let [layer (first layers)
|
||||
elements (map (fn [element]
|
||||
(update element "attributes" assoc "fill" (:color layer)))
|
||||
(get-in layer [:svgdata "elements"] []))]
|
||||
(recur (update main "elements" d/concat elements)
|
||||
(next layers))))))
|
||||
|
||||
(convert-to-svg [colors ppmpath]
|
||||
(log/info :fn :convert-to-svg :ppmpath ppmpath :colors colors)
|
||||
(-> (p/all (map (partial generate-color-mask ppmpath) colors))
|
||||
(p/then join-color-layers)))
|
||||
|
||||
(clean-svg [svgpath]
|
||||
(log/info :fn :clean-svg :svgpath svgpath)
|
||||
(let [basepath (path/dirname svgpath)
|
||||
basename (path/basename svgpath ".svg")
|
||||
svgpath' (path/join basepath (str basename "-optimized.svg"))]
|
||||
(-> (run-cmd! (str "svgcleaner " svgpath " " svgpath'))
|
||||
(p/then (constantly svgpath')))))
|
||||
|
||||
(trace-single-node [{:keys [data] :as node}]
|
||||
(log/info :fn :trace-single-node)
|
||||
(p/let [tdpath (create-tmpdir! "svgexport-")
|
||||
pngpath (path/join tdpath "origin.png")
|
||||
_ (write-file! pngpath data)
|
||||
ppmpath (convert-to-ppm pngpath)
|
||||
svgdata (convert-to-svg (:colors node) ppmpath)
|
||||
svgdata (update svgdata "attributes" assoc
|
||||
"width" (:width node)
|
||||
"height" (:height node)
|
||||
"x" (:x node)
|
||||
"y" (:y node))]
|
||||
(-> node
|
||||
(dissoc :data)
|
||||
(assoc :tempdir tdpath
|
||||
:svgdata svgdata))))
|
||||
|
||||
(extract-element-attrs [^js element]
|
||||
(let [^js attrs (.. element -attributes)
|
||||
^js colors (.. element -dataset -colors)]
|
||||
#js {:id (.. attrs -id -value)
|
||||
:x (.. attrs -x -value)
|
||||
:y (.. attrs -y -value)
|
||||
:width (.. attrs -width -value)
|
||||
:height (.. attrs -height -value)
|
||||
:colors (.split colors ",")}))
|
||||
|
||||
(extract-single-node [node]
|
||||
(log/info :fn :extract-single-node)
|
||||
|
||||
(p/let [attrs (bwr/eval! node extract-element-attrs)
|
||||
shot (bwr/screenshot node {:omit-background? true :type "png"})]
|
||||
{:id (unchecked-get attrs "id")
|
||||
:x (unchecked-get attrs "x")
|
||||
:y (unchecked-get attrs "y")
|
||||
:width (unchecked-get attrs "width")
|
||||
:height (unchecked-get attrs "height")
|
||||
:colors (vec (unchecked-get attrs "colors"))
|
||||
:data shot}))
|
||||
|
||||
(clean-temp-data [{:keys [tempdir] :as node}]
|
||||
(p/do!
|
||||
(rmdir! tempdir)
|
||||
(dissoc node :tempdir)))
|
||||
|
||||
(process-single-text-node [item]
|
||||
(-> (p/resolved item)
|
||||
(p/then extract-single-node)
|
||||
(p/then trace-single-node)
|
||||
(p/then clean-temp-data)))
|
||||
|
||||
(process-text-nodes [page]
|
||||
(log/info :fn :process-text-nodes)
|
||||
(-> (bwr/select-all page "#screenshot foreignObject")
|
||||
(p/then #(p/all (map process-single-text-node %)))))
|
||||
|
||||
(replace-nodes-on-main [main nodes]
|
||||
(let [main (parse-xml main)
|
||||
index (d/index-by :id nodes)
|
||||
main (walk/prewalk (fn [form]
|
||||
(cond
|
||||
(and (map? form)
|
||||
(= "element" (get form "type"))
|
||||
(= "foreignObject" (get form "name")))
|
||||
(let [id (get-in form ["attributes" "id"])
|
||||
node (get index id)]
|
||||
(if node
|
||||
(:svgdata node)
|
||||
form))
|
||||
|
||||
:else
|
||||
form))
|
||||
main)]
|
||||
(encode-xml main)))
|
||||
|
||||
(render-svg [page]
|
||||
(p/let [dom (bwr/select page "#screenshot")
|
||||
main (bwr/eval! dom (fn [elem] (.-innerHTML ^js elem)))
|
||||
nodes (process-text-nodes page)]
|
||||
(replace-nodes-on-main main nodes)))
|
||||
|
||||
(handle [rctx page]
|
||||
(p/let [page (render-in-page page rctx)]
|
||||
(render-svg page)))]
|
||||
|
||||
(let [path (str "/render-object/" page-id "/" object-id)
|
||||
uri (doto (Uri. (:public-uri cfg/config))
|
||||
(.setPath "/")
|
||||
(.setFragment path))
|
||||
rctx {:cookie {:domain (str (.getDomain uri) ":" (.getPort uri))
|
||||
:key "auth-token"
|
||||
:value token}
|
||||
:uri (.toString uri)}]
|
||||
(bwr/exec! browser (partial handle rctx)))))
|
||||
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::suffix ::us/string)
|
||||
(s/def ::type #{:svg})
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::object-id ::us/uuid)
|
||||
(s/def ::scale ::us/number)
|
||||
(s/def ::token ::us/string)
|
||||
(s/def ::filename ::us/string)
|
||||
|
||||
(s/def ::export-params
|
||||
(s/keys :req-un [::name ::suffix ::type ::object-id ::page-id ::scale ::token]
|
||||
:opt-un [::filename]))
|
||||
|
||||
(defn export
|
||||
[browser params]
|
||||
(us/assert ::export-params params)
|
||||
(p/let [content (render-object browser params)]
|
||||
{:content content
|
||||
:filename (or (:filename params)
|
||||
(str (str/slug (:name params))
|
||||
(str/trim (:suffix params ""))
|
||||
".svg"))
|
||||
:length (alength content)
|
||||
:mime-type "image/svg+xml"}))
|
136
exporter/src/app/http/impl.cljs
Normal file
136
exporter/src/app/http/impl.cljs
Normal file
|
@ -0,0 +1,136 @@
|
|||
(ns app.http.impl
|
||||
(:require
|
||||
["http" :as http]
|
||||
["inflation" :as inflate]
|
||||
["koa" :as koa]
|
||||
["raw-body" :as raw-body]
|
||||
[app.util.transit :as t]
|
||||
[cuerdas.core :as str]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[reitit.core :as r])
|
||||
(:import
|
||||
goog.Uri))
|
||||
|
||||
(defn- query-params
|
||||
"Given goog.Uri, read query parameters into Clojure map."
|
||||
[^Uri uri]
|
||||
(let [^js q (.getQueryData uri)]
|
||||
(->> q
|
||||
(.getKeys)
|
||||
(map (juxt keyword #(.get q %)))
|
||||
(into {}))))
|
||||
|
||||
(defn- match
|
||||
[router ctx]
|
||||
(let [uri (.parse Uri (unchecked-get ctx "originalUrl"))]
|
||||
(when-let [match (r/match-by-path router (.getPath ^js uri))]
|
||||
(assoc match :query-params (query-params uri)))))
|
||||
|
||||
(defn- handle-error
|
||||
[error request]
|
||||
(let [{:keys [type message code] :as data} (ex-data error)]
|
||||
(cond
|
||||
(= :validation type)
|
||||
(let [header (get-in request [:headers "accept"])]
|
||||
(if (and (str/starts-with? header "text/html")
|
||||
(= :spec-validation (:code data)))
|
||||
{:status 400
|
||||
:headers {"content-type" "text/html"}
|
||||
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}
|
||||
{:status 400
|
||||
:headers {"content-type" "text/html"}
|
||||
:body (str "<pre style='font-size:16px'>" (:explain data) "</pre>\n")}))
|
||||
|
||||
:else
|
||||
(do
|
||||
(log/error :msg "Unexpected error"
|
||||
:error error)
|
||||
(js/console.error error)
|
||||
{:status 500
|
||||
:headers {"x-metadata" (t/encode {:type :unexpected
|
||||
:message (ex-message error)})}
|
||||
:body ""}))))
|
||||
|
||||
|
||||
(defn- handle-response
|
||||
[ctx {:keys [body headers status] :or {headers {} status 200}}]
|
||||
(run! (fn [[k v]] (.set ^js ctx k v)) headers)
|
||||
(set! (.-body ^js ctx) body)
|
||||
(set! (.-status ^js ctx) status)
|
||||
nil)
|
||||
|
||||
(defn- parse-headers
|
||||
[ctx]
|
||||
(let [orig (unchecked-get ctx "headers")]
|
||||
(persistent!
|
||||
(reduce #(assoc! %1 %2 (unchecked-get orig %2))
|
||||
(transient {})
|
||||
(js/Object.keys orig)))))
|
||||
|
||||
(def parse-body? #{"POST" "PUT" "DELETE"})
|
||||
|
||||
(defn- parse-body
|
||||
[ctx]
|
||||
(let [headers (unchecked-get ctx "headers")
|
||||
ctype (unchecked-get headers "content-type")]
|
||||
(when (parse-body? (.-method ^js ctx))
|
||||
(-> (inflate (.-req ^js ctx))
|
||||
(raw-body #js {:limit "5mb" :encoding "utf8"})
|
||||
(p/then (fn [data]
|
||||
(cond-> data
|
||||
(= ctype "application/transit+json")
|
||||
(t/decode))))))))
|
||||
|
||||
(defn- wrap-handler
|
||||
[f extra]
|
||||
(fn [ctx]
|
||||
(p/let [cookies (unchecked-get ctx "cookies")
|
||||
headers (parse-headers ctx)
|
||||
body (parse-body ctx)
|
||||
request (assoc extra
|
||||
:method (str/lower (unchecked-get ctx "method"))
|
||||
:body body
|
||||
:ctx ctx
|
||||
:headers headers
|
||||
:cookies cookies)]
|
||||
(-> (p/do! (f request))
|
||||
(p/then (fn [rsp]
|
||||
(when (map? rsp)
|
||||
(handle-response ctx rsp))))
|
||||
(p/catch (fn [err]
|
||||
(->> (handle-error err request)
|
||||
(handle-response ctx))))))))
|
||||
|
||||
(defn- router-handler
|
||||
[router]
|
||||
(fn [{:keys [ctx body] :as request}]
|
||||
(let [route (match router ctx)
|
||||
params (merge {}
|
||||
(:query-params route)
|
||||
(:path-params route)
|
||||
(when (map? body) body))
|
||||
request (assoc request
|
||||
:route route
|
||||
:params params)
|
||||
|
||||
handler (get-in route [:data :handler])]
|
||||
(if (and route handler)
|
||||
(handler request)
|
||||
{:status 404
|
||||
:body "Not found"}))))
|
||||
|
||||
(defn server
|
||||
[handler]
|
||||
(.createServer http @handler))
|
||||
|
||||
(defn handler
|
||||
[router extra]
|
||||
(let [instance (doto (new koa)
|
||||
(.use (-> (router-handler router)
|
||||
(wrap-handler extra))))]
|
||||
(specify! instance
|
||||
cljs.core/IDeref
|
||||
(-deref [_]
|
||||
(.callback instance)))))
|
||||
|
1
exporter/test.svg
Normal file
1
exporter/test.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 20 KiB |
|
@ -15,6 +15,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
||||
integrity sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==
|
||||
|
||||
"@types/q@^1.5.1":
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
|
||||
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af"
|
||||
|
@ -35,11 +40,25 @@ agent-base@5:
|
|||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
|
||||
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
any-promise@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
asn1.js@^4.0.0:
|
||||
version "4.10.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
|
||||
|
@ -91,6 +110,11 @@ bn.js@^5.1.1:
|
|||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0"
|
||||
integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==
|
||||
|
||||
boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
|
@ -215,6 +239,15 @@ cache-content-type@^1.0.0:
|
|||
mime-types "^2.1.18"
|
||||
ylru "^1.2.0"
|
||||
|
||||
chalk@^2.4.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
|
@ -233,6 +266,27 @@ co@^4.6.0:
|
|||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
|
||||
|
||||
coa@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
|
||||
integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
|
||||
dependencies:
|
||||
"@types/q" "^1.5.1"
|
||||
chalk "^2.4.1"
|
||||
q "^1.1.2"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
@ -326,6 +380,49 @@ crypto-browserify@^3.11.0:
|
|||
randombytes "^2.0.0"
|
||||
randomfill "^1.0.3"
|
||||
|
||||
css-select-base-adapter@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
|
||||
integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
|
||||
|
||||
css-select@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
|
||||
integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
css-what "^3.2.1"
|
||||
domutils "^1.7.0"
|
||||
nth-check "^1.0.2"
|
||||
|
||||
css-tree@1.0.0-alpha.37:
|
||||
version "1.0.0-alpha.37"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
|
||||
integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
|
||||
dependencies:
|
||||
mdn-data "2.0.4"
|
||||
source-map "^0.6.1"
|
||||
|
||||
css-tree@1.0.0-alpha.39:
|
||||
version "1.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
|
||||
integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
|
||||
dependencies:
|
||||
mdn-data "2.0.6"
|
||||
source-map "^0.6.1"
|
||||
|
||||
css-what@^3.2.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
|
||||
integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
|
||||
|
||||
csso@^4.0.2:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903"
|
||||
integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==
|
||||
dependencies:
|
||||
css-tree "1.0.0-alpha.39"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
|
@ -345,6 +442,13 @@ deep-equal@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||
integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=
|
||||
|
||||
define-properties@^1.1.2, define-properties@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
|
||||
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
|
||||
dependencies:
|
||||
object-keys "^1.0.12"
|
||||
|
||||
delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
|
@ -382,11 +486,37 @@ diffie-hellman@^5.0.0:
|
|||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
||||
integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
entities "^2.0.0"
|
||||
|
||||
domain-browser@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
|
||||
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
||||
|
||||
domelementtype@1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
|
||||
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
|
||||
|
||||
domelementtype@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
|
||||
integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
|
||||
|
||||
domutils@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
|
||||
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
|
||||
dependencies:
|
||||
dom-serializer "0"
|
||||
domelementtype "1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
|
@ -417,11 +547,52 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
|||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
entities@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
|
||||
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
|
||||
|
||||
es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
|
||||
version "1.17.6"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
|
||||
integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
|
||||
dependencies:
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.2.0"
|
||||
is-regex "^1.1.0"
|
||||
object-inspect "^1.7.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.0"
|
||||
string.prototype.trimend "^1.0.1"
|
||||
string.prototype.trimstart "^1.0.1"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
||||
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
|
||||
dependencies:
|
||||
is-callable "^1.1.4"
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
escape-html@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
events@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
|
||||
|
@ -468,6 +639,11 @@ fs.realpath@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9"
|
||||
|
@ -487,6 +663,23 @@ glob@^7.1.3:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-symbols@^1.0.0, has-symbols@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
||||
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
hash-base@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33"
|
||||
|
@ -601,11 +794,35 @@ inherits@2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
is-callable@^1.1.4, is-callable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
|
||||
integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
|
||||
|
||||
is-date-object@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
||||
|
||||
is-generator-function@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
|
||||
integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==
|
||||
|
||||
is-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff"
|
||||
integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-symbol@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
||||
integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
isarray@^1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
@ -616,6 +833,14 @@ isexe@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
js-yaml@^3.13.1:
|
||||
version "3.14.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jszip@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
|
||||
|
@ -698,6 +923,16 @@ md5.js@^1.3.4:
|
|||
inherits "^2.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
mdn-data@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
|
||||
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
|
||||
|
||||
mdn-data@2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
|
||||
integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
|
@ -745,6 +980,11 @@ minimatch@^3.0.4:
|
|||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
mitt@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.0.1.tgz#9e8a075b4daae82dd91aac155a0ece40ca7cb393"
|
||||
|
@ -755,6 +995,13 @@ mkdirp-classic@^0.5.2:
|
|||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
mkdirp@~0.5.1:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
@ -799,11 +1046,56 @@ node-libs-browser@^2.0.0:
|
|||
util "^0.11.0"
|
||||
vm-browserify "^1.0.1"
|
||||
|
||||
nth-check@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
|
||||
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
|
||||
dependencies:
|
||||
boolbase "~1.0.0"
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
object-inspect@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
|
||||
integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
|
||||
|
||||
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object.assign@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
|
||||
integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
|
||||
dependencies:
|
||||
define-properties "^1.1.2"
|
||||
function-bind "^1.1.1"
|
||||
has-symbols "^1.0.0"
|
||||
object-keys "^1.0.11"
|
||||
|
||||
object.getownpropertydescriptors@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
|
||||
integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.0-next.1"
|
||||
|
||||
object.values@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
|
||||
integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.0-next.1"
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
|
||||
on-finished@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
|
@ -950,6 +1242,11 @@ puppeteer@^4.0.1:
|
|||
unbzip2-stream "^1.3.3"
|
||||
ws "^7.2.3"
|
||||
|
||||
q@^1.1.2:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
||||
|
||||
querystring-es3@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
|
@ -1047,6 +1344,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2,
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sax@^1.2.4, sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
set-immediate-shim@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
||||
|
@ -1080,10 +1382,10 @@ 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.10.13:
|
||||
version "2.10.13"
|
||||
resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.10.13.tgz#8e9f74901b51cd22169fc476eae048dba5a40c93"
|
||||
integrity sha512-I3TJeh/hwJFwKruALnKrWVPnqbdou+tXQ++SWwOfN0CiYHcj4MMhyfBJfMsKPiOsOouRCCYTQVqsiJHIitK2/Q==
|
||||
shadow-cljs@^2.10.17:
|
||||
version "2.10.17"
|
||||
resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.10.17.tgz#490aea50f95481bb40936d79bade3aa62033dca3"
|
||||
integrity sha512-yUz/hpaLz72IhI/lENYIGcwAytsUlie6/HX10ilVTjZDG9wn3Kc+DtNdgVSDpods79n7HHbDq7kImz7PUmBKnA==
|
||||
dependencies:
|
||||
node-libs-browser "^2.0.0"
|
||||
readline-sync "^1.4.7"
|
||||
|
@ -1112,11 +1414,21 @@ source-map@^0.5.6:
|
|||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||
|
||||
source-map@^0.6.0:
|
||||
source-map@^0.6.0, source-map@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
stable@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
||||
|
||||
"statuses@>= 1.5.0 < 2", statuses@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
|
@ -1141,6 +1453,22 @@ stream-http@^2.7.2:
|
|||
to-arraybuffer "^1.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
string.prototype.trimend@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
||||
integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.5"
|
||||
|
||||
string.prototype.trimstart@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
||||
integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.5"
|
||||
|
||||
string_decoder@^1.0.0, string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
|
@ -1155,6 +1483,32 @@ string_decoder@~1.1.1:
|
|||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
svgo@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
|
||||
integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
coa "^2.0.2"
|
||||
css-select "^2.0.0"
|
||||
css-select-base-adapter "^0.1.1"
|
||||
css-tree "1.0.0-alpha.37"
|
||||
csso "^4.0.2"
|
||||
js-yaml "^3.13.1"
|
||||
mkdirp "~0.5.1"
|
||||
object.values "^1.1.0"
|
||||
sax "~1.2.4"
|
||||
stable "^0.1.8"
|
||||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
tar-fs@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5"
|
||||
|
@ -1234,6 +1588,11 @@ unpipe@1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
||||
|
||||
unquote@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
|
||||
integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
|
||||
|
||||
url@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||
|
@ -1247,6 +1606,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
util.promisify@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
|
||||
integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.2"
|
||||
has-symbols "^1.0.1"
|
||||
object.getownpropertydescriptors "^2.1.0"
|
||||
|
||||
util@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
|
||||
|
@ -1297,6 +1666,13 @@ ws@^7.2.3:
|
|||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd"
|
||||
integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==
|
||||
|
||||
xml-js@^1.6.11:
|
||||
version "1.6.11"
|
||||
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
|
||||
integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==
|
||||
dependencies:
|
||||
sax "^1.2.4"
|
||||
|
||||
xregexp@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50"
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
ret)))]
|
||||
(js->clj result :keywordize-keys true)))))
|
||||
|
||||
(defn- nodes-seq
|
||||
(defn nodes-seq
|
||||
[match? node]
|
||||
(->> (tree-seq map? :children node)
|
||||
(filter match?)))
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
(let [childs (mapv #(get objects %) (:shapes shape))]
|
||||
[:& group-shape {:frame frame
|
||||
:shape shape
|
||||
:is-child-selected? true
|
||||
:childs childs}]))))
|
||||
|
||||
(defn shape-wrapper-factory
|
||||
|
|
|
@ -108,10 +108,10 @@
|
|||
(defn is-child-selected?
|
||||
[id]
|
||||
(letfn [(selector [state]
|
||||
(let [page-id (get-in state [:workspace-page :id])
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
(let [page-id (get-in state [:workspace-page :id])
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
shape (get objects id)
|
||||
shape (get objects id)
|
||||
children (cph/get-children id objects)]
|
||||
(some selected children)))]
|
||||
(l/derived selector st/state)))
|
||||
|
|
|
@ -34,7 +34,9 @@
|
|||
:id (str "shape-" id)
|
||||
:width width
|
||||
:height height}))]
|
||||
[:svg {:x x :y y :width width :height height}
|
||||
[:svg {:x x :y y :width width :height height
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns "http://www.w3.org/2000/svg"}
|
||||
[:> "rect" props]
|
||||
(for [[i item] (d/enumerate childs)]
|
||||
[:& shape-wrapper {:frame shape
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
[:& shape-wrapper {:frame frame
|
||||
:shape item
|
||||
:key (:id item)}])
|
||||
|
||||
(when (not is-child-selected?)
|
||||
[:rect {:transform transform
|
||||
:x x
|
||||
|
|
|
@ -94,7 +94,11 @@
|
|||
(case type
|
||||
"root"
|
||||
(let [style (generate-root-styles (clj->js node))]
|
||||
[:div.root.rich-text {:key index :style style} children])
|
||||
[:div.root.rich-text
|
||||
{:key index
|
||||
:style style
|
||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||
children])
|
||||
|
||||
"paragraph-set"
|
||||
(let [style #js {:display "inline-block"
|
||||
|
@ -114,6 +118,13 @@
|
|||
(let [root (obj/get props "content")]
|
||||
(render-text-node root)))
|
||||
|
||||
(defn- retrieve-colors
|
||||
[shape]
|
||||
(let [colors (into #{} (comp (map :fill)
|
||||
(filter string?))
|
||||
(tree-seq map? :children (:content shape)))]
|
||||
(apply str (interpose "," colors))))
|
||||
|
||||
(mf/defc text-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
@ -122,6 +133,7 @@
|
|||
{:keys [id x y width height rotation content]} shape]
|
||||
[:foreignObject {:x x
|
||||
:y y
|
||||
:data-colors (retrieve-colors shape)
|
||||
:transform (geom/transform-matrix shape)
|
||||
:id (str id)
|
||||
:width width
|
||||
|
|
|
@ -39,18 +39,21 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
on-mouse-down (mf/use-callback (mf/deps shape)
|
||||
#(common/on-mouse-down % shape))
|
||||
on-context-menu (mf/use-callback (mf/deps shape)
|
||||
#(common/on-context-menu % shape))
|
||||
|
||||
childs-ref (mf/use-memo (mf/deps shape)
|
||||
#(refs/objects-by-id (:shapes shape)))
|
||||
on-mouse-down
|
||||
(mf/use-callback (mf/deps shape) #(common/on-mouse-down % shape))
|
||||
|
||||
on-context-menu
|
||||
(mf/use-callback (mf/deps shape) #(common/on-context-menu % shape))
|
||||
|
||||
childs-ref (mf/use-memo (mf/deps shape) #(refs/objects-by-id (:shapes shape)))
|
||||
childs (mf/deref childs-ref)
|
||||
|
||||
is-child-selected-ref (mf/use-memo (mf/deps (:id shape))
|
||||
#(refs/is-child-selected? (:id shape)))
|
||||
is-child-selected? (mf/deref is-child-selected-ref)
|
||||
is-child-selected-ref
|
||||
(mf/use-memo (mf/deps (:id shape)) #(refs/is-child-selected? (:id shape)))
|
||||
|
||||
is-child-selected?
|
||||
(mf/deref is-child-selected-ref)
|
||||
|
||||
on-double-click
|
||||
(mf/use-callback
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
(defn- request-export
|
||||
[shape exports]
|
||||
(http/send! {:method :post
|
||||
:uri "/export/bitmap"
|
||||
:uri "/export"
|
||||
:response-type :blob
|
||||
:auth true
|
||||
:body {:page-id (:page-id shape)
|
||||
|
@ -140,7 +140,8 @@
|
|||
[:select.input-select {:value (name (:type export))
|
||||
:on-change (partial on-type-change index)}
|
||||
[:option {:value "png"} "PNG"]
|
||||
[:option {:value "jpeg"} "JPEG"]]
|
||||
[:option {:value "jpeg"} "JPEG"]
|
||||
[:option {:value "svg"} "SVG"]]
|
||||
[:div.delete-icon {:on-click (partial delete-export index)}
|
||||
i/minus]])
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
How to trace a bitmap to svg in multiple colors:
|
||||
|
||||
|
||||
Dependencies: imagemagick, netpbm, potrace
|
||||
|
||||
Generate the PPM format from PNG:
|
||||
|
||||
convert download.png download.ppm
|
||||
|
|
Loading…
Reference in a new issue