mirror of
https://github.com/penpot/penpot.git
synced 2025-05-29 10:06:12 +02:00
🎉 Add .penpot (binfile-v3) support for library
This commit is contained in:
parent
1fea1e8f5b
commit
29d23577d2
20 changed files with 926 additions and 751 deletions
80
CHANGES.md
80
CHANGES.md
|
@ -8,33 +8,65 @@
|
||||||
|
|
||||||
**Breaking changes on penpot library:**
|
**Breaking changes on penpot library:**
|
||||||
|
|
||||||
|
The library entrypoint API object has been changed. From now you start creating a new
|
||||||
|
build context, from where you can add multiple files and attach media. This change add the
|
||||||
|
ability to build more than one file at same time and export them in an unique .penpot
|
||||||
|
file.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const context = penpot.createBuildContext()
|
||||||
|
|
||||||
|
context.addFile({name:"aa"})
|
||||||
|
context.addPage({name:"aa"})
|
||||||
|
context.closePage()
|
||||||
|
context.closeFile()
|
||||||
|
|
||||||
|
;; barray is instance of Uint8Array
|
||||||
|
const barray = penpot.exportAsBytes(context);
|
||||||
|
```
|
||||||
|
|
||||||
|
The previous `file.export()` method has been removed and several alternatives are
|
||||||
|
added as first level functions on penpot library API entrypoint:
|
||||||
|
|
||||||
|
- `exportAsBytes(BuildContext context) -> Promise<Uint8Array>`
|
||||||
|
- `exportAsBlob(BuildContext context) -> Promise<Blob>`
|
||||||
|
- `exportStream(BuildContext context, WritableStream stream) -> Promise<Void>`
|
||||||
|
|
||||||
|
The stream variant allows writting data as it is generated to the stream, without the need
|
||||||
|
to store the generated output entirelly in the memory.
|
||||||
|
|
||||||
|
There are also relevant semantic changes in how components should be created: this
|
||||||
|
refactor removes all notions of the old components (v1). Since v2, the shapes that are
|
||||||
|
part of a component live on a page. So, from now on, to create a component, you should
|
||||||
|
first create a frame, then add shapes and/or groups to that frame, and then create a
|
||||||
|
component by declaring that frame as the component root.
|
||||||
|
|
||||||
|
A non exhaustive list of changes:
|
||||||
|
|
||||||
- Change the signature of the `addPage` method: it now accepts an object (as a single argument) where you can pass `id`,
|
- Change the signature of the `addPage` method: it now accepts an object (as a single argument) where you can pass `id`,
|
||||||
`name`, and `background` props (instead of the previous positional arguments)
|
`name`, and `background` props (instead of the previous positional arguments)
|
||||||
- Rename the `file.createRect` method to `file.addRect`
|
- Rename the `createRect` method to `addRect`
|
||||||
- Rename the `file.createCircle` method to `file.addCircle`
|
- Rename the `createCircle` method to `addCircle`
|
||||||
- Rename the `file.createPath` method to `file.addPath`
|
- Rename the `createPath` method to `addPath`
|
||||||
- Rename the `file.createText` method to `file.addText`
|
- Rename the `createText` method to `addText`
|
||||||
- Rename `file.startComponent` to `file.addComponent` (to preserve the naming style)
|
- Rename the `addArtboard` method to `addBoard`
|
||||||
- Rename `file.createComponentInstance` to `file.addComponentInstance` (to preserve the naming style)
|
- Rename `startComponent` to `addComponent` (to preserve the naming style)
|
||||||
- Rename `file.lookupShape` to `file.getShape`
|
- Rename `createComponentInstance` to `addComponentInstance` (to preserve the naming style)
|
||||||
- Rename `file.asMap` to `file.toMap`
|
- Remove `lookupShape`
|
||||||
- Remove `file.updateLibraryColor` (use `file.addLibraryColor` if you just need to replace a color)
|
- Remove `asMap`
|
||||||
- Remove `file.deleteLibraryColor` (this library is intended to build files)
|
- Remove `updateLibraryColor` (use `addLibraryColor` if you just need to replace a color)
|
||||||
- Remove `file.updateLibraryTypography` (use `file.addLibraryTypography` if you just need to replace a typography)
|
- Remove `deleteLibraryColor` (this library is intended to build files)
|
||||||
- Remove `file.deleteLibraryTypography` (this library is intended to build files)
|
- Remove `updateLibraryTypography` (use `addLibraryTypography` if you just need to replace a typography)
|
||||||
- Remove `file.add/update/deleteLibraryMedia` (they are no longer supported by Penpot and have been replaced by components)
|
- Remove `deleteLibraryTypography` (this library is intended to build files)
|
||||||
- Remove `file.deleteObject` (this library is intended to build files)
|
- Remove `add/update/deleteLibraryMedia` (they are no longer supported by Penpot and have been replaced by components)
|
||||||
- Remove `file.updateObject` (this library is intended to build files)
|
- Remove `deleteObject` (this library is intended to build files)
|
||||||
- Remove `file.finishComponent` (it is no longer necessary; see below for more details on component creation changes)
|
- Remove `updateObject` (this library is intended to build files)
|
||||||
- Change the `file.getCurrentPageId` function to a read-only `file.currentPageId` property
|
- Remove `finishComponent` (it is no longer necessary; see below for more details on component creation changes)
|
||||||
- Add `file.currentFrameId` read-only property
|
|
||||||
- Add `file.lastId` read-only property
|
|
||||||
|
|
||||||
There are also relevant semantic changes in how components should be created: this refactor removes
|
- Change the `getCurrentPageId` function to a read-only `currentPageId` property
|
||||||
all notions of the old components (v1). Since v2, the shapes that are part of a component live on a
|
- Add `currentFileId` read-only property
|
||||||
page. So, from now on, to create a component, you should first create a frame, then add shapes
|
- Add `currentFrameId` read-only property
|
||||||
and/or groups to that frame, and then create a component by declaring that frame as the component
|
- Add `lastId` read-only property
|
||||||
root.
|
|
||||||
|
|
||||||
### :heart: Community contributions (Thank you!)
|
### :heart: Community contributions (Thank you!)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
[app.common.files.migrations :as-alias fmg]
|
[app.common.files.migrations :as-alias fmg]
|
||||||
[app.common.json :as json]
|
[app.common.json :as json]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
[app.common.media :as cmedia]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.thumbnails :as cth]
|
[app.common.thumbnails :as cth]
|
||||||
[app.common.types.color :as ctcl]
|
[app.common.types.color :as ctcl]
|
||||||
|
@ -73,7 +74,7 @@
|
||||||
[:size ::sm/int]
|
[:size ::sm/int]
|
||||||
[:content-type :string]
|
[:content-type :string]
|
||||||
[:bucket [::sm/one-of {:format :string} sto/valid-buckets]]
|
[:bucket [::sm/one-of {:format :string} sto/valid-buckets]]
|
||||||
[:hash :string]])
|
[:hash {:optional true} :string]])
|
||||||
|
|
||||||
(def ^:private schema:file-thumbnail
|
(def ^:private schema:file-thumbnail
|
||||||
[:map {:title "FileThumbnail"}
|
[:map {:title "FileThumbnail"}
|
||||||
|
@ -88,13 +89,19 @@
|
||||||
ctf/schema:file
|
ctf/schema:file
|
||||||
[:map [:options {:optional true} ctf/schema:options]]])
|
[:map [:options {:optional true} ctf/schema:options]]])
|
||||||
|
|
||||||
|
;; --- HELPERS
|
||||||
|
|
||||||
|
(defn- default-now
|
||||||
|
[o]
|
||||||
|
(or o (dt/now)))
|
||||||
|
|
||||||
;; --- ENCODERS
|
;; --- ENCODERS
|
||||||
|
|
||||||
(def encode-file
|
(def encode-file
|
||||||
(sm/encoder schema:file sm/json-transformer))
|
(sm/encoder schema:file sm/json-transformer))
|
||||||
|
|
||||||
(def encode-page
|
(def encode-page
|
||||||
(sm/encoder ::ctp/page sm/json-transformer))
|
(sm/encoder ctp/schema:page sm/json-transformer))
|
||||||
|
|
||||||
(def encode-shape
|
(def encode-shape
|
||||||
(sm/encoder ::cts/shape sm/json-transformer))
|
(sm/encoder ::cts/shape sm/json-transformer))
|
||||||
|
@ -129,7 +136,7 @@
|
||||||
(sm/decoder schema:manifest sm/json-transformer))
|
(sm/decoder schema:manifest sm/json-transformer))
|
||||||
|
|
||||||
(def decode-media
|
(def decode-media
|
||||||
(sm/decoder ::ctf/media sm/json-transformer))
|
(sm/decoder ctf/schema:media sm/json-transformer))
|
||||||
|
|
||||||
(def decode-component
|
(def decode-component
|
||||||
(sm/decoder ::ctc/component sm/json-transformer))
|
(sm/decoder ::ctc/component sm/json-transformer))
|
||||||
|
@ -229,27 +236,13 @@
|
||||||
:always
|
:always
|
||||||
(bfc/clean-file-features))))))
|
(bfc/clean-file-features))))))
|
||||||
|
|
||||||
(defn- resolve-extension
|
|
||||||
[mtype]
|
|
||||||
(case mtype
|
|
||||||
"image/png" ".png"
|
|
||||||
"image/jpeg" ".jpg"
|
|
||||||
"image/gif" ".gif"
|
|
||||||
"image/svg+xml" ".svg"
|
|
||||||
"image/webp" ".webp"
|
|
||||||
"font/woff" ".woff"
|
|
||||||
"font/woff2" ".woff2"
|
|
||||||
"font/ttf" ".ttf"
|
|
||||||
"font/otf" ".otf"
|
|
||||||
"application/octet-stream" ".bin"))
|
|
||||||
|
|
||||||
(defn- export-storage-objects
|
(defn- export-storage-objects
|
||||||
[{:keys [::output] :as cfg}]
|
[{:keys [::output] :as cfg}]
|
||||||
(let [storage (sto/resolve cfg)]
|
(let [storage (sto/resolve cfg)]
|
||||||
(doseq [id (-> bfc/*state* deref :storage-objects not-empty)]
|
(doseq [id (-> bfc/*state* deref :storage-objects not-empty)]
|
||||||
(let [sobject (sto/get-object storage id)
|
(let [sobject (sto/get-object storage id)
|
||||||
smeta (meta sobject)
|
smeta (meta sobject)
|
||||||
ext (resolve-extension (:content-type smeta))
|
ext (cmedia/mtype->extension (:content-type smeta))
|
||||||
path (str "objects/" id ".json")
|
path (str "objects/" id ".json")
|
||||||
params (-> (meta sobject)
|
params (-> (meta sobject)
|
||||||
(assoc :id (:id sobject))
|
(assoc :id (:id sobject))
|
||||||
|
@ -574,7 +567,14 @@
|
||||||
(let [object (->> (read-entry input entry)
|
(let [object (->> (read-entry input entry)
|
||||||
(decode-media)
|
(decode-media)
|
||||||
(validate-media))
|
(validate-media))
|
||||||
object (assoc object :file-id file-id)]
|
object (-> object
|
||||||
|
(assoc :file-id file-id)
|
||||||
|
(update :created-at default-now)
|
||||||
|
(update :modified-at default-now)
|
||||||
|
;; FIXME: this is set default to true for
|
||||||
|
;; setting a value, this prop is no longer
|
||||||
|
;; relevant;
|
||||||
|
(assoc :is-local true))]
|
||||||
(if (= id (:id object))
|
(if (= id (:id object))
|
||||||
(conj result object)
|
(conj result object)
|
||||||
result)))
|
result)))
|
||||||
|
@ -800,7 +800,7 @@
|
||||||
:expected-id (str id)
|
:expected-id (str id)
|
||||||
:found-id (str (:id object))))
|
:found-id (str (:id object))))
|
||||||
|
|
||||||
(let [ext (resolve-extension (:content-type object))
|
(let [ext (cmedia/mtype->extension (:content-type object))
|
||||||
path (str "objects/" id ext)
|
path (str "objects/" id ext)
|
||||||
content (->> path
|
content (->> path
|
||||||
(get-zip-entry input)
|
(get-zip-entry input)
|
||||||
|
@ -814,13 +814,14 @@
|
||||||
:expected-size (:size object)
|
:expected-size (:size object)
|
||||||
:found-size (sto/get-size content)))
|
:found-size (sto/get-size content)))
|
||||||
|
|
||||||
(when (not= (:hash object) (sto/get-hash content))
|
(when-let [hash (get object :hash)]
|
||||||
(ex/raise :type :validation
|
(when (not= hash (sto/get-hash content))
|
||||||
:code :inconsistent-penpot-file
|
(ex/raise :type :validation
|
||||||
:hint "found corrupted storage object: hash does not match"
|
:code :inconsistent-penpot-file
|
||||||
:path path
|
:hint "found corrupted storage object: hash does not match"
|
||||||
:expected-hash (:hash object)
|
:path path
|
||||||
:found-hash (sto/get-hash content)))
|
:expected-hash (:hash object)
|
||||||
|
:found-hash (sto/get-hash content))))
|
||||||
|
|
||||||
(let [params (-> object
|
(let [params (-> object
|
||||||
(dissoc :id :size)
|
(dissoc :id :size)
|
||||||
|
|
|
@ -1712,6 +1712,7 @@
|
||||||
[{:fill-image
|
[{:fill-image
|
||||||
{:id (:id fmedia)
|
{:id (:id fmedia)
|
||||||
:name "test"
|
:name "test"
|
||||||
|
:mtype "image/jpeg"
|
||||||
:width 200
|
:width 200
|
||||||
:height 200}}]]
|
:height 200}}]]
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,12 @@
|
||||||
[app.common.features :as cfeat]
|
[app.common.features :as cfeat]
|
||||||
[app.common.files.changes :as ch]
|
[app.common.files.changes :as ch]
|
||||||
[app.common.files.migrations :as fmig]
|
[app.common.files.migrations :as fmig]
|
||||||
[app.common.geom.point :as gpt]
|
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.svg :as csvg]
|
[app.common.svg :as csvg]
|
||||||
[app.common.types.color :as types.color]
|
[app.common.types.color :as types.color]
|
||||||
[app.common.types.component :as types.component]
|
|
||||||
[app.common.types.components-list :as types.components-list]
|
|
||||||
[app.common.types.container :as types.container]
|
|
||||||
[app.common.types.file :as types.file]
|
[app.common.types.file :as types.file]
|
||||||
[app.common.types.page :as types.page]
|
[app.common.types.page :as types.page]
|
||||||
[app.common.types.pages-list :as types.pages-list]
|
|
||||||
[app.common.types.shape :as types.shape]
|
[app.common.types.shape :as types.shape]
|
||||||
[app.common.types.typography :as types.typography]
|
[app.common.types.typography :as types.typography]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
@ -37,41 +32,36 @@
|
||||||
(def ^:private conjv (fnil conj []))
|
(def ^:private conjv (fnil conj []))
|
||||||
(def ^:private conjs (fnil conj #{}))
|
(def ^:private conjs (fnil conj #{}))
|
||||||
|
|
||||||
(defn default-uuid
|
(defn- default-uuid
|
||||||
[v]
|
[v]
|
||||||
(or v (uuid/next)))
|
(or v (uuid/next)))
|
||||||
|
|
||||||
(defn- track-used-name
|
(defn- track-used-name
|
||||||
[file name]
|
[state name]
|
||||||
(let [container-id (::current-page-id file)]
|
(let [container-id (::current-page-id state)]
|
||||||
(update-in file [::unames container-id] conjs name)))
|
(update-in state [::unames container-id] conjs name)))
|
||||||
|
|
||||||
(defn- commit-change
|
(defn- commit-change
|
||||||
[file change & {:keys [add-container]
|
[state change & {:keys [add-container]}]
|
||||||
:or {add-container false}}]
|
(let [file-id (get state ::current-file-id)]
|
||||||
|
(assert (uuid? file-id) "no current file id")
|
||||||
|
|
||||||
(let [change (cond-> change
|
(let [change (cond-> change
|
||||||
add-container
|
add-container
|
||||||
(assoc :page-id (::current-page-id file)
|
(assoc :page-id (::current-page-id state)
|
||||||
:frame-id (::current-frame-id file)))]
|
:frame-id (::current-frame-id state)))]
|
||||||
(-> file
|
(update-in state [::files file-id :data] ch/process-changes [change] false))))
|
||||||
(update ::changes conjv change)
|
|
||||||
(update :data ch/process-changes [change] false))))
|
|
||||||
|
|
||||||
(defn- lookup-objects
|
|
||||||
[file]
|
|
||||||
(dm/get-in file [:data :pages-index (::current-page-id file) :objects]))
|
|
||||||
|
|
||||||
(defn- commit-shape
|
(defn- commit-shape
|
||||||
[file shape]
|
[state shape]
|
||||||
(let [parent-id
|
(let [parent-id
|
||||||
(-> file ::parent-stack peek)
|
(-> state ::parent-stack peek)
|
||||||
|
|
||||||
frame-id
|
frame-id
|
||||||
(::current-frame-id file)
|
(get state ::current-frame-id)
|
||||||
|
|
||||||
page-id
|
page-id
|
||||||
(::current-page-id file)
|
(get state ::current-page-id)
|
||||||
|
|
||||||
change
|
change
|
||||||
{:type :add-obj
|
{:type :add-obj
|
||||||
|
@ -82,39 +72,31 @@
|
||||||
:frame-id frame-id
|
:frame-id frame-id
|
||||||
:page-id page-id}]
|
:page-id page-id}]
|
||||||
|
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change change)
|
(commit-change change)
|
||||||
(track-used-name (:name shape)))))
|
(track-used-name (:name shape)))))
|
||||||
|
|
||||||
(defn- generate-name
|
|
||||||
[type data]
|
|
||||||
(if (= type :svg-raw)
|
|
||||||
(let [tag (dm/get-in data [:content :tag])]
|
|
||||||
(str "svg-" (cond (string? tag) tag
|
|
||||||
(keyword? tag) (d/name tag)
|
|
||||||
(nil? tag) "node"
|
|
||||||
:else (str tag))))
|
|
||||||
(str/capital (d/name type))))
|
|
||||||
|
|
||||||
(defn- unique-name
|
(defn- unique-name
|
||||||
[name file]
|
[name state]
|
||||||
(let [container-id (::current-page-id file)
|
(let [container-id (::current-page-id state)
|
||||||
unames (dm/get-in file [:unames container-id])]
|
unames (dm/get-in state [:unames container-id])]
|
||||||
(d/unique-name name (or unames #{}))))
|
(d/unique-name name (or unames #{}))))
|
||||||
|
|
||||||
(defn- clear-names [file]
|
(defn- clear-names [file]
|
||||||
(dissoc file ::unames))
|
(dissoc file ::unames))
|
||||||
|
|
||||||
(defn- assign-name
|
(defn- assign-shape-name
|
||||||
"Given a tag returns its layer name"
|
"Given a tag returns its layer name"
|
||||||
[data file type]
|
[shape state]
|
||||||
|
(cond-> shape
|
||||||
(cond-> data
|
(nil? (:name shape))
|
||||||
(nil? (:name data))
|
(assoc :name (let [type (get shape :type)]
|
||||||
(assoc :name (generate-name type data))
|
(case type
|
||||||
|
:frame "Board"
|
||||||
|
(str/capital (d/name type)))))
|
||||||
|
|
||||||
:always
|
:always
|
||||||
(update :name unique-name file)))
|
(update :name unique-name state)))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMAS
|
;; SCHEMAS
|
||||||
|
@ -135,16 +117,25 @@
|
||||||
(def decode-library-typography
|
(def decode-library-typography
|
||||||
(sm/decode-fn types.typography/schema:typography sm/json-transformer))
|
(sm/decode-fn types.typography/schema:typography sm/json-transformer))
|
||||||
|
|
||||||
(def decode-component
|
(def schema:add-component
|
||||||
(sm/decode-fn types.component/schema:component sm/json-transformer))
|
[:map
|
||||||
|
[:component-id ::sm/uuid]
|
||||||
|
[:file-id {:optional true} ::sm/uuid]
|
||||||
|
[:name {:optional true} ::sm/text]
|
||||||
|
[:path {:optional true} ::sm/text]])
|
||||||
|
|
||||||
|
(def ^:private check-add-component
|
||||||
|
(sm/check-fn schema:add-component))
|
||||||
|
|
||||||
|
(def decode-add-component
|
||||||
|
(sm/decode-fn schema:add-component sm/json-transformer))
|
||||||
|
|
||||||
(def schema:add-component-instance
|
(def schema:add-component-instance
|
||||||
[:map
|
[:map
|
||||||
[:component-id ::sm/uuid]
|
[:component-id ::sm/uuid]
|
||||||
[:x ::sm/safe-number]
|
[:file-id {:optional true} ::sm/uuid]])
|
||||||
[:y ::sm/safe-number]])
|
|
||||||
|
|
||||||
(def check-add-component-instance
|
(def ^:private check-add-component-instance
|
||||||
(sm/check-fn schema:add-component-instance))
|
(sm/check-fn schema:add-component-instance))
|
||||||
|
|
||||||
(def decode-add-component-instance
|
(def decode-add-component-instance
|
||||||
|
@ -158,37 +149,76 @@
|
||||||
(def decode-add-bool
|
(def decode-add-bool
|
||||||
(sm/decode-fn schema:add-bool sm/json-transformer))
|
(sm/decode-fn schema:add-bool sm/json-transformer))
|
||||||
|
|
||||||
(def check-add-bool
|
(def ^:private check-add-bool
|
||||||
(sm/check-fn schema:add-bool))
|
(sm/check-fn schema:add-bool))
|
||||||
|
|
||||||
|
(def schema:add-file-media
|
||||||
|
[:map
|
||||||
|
[:id {:optional true} ::sm/uuid]
|
||||||
|
[:name ::sm/text]
|
||||||
|
[:width ::sm/int]
|
||||||
|
[:height ::sm/int]])
|
||||||
|
|
||||||
|
(def decode-add-file-media
|
||||||
|
(sm/decode-fn schema:add-file-media sm/json-transformer))
|
||||||
|
|
||||||
|
(def check-add-file-media
|
||||||
|
(sm/check-fn schema:add-file-media))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; PUBLIC API
|
;; PUBLIC API
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn lookup-shape [file shape-id]
|
(defn create-state
|
||||||
(-> (lookup-objects file)
|
[]
|
||||||
(get shape-id)))
|
{})
|
||||||
|
|
||||||
(defn get-current-page
|
(defn get-current-page
|
||||||
[file]
|
[state]
|
||||||
(let [page-id (::current-page-id file)]
|
(let [file-id (get state ::current-file-id)
|
||||||
(dm/get-in file [:data :pages-index page-id])))
|
page-id (get state ::current-page-id)]
|
||||||
|
|
||||||
(defn create-file
|
(assert (uuid? file-id) "expected current-file-id to be assigned")
|
||||||
[params]
|
(assert (uuid? page-id) "expected current-page-id to be assigned")
|
||||||
|
(dm/get-in state [::files file-id :data :pages-index page-id])))
|
||||||
|
|
||||||
|
(defn get-current-objects
|
||||||
|
[state]
|
||||||
|
(-> (get-current-page state)
|
||||||
|
(get :objects)))
|
||||||
|
|
||||||
|
(defn get-shape
|
||||||
|
[state shape-id]
|
||||||
|
(-> (get-current-objects state)
|
||||||
|
(get shape-id)))
|
||||||
|
|
||||||
|
(defn add-file
|
||||||
|
[state params]
|
||||||
(let [params (-> params
|
(let [params (-> params
|
||||||
(assoc :features cfeat/default-features)
|
(assoc :features cfeat/default-features)
|
||||||
(assoc :migrations fmig/available-migrations))]
|
(assoc :migrations fmig/available-migrations))
|
||||||
(types.file/make-file params :create-page false)))
|
file (types.file/make-file params :create-page false)]
|
||||||
|
(-> state
|
||||||
|
(update ::files assoc (:id file) file)
|
||||||
|
(assoc ::current-file-id (:id file)))))
|
||||||
|
|
||||||
|
(declare close-page)
|
||||||
|
|
||||||
|
(defn close-file
|
||||||
|
[state]
|
||||||
|
(let [state (-> state
|
||||||
|
(close-page)
|
||||||
|
(dissoc ::current-file-id))]
|
||||||
|
state))
|
||||||
|
|
||||||
(defn add-page
|
(defn add-page
|
||||||
[file params]
|
[state params]
|
||||||
(let [page (-> (types.page/make-empty-page params)
|
(let [page (-> (types.page/make-empty-page params)
|
||||||
(types.page/check-page))
|
(types.page/check-page))
|
||||||
change {:type :add-page
|
change {:type :add-page
|
||||||
:page page}]
|
:page page}]
|
||||||
|
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change change)
|
(commit-change change)
|
||||||
|
|
||||||
;; Current page being edited
|
;; Current page being edited
|
||||||
|
@ -203,96 +233,96 @@
|
||||||
;; Last object id added
|
;; Last object id added
|
||||||
(assoc ::last-id nil))))
|
(assoc ::last-id nil))))
|
||||||
|
|
||||||
(defn close-page [file]
|
(defn close-page [state]
|
||||||
(-> file
|
(-> state
|
||||||
(dissoc ::current-page-id)
|
(dissoc ::current-page-id)
|
||||||
(dissoc ::parent-stack)
|
(dissoc ::parent-stack)
|
||||||
(dissoc ::last-id)
|
(dissoc ::last-id)
|
||||||
(clear-names)))
|
(clear-names)))
|
||||||
|
|
||||||
(defn add-artboard
|
(defn add-board
|
||||||
[file data]
|
[state params]
|
||||||
(let [{:keys [id] :as shape}
|
(let [{:keys [id] :as shape}
|
||||||
(-> data
|
(-> params
|
||||||
(update :id default-uuid)
|
(update :id default-uuid)
|
||||||
(assoc :type :frame)
|
(assoc :type :frame)
|
||||||
(assign-name file :frame)
|
(assign-shape-name state)
|
||||||
(types.shape/setup-shape)
|
(types.shape/setup-shape)
|
||||||
(types.shape/check-shape))]
|
(types.shape/check-shape))]
|
||||||
|
|
||||||
(-> file
|
(-> state
|
||||||
(commit-shape shape)
|
(commit-shape shape)
|
||||||
(update ::parent-stack conjv id)
|
(update ::parent-stack conjv id)
|
||||||
(assoc ::current-frame-id id)
|
(assoc ::current-frame-id id)
|
||||||
(assoc ::last-id id))))
|
(assoc ::last-id id))))
|
||||||
|
|
||||||
(defn close-artboard
|
(defn close-board
|
||||||
[file]
|
[state]
|
||||||
(let [parent-id (-> file ::parent-stack peek)
|
(let [parent-id (-> state ::parent-stack peek)
|
||||||
parent (lookup-shape file parent-id)]
|
parent (get-shape state parent-id)]
|
||||||
(-> file
|
(-> state
|
||||||
(assoc ::current-frame-id (or (:frame-id parent) root-id))
|
(assoc ::current-frame-id (or (:frame-id parent) root-id))
|
||||||
(update ::parent-stack pop))))
|
(update ::parent-stack pop))))
|
||||||
|
|
||||||
(defn add-group
|
(defn add-group
|
||||||
[file params]
|
[state params]
|
||||||
(let [{:keys [id] :as shape}
|
(let [{:keys [id] :as shape}
|
||||||
(-> params
|
(-> params
|
||||||
(update :id default-uuid)
|
(update :id default-uuid)
|
||||||
(assoc :type :group)
|
(assoc :type :group)
|
||||||
(assign-name file :group)
|
(assign-shape-name state)
|
||||||
(types.shape/setup-shape)
|
(types.shape/setup-shape)
|
||||||
(types.shape/check-shape))]
|
(types.shape/check-shape))]
|
||||||
(-> file
|
(-> state
|
||||||
(commit-shape shape)
|
(commit-shape shape)
|
||||||
(assoc ::last-id id)
|
(assoc ::last-id id)
|
||||||
(update ::parent-stack conjv id))))
|
(update ::parent-stack conjv id))))
|
||||||
|
|
||||||
(defn close-group
|
(defn close-group
|
||||||
[file]
|
[state]
|
||||||
(let [group-id (-> file :parent-stack peek)
|
(let [group-id (-> state :parent-stack peek)
|
||||||
group (lookup-shape file group-id)
|
group (get-shape state group-id)
|
||||||
children (->> (get group :shapes)
|
children (->> (get group :shapes)
|
||||||
(into [] (keep (partial lookup-shape file)))
|
(into [] (keep (partial get-shape state)))
|
||||||
(not-empty))]
|
(not-empty))]
|
||||||
|
|
||||||
(assert (some? children) "group expect to have at least 1 children")
|
(assert (some? children) "group expect to have at least 1 children")
|
||||||
|
|
||||||
(let [file (if (:masked-group group)
|
(let [state (if (:masked-group group)
|
||||||
(let [mask (first children)
|
(let [mask (first children)
|
||||||
change {:type :mod-obj
|
change {:type :mod-obj
|
||||||
:id group-id
|
:id group-id
|
||||||
:operations
|
:operations
|
||||||
[{:type :set :attr :x :val (-> mask :selrect :x) :ignore-touched true}
|
[{:type :set :attr :x :val (-> mask :selrect :x) :ignore-touched true}
|
||||||
{:type :set :attr :y :val (-> mask :selrect :y) :ignore-touched true}
|
{:type :set :attr :y :val (-> mask :selrect :y) :ignore-touched true}
|
||||||
{:type :set :attr :width :val (-> mask :selrect :width) :ignore-touched true}
|
{:type :set :attr :width :val (-> mask :selrect :width) :ignore-touched true}
|
||||||
{:type :set :attr :height :val (-> mask :selrect :height) :ignore-touched true}
|
{:type :set :attr :height :val (-> mask :selrect :height) :ignore-touched true}
|
||||||
{:type :set :attr :flip-x :val (-> mask :flip-x) :ignore-touched true}
|
{:type :set :attr :flip-x :val (-> mask :flip-x) :ignore-touched true}
|
||||||
{:type :set :attr :flip-y :val (-> mask :flip-y) :ignore-touched true}
|
{:type :set :attr :flip-y :val (-> mask :flip-y) :ignore-touched true}
|
||||||
{:type :set :attr :selrect :val (-> mask :selrect) :ignore-touched true}
|
{:type :set :attr :selrect :val (-> mask :selrect) :ignore-touched true}
|
||||||
{:type :set :attr :points :val (-> mask :points) :ignore-touched true}]}]
|
{:type :set :attr :points :val (-> mask :points) :ignore-touched true}]}]
|
||||||
(commit-change file change :add-container true))
|
(commit-change state change :add-container true))
|
||||||
(let [group (gsh/update-group-selrect group children)
|
(let [group (gsh/update-group-selrect group children)
|
||||||
change {:type :mod-obj
|
change {:type :mod-obj
|
||||||
:id group-id
|
:id group-id
|
||||||
:operations
|
:operations
|
||||||
[{:type :set :attr :selrect :val (:selrect group) :ignore-touched true}
|
[{:type :set :attr :selrect :val (:selrect group) :ignore-touched true}
|
||||||
{:type :set :attr :points :val (:points group) :ignore-touched true}
|
{:type :set :attr :points :val (:points group) :ignore-touched true}
|
||||||
{:type :set :attr :x :val (-> group :selrect :x) :ignore-touched true}
|
{:type :set :attr :x :val (-> group :selrect :x) :ignore-touched true}
|
||||||
{:type :set :attr :y :val (-> group :selrect :y) :ignore-touched true}
|
{:type :set :attr :y :val (-> group :selrect :y) :ignore-touched true}
|
||||||
{:type :set :attr :width :val (-> group :selrect :width) :ignore-touched true}
|
{:type :set :attr :width :val (-> group :selrect :width) :ignore-touched true}
|
||||||
{:type :set :attr :height :val (-> group :selrect :height) :ignore-touched true}]}]
|
{:type :set :attr :height :val (-> group :selrect :height) :ignore-touched true}]}]
|
||||||
|
|
||||||
(commit-change file change :add-container true)))]
|
(commit-change state change :add-container true)))]
|
||||||
(update file ::parent-stack pop))))
|
(update state ::parent-stack pop))))
|
||||||
|
|
||||||
(defn add-bool
|
(defn add-bool
|
||||||
[file params]
|
[state params]
|
||||||
(let [{:keys [group-id type]}
|
(let [{:keys [group-id type]}
|
||||||
(check-add-bool params)
|
(check-add-bool params)
|
||||||
|
|
||||||
group
|
group
|
||||||
(lookup-shape file group-id)
|
(get-shape state group-id)
|
||||||
|
|
||||||
children
|
children
|
||||||
(->> (get group :shapes)
|
(->> (get group :shapes)
|
||||||
|
@ -300,7 +330,7 @@
|
||||||
|
|
||||||
(assert (some? children) "expect group to have at least 1 element")
|
(assert (some? children) "expect group to have at least 1 element")
|
||||||
|
|
||||||
(let [objects (lookup-objects file)
|
(let [objects (get-current-objects state)
|
||||||
bool (-> group
|
bool (-> group
|
||||||
(assoc :type :bool)
|
(assoc :type :bool)
|
||||||
(gsh/update-bool objects))
|
(gsh/update-bool objects))
|
||||||
|
@ -317,101 +347,102 @@
|
||||||
{:type :set :attr :width :val (-> bool :selrect :width) :ignore-touched true}
|
{:type :set :attr :width :val (-> bool :selrect :width) :ignore-touched true}
|
||||||
{:type :set :attr :height :val (-> bool :selrect :height) :ignore-touched true}]}]
|
{:type :set :attr :height :val (-> bool :selrect :height) :ignore-touched true}]}]
|
||||||
|
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change change :add-container true)
|
(commit-change change :add-container true)
|
||||||
(assoc ::last-id group-id)))))
|
(assoc ::last-id group-id)))))
|
||||||
|
|
||||||
(defn add-shape
|
(defn add-shape
|
||||||
[file params]
|
[state params]
|
||||||
(let [obj (-> params
|
(let [obj (-> params
|
||||||
(d/update-when :svg-attrs csvg/attrs->props)
|
(d/update-when :svg-attrs csvg/attrs->props)
|
||||||
(types.shape/setup-shape)
|
(types.shape/setup-shape)
|
||||||
(assign-name file :type))]
|
(assign-shape-name state))]
|
||||||
(-> file
|
(-> state
|
||||||
(commit-shape obj)
|
(commit-shape obj)
|
||||||
(assoc ::last-id (:id obj)))))
|
(assoc ::last-id (:id obj)))))
|
||||||
|
|
||||||
(defn add-library-color
|
(defn add-library-color
|
||||||
[file color]
|
[state color]
|
||||||
(let [color (-> color
|
(let [color (-> color
|
||||||
|
(update :opacity d/nilv 1)
|
||||||
(update :id default-uuid)
|
(update :id default-uuid)
|
||||||
(types.color/check-library-color color))
|
(types.color/check-library-color color))
|
||||||
|
|
||||||
change {:type :add-color
|
change {:type :add-color
|
||||||
:color color}]
|
:color color}]
|
||||||
(-> file
|
|
||||||
|
(-> state
|
||||||
(commit-change change)
|
(commit-change change)
|
||||||
(assoc ::last-id (:id color)))))
|
(assoc ::last-id (:id color)))))
|
||||||
|
|
||||||
(defn add-library-typography
|
(defn add-library-typography
|
||||||
[file typography]
|
[state typography]
|
||||||
(let [typography (-> typography
|
(let [typography (-> typography
|
||||||
(update :id default-uuid)
|
(update :id default-uuid)
|
||||||
(d/without-nils))
|
(d/without-nils))
|
||||||
change {:type :add-typography
|
change {:type :add-typography
|
||||||
:id (:id typography)
|
:id (:id typography)
|
||||||
:typography typography}]
|
:typography typography}]
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change change)
|
(commit-change change)
|
||||||
(assoc ::last-id (:id typography)))))
|
(assoc ::last-id (:id typography)))))
|
||||||
|
|
||||||
(defn add-component
|
(defn add-component
|
||||||
[file params]
|
[state params]
|
||||||
(let [change1 {:type :add-component
|
(let [{:keys [component-id file-id name path]}
|
||||||
:id (or (:id params) (uuid/next))
|
(check-add-component params)
|
||||||
:name (:name params)
|
|
||||||
:path (:path params)
|
|
||||||
:main-instance-id (:main-instance-id params)
|
|
||||||
:main-instance-page (:main-instance-page params)}
|
|
||||||
|
|
||||||
comp-id (get change1 :id)
|
frame-id
|
||||||
|
(get state ::current-frame-id)
|
||||||
change2 {:type :mod-obj
|
|
||||||
:id (:main-instance-id params)
|
|
||||||
:operations
|
|
||||||
[{:type :set :attr :component-root :val true}
|
|
||||||
{:type :set :attr :component-id :val comp-id}
|
|
||||||
{:type :set :attr :component-file :val (:id file)}]}]
|
|
||||||
(-> file
|
|
||||||
(commit-change change1)
|
|
||||||
(commit-change change2)
|
|
||||||
(assoc ::last-id comp-id)
|
|
||||||
(assoc ::current-frame-id comp-id))))
|
|
||||||
|
|
||||||
(defn add-component-instance
|
|
||||||
[{:keys [id data] :as file} params]
|
|
||||||
|
|
||||||
(let [{:keys [component-id x y]}
|
|
||||||
(check-add-component-instance params)
|
|
||||||
|
|
||||||
component
|
|
||||||
(types.components-list/get-component data component-id)
|
|
||||||
|
|
||||||
page-id
|
page-id
|
||||||
(get file ::current-page-id)]
|
(get state ::current-page-id)
|
||||||
|
|
||||||
(assert (uuid? page-id) "page-id is expected to be set")
|
component-id
|
||||||
(assert (uuid? component) "component is expected to exist")
|
(or component-id (uuid/next))
|
||||||
|
|
||||||
;; FIXME: this should be on files and not in pages-list
|
change1
|
||||||
(let [page (types.pages-list/get-page (:data file) page-id)
|
(d/without-nils
|
||||||
pos (gpt/point x y)
|
{:type :add-component
|
||||||
|
:id component-id
|
||||||
|
:name (or name "anonmous")
|
||||||
|
:path path
|
||||||
|
:main-instance-id frame-id
|
||||||
|
:main-instance-page page-id})
|
||||||
|
|
||||||
[shape shapes]
|
change2
|
||||||
(types.container/make-component-instance page component id pos)
|
{:type :mod-obj
|
||||||
|
:id frame-id
|
||||||
|
:operations
|
||||||
|
[{:type :set :attr :component-root :val true}
|
||||||
|
{:type :set :attr :component-id :val component-id}
|
||||||
|
{:type :set :attr :component-file :val file-id}]}]
|
||||||
|
|
||||||
file
|
(-> state
|
||||||
(reduce #(commit-change %1
|
(commit-change change1)
|
||||||
{:type :add-obj
|
(commit-change change2))))
|
||||||
:id (:id %2)
|
|
||||||
:page-id (:id page)
|
|
||||||
:parent-id (:parent-id %2)
|
|
||||||
:frame-id (:frame-id %2)
|
|
||||||
:ignore-touched true
|
|
||||||
:obj %2})
|
|
||||||
file
|
|
||||||
shapes)]
|
|
||||||
|
|
||||||
(assoc file ::last-id (:id shape)))))
|
(defn add-component-instance
|
||||||
|
[state params]
|
||||||
|
|
||||||
|
(let [{:keys [component-id file-id]}
|
||||||
|
(check-add-component-instance params)
|
||||||
|
|
||||||
|
file-id
|
||||||
|
(or file-id (get state ::current-file-id))
|
||||||
|
|
||||||
|
frame-id
|
||||||
|
(get state ::current-frame-id)
|
||||||
|
|
||||||
|
change
|
||||||
|
{:type :mod-obj
|
||||||
|
:id frame-id
|
||||||
|
:operations
|
||||||
|
[{:type :set :attr :component-root :val false}
|
||||||
|
{:type :set :attr :component-id :val component-id}
|
||||||
|
{:type :set :attr :component-file :val file-id}]}]
|
||||||
|
|
||||||
|
(commit-change state change)))
|
||||||
|
|
||||||
(defn delete-shape
|
(defn delete-shape
|
||||||
[file id]
|
[file id]
|
||||||
|
@ -423,10 +454,12 @@
|
||||||
:id id}))
|
:id id}))
|
||||||
|
|
||||||
(defn update-shape
|
(defn update-shape
|
||||||
[file shape-id f]
|
[state shape-id f]
|
||||||
(let [page-id (::current-page-id file)
|
(let [page-id (get state ::current-page-id)
|
||||||
objects (lookup-objects file)
|
|
||||||
|
objects (get-current-objects state)
|
||||||
old-shape (get objects shape-id)
|
old-shape (get objects shape-id)
|
||||||
|
|
||||||
new-shape (f old-shape)
|
new-shape (f old-shape)
|
||||||
attrs (d/concat-set
|
attrs (d/concat-set
|
||||||
(keys old-shape)
|
(keys old-shape)
|
||||||
|
@ -440,7 +473,7 @@
|
||||||
changes
|
changes
|
||||||
(conj changes {:type :set :attr attr :val new-val :ignore-touched true}))))]
|
(conj changes {:type :set :attr attr :val new-val :ignore-touched true}))))]
|
||||||
|
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change
|
(commit-change
|
||||||
{:type :mod-obj
|
{:type :mod-obj
|
||||||
:operations (reduce generate-operation [] attrs)
|
:operations (reduce generate-operation [] attrs)
|
||||||
|
@ -449,12 +482,12 @@
|
||||||
(assoc ::last-id shape-id))))
|
(assoc ::last-id shape-id))))
|
||||||
|
|
||||||
(defn add-guide
|
(defn add-guide
|
||||||
[file guide]
|
[state guide]
|
||||||
(let [guide (cond-> guide
|
(let [guide (cond-> guide
|
||||||
(nil? (:id guide))
|
(nil? (:id guide))
|
||||||
(assoc :id (uuid/next)))
|
(assoc :id (uuid/next)))
|
||||||
page-id (::current-page-id file)]
|
page-id (::current-page-id state)]
|
||||||
(-> file
|
(-> state
|
||||||
(commit-change
|
(commit-change
|
||||||
{:type :set-guide
|
{:type :set-guide
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
|
@ -463,24 +496,54 @@
|
||||||
(assoc ::last-id (:id guide)))))
|
(assoc ::last-id (:id guide)))))
|
||||||
|
|
||||||
(defn delete-guide
|
(defn delete-guide
|
||||||
[file id]
|
[state id]
|
||||||
|
(let [page-id (::current-page-id state)]
|
||||||
(let [page-id (::current-page-id file)]
|
(commit-change state
|
||||||
(commit-change file
|
|
||||||
{:type :set-guide
|
{:type :set-guide
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:id id
|
:id id
|
||||||
:params nil})))
|
:params nil})))
|
||||||
|
|
||||||
(defn update-guide
|
(defn update-guide
|
||||||
[file guide]
|
[state guide]
|
||||||
(let [page-id (::current-page-id file)]
|
(let [page-id (::current-page-id state)]
|
||||||
(commit-change file
|
(commit-change state
|
||||||
{:type :set-guide
|
{:type :set-guide
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:id (:id guide)
|
:id (:id guide)
|
||||||
:params guide})))
|
:params guide})))
|
||||||
|
|
||||||
(defn strip-image-extension [filename]
|
(defrecord BlobWrapper [mtype size blob])
|
||||||
(let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"]
|
|
||||||
(str/replace filename image-extensions-re "")))
|
(defn add-file-media
|
||||||
|
[state params blob]
|
||||||
|
(assert (instance? BlobWrapper blob) "expect blob to be wrapped")
|
||||||
|
|
||||||
|
(let [media-id
|
||||||
|
(uuid/next)
|
||||||
|
|
||||||
|
file-id
|
||||||
|
(get state ::current-file-id)
|
||||||
|
|
||||||
|
{:keys [id width height name]}
|
||||||
|
(-> params
|
||||||
|
(update :id default-uuid)
|
||||||
|
(check-add-file-media params))]
|
||||||
|
|
||||||
|
(-> state
|
||||||
|
(update ::blobs assoc media-id blob)
|
||||||
|
(update ::media assoc media-id
|
||||||
|
{:id media-id
|
||||||
|
:bucket "file-media-object"
|
||||||
|
:content-type (get blob :mtype)
|
||||||
|
:size (get blob :size)})
|
||||||
|
(update ::file-media assoc id
|
||||||
|
{:id id
|
||||||
|
:name name
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:file-id file-id
|
||||||
|
:media-id media-id
|
||||||
|
:mtype (get blob :mtype)})
|
||||||
|
|
||||||
|
(assoc ::last-id id))))
|
||||||
|
|
|
@ -310,12 +310,12 @@
|
||||||
[:add-media
|
[:add-media
|
||||||
[:map {:title "AddMediaChange"}
|
[:map {:title "AddMediaChange"}
|
||||||
[:type [:= :add-media]]
|
[:type [:= :add-media]]
|
||||||
[:object ::ctf/media-object]]]
|
[:object ctf/schema:media]]]
|
||||||
|
|
||||||
[:mod-media
|
[:mod-media
|
||||||
[:map {:title "ModMediaChange"}
|
[:map {:title "ModMediaChange"}
|
||||||
[:type [:= :mod-media]]
|
[:type [:= :mod-media]]
|
||||||
[:object ::ctf/media-object]]]
|
[:object ctf/schema:media]]]
|
||||||
|
|
||||||
[:del-media
|
[:del-media
|
||||||
[:map {:title "DelMediaChange"}
|
[:map {:title "DelMediaChange"}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
(ns app.common.media
|
(ns app.common.media
|
||||||
|
"Media assets helpers (images, fonts, etc)"
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
;; We have added ".ttf" as string to solve a problem with chrome input selector
|
;; We have added ".ttf" as string to solve a problem with chrome input selector
|
||||||
|
@ -48,38 +48,28 @@
|
||||||
(defn mtype->extension [mtype]
|
(defn mtype->extension [mtype]
|
||||||
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
||||||
(case mtype
|
(case mtype
|
||||||
"image/apng" ".apng"
|
"image/apng" ".apng"
|
||||||
"image/avif" ".avif"
|
"image/avif" ".avif"
|
||||||
"image/gif" ".gif"
|
"image/gif" ".gif"
|
||||||
"image/jpeg" ".jpg"
|
"image/jpeg" ".jpg"
|
||||||
"image/png" ".png"
|
"image/png" ".png"
|
||||||
"image/svg+xml" ".svg"
|
"image/svg+xml" ".svg"
|
||||||
"image/webp" ".webp"
|
"image/webp" ".webp"
|
||||||
"application/zip" ".zip"
|
"application/zip" ".zip"
|
||||||
"application/penpot" ".penpot"
|
"application/penpot" ".penpot"
|
||||||
"application/pdf" ".pdf"
|
"application/pdf" ".pdf"
|
||||||
"text/plain" ".txt"
|
"text/plain" ".txt"
|
||||||
|
"font/woff" ".woff"
|
||||||
|
"font/woff2" ".woff2"
|
||||||
|
"font/ttf" ".ttf"
|
||||||
|
"font/otf" ".otf"
|
||||||
|
"application/octet-stream" ".bin"
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(s/def ::id uuid?)
|
(defn strip-image-extension
|
||||||
(s/def ::name string?)
|
[filename]
|
||||||
(s/def ::width number?)
|
(let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"]
|
||||||
(s/def ::height number?)
|
(str/replace filename image-extensions-re "")))
|
||||||
(s/def ::created-at inst?)
|
|
||||||
(s/def ::modified-at inst?)
|
|
||||||
(s/def ::mtype string?)
|
|
||||||
(s/def ::uri string?)
|
|
||||||
|
|
||||||
(s/def ::media-object
|
|
||||||
(s/keys :req-un [::id
|
|
||||||
::name
|
|
||||||
::width
|
|
||||||
::height
|
|
||||||
::mtype
|
|
||||||
::created-at
|
|
||||||
::modified-at
|
|
||||||
::uri]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn parse-font-weight
|
(defn parse-font-weight
|
||||||
[variant]
|
[variant]
|
||||||
|
|
|
@ -54,13 +54,13 @@
|
||||||
::oapi/type "integer"
|
::oapi/type "integer"
|
||||||
::oapi/format "int64"}}))
|
::oapi/format "int64"}}))
|
||||||
|
|
||||||
(def schema:image-color
|
(def schema:image
|
||||||
[:map {:title "ImageColor"}
|
[:map {:title "ImageColor"}
|
||||||
[:name {:optional true} :string]
|
|
||||||
[:width ::sm/int]
|
[:width ::sm/int]
|
||||||
[:height ::sm/int]
|
[:height ::sm/int]
|
||||||
[:mtype {:optional true} [:maybe :string]]
|
[:mtype ::sm/text]
|
||||||
[:id ::sm/uuid]
|
[:id ::sm/uuid]
|
||||||
|
[:name {:optional true} ::sm/text]
|
||||||
[:keep-aspect-ratio {:optional true} :boolean]])
|
[:keep-aspect-ratio {:optional true} :boolean]])
|
||||||
|
|
||||||
(def gradient-types
|
(def gradient-types
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
[:ref-id {:optional true} ::sm/uuid]
|
[:ref-id {:optional true} ::sm/uuid]
|
||||||
[:ref-file {:optional true} ::sm/uuid]
|
[:ref-file {:optional true} ::sm/uuid]
|
||||||
[:gradient {:optional true} [:maybe schema:gradient]]
|
[:gradient {:optional true} [:maybe schema:gradient]]
|
||||||
[:image {:optional true} [:maybe schema:image-color]]
|
[:image {:optional true} [:maybe schema:image]]
|
||||||
[:plugin-data {:optional true} ::ctpg/plugin-data]])
|
[:plugin-data {:optional true} ::ctpg/plugin-data]])
|
||||||
|
|
||||||
(def schema:color
|
(def schema:color
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
||||||
[:color {:optional true} [:maybe schema:rgb-color]]
|
[:color {:optional true} [:maybe schema:rgb-color]]
|
||||||
[:gradient {:optional true} [:maybe schema:gradient]]
|
[:gradient {:optional true} [:maybe schema:gradient]]
|
||||||
[:image {:optional true} [:maybe schema:image-color]]]
|
[:image {:optional true} [:maybe schema:image]]]
|
||||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||||
|
|
||||||
;; Same as color but with :id prop required
|
;; Same as color but with :id prop required
|
||||||
|
@ -115,9 +115,10 @@
|
||||||
(sm/required-keys schema:color-attrs [:id])
|
(sm/required-keys schema:color-attrs [:id])
|
||||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||||
|
|
||||||
|
;; FIXME: revisit if we really need this all registers
|
||||||
(sm/register! ::color schema:color)
|
(sm/register! ::color schema:color)
|
||||||
(sm/register! ::gradient schema:gradient)
|
(sm/register! ::gradient schema:gradient)
|
||||||
(sm/register! ::image-color schema:image-color)
|
(sm/register! ::image-color schema:image)
|
||||||
(sm/register! ::recent-color schema:recent-color)
|
(sm/register! ::recent-color schema:recent-color)
|
||||||
(sm/register! ::color-attrs schema:color-attrs)
|
(sm/register! ::color-attrs schema:color-attrs)
|
||||||
|
|
||||||
|
|
|
@ -38,18 +38,18 @@
|
||||||
|
|
||||||
(def schema:media
|
(def schema:media
|
||||||
"A schema that represents the file media object"
|
"A schema that represents the file media object"
|
||||||
[:map {:title "FileMediaObject"}
|
[:map {:title "FileMedia"}
|
||||||
[:id ::sm/uuid]
|
[:id ::sm/uuid]
|
||||||
[:created-at ::sm/inst]
|
[:created-at {:optional true} ::sm/inst]
|
||||||
[:deleted-at {:optional true} ::sm/inst]
|
[:deleted-at {:optional true} ::sm/inst]
|
||||||
[:name :string]
|
[:name :string]
|
||||||
[:width ::sm/safe-int]
|
[:width ::sm/safe-int]
|
||||||
[:height ::sm/safe-int]
|
[:height ::sm/safe-int]
|
||||||
[:mtype :string]
|
[:mtype :string]
|
||||||
[:file-id {:optional true} ::sm/uuid]
|
|
||||||
[:media-id ::sm/uuid]
|
[:media-id ::sm/uuid]
|
||||||
|
[:file-id {:optional true} ::sm/uuid]
|
||||||
[:thumbnail-id {:optional true} ::sm/uuid]
|
[:thumbnail-id {:optional true} ::sm/uuid]
|
||||||
[:is-local :boolean]])
|
[:is-local {:optional true} :boolean]])
|
||||||
|
|
||||||
(def schema:colors
|
(def schema:colors
|
||||||
[:map-of {:gen/max 5} ::sm/uuid ::ctc/color])
|
[:map-of {:gen/max 5} ::sm/uuid ::ctc/color])
|
||||||
|
@ -102,7 +102,6 @@
|
||||||
(sm/register! ::media schema:media)
|
(sm/register! ::media schema:media)
|
||||||
(sm/register! ::colors schema:colors)
|
(sm/register! ::colors schema:colors)
|
||||||
(sm/register! ::typographies schema:typographies)
|
(sm/register! ::typographies schema:typographies)
|
||||||
(sm/register! ::media-object schema:media)
|
|
||||||
|
|
||||||
(def check-file
|
(def check-file
|
||||||
(sm/check-fn schema:file :hint "check error on validating file"))
|
(sm/check-fn schema:file :hint "check error on validating file"))
|
||||||
|
@ -110,7 +109,7 @@
|
||||||
(def check-file-data
|
(def check-file-data
|
||||||
(sm/check-fn schema:data))
|
(sm/check-fn schema:data))
|
||||||
|
|
||||||
(def check-media-object
|
(def check-file-media
|
||||||
(sm/check-fn schema:media))
|
(sm/check-fn schema:media))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -34,41 +34,35 @@
|
||||||
|
|
||||||
(defn export-files
|
(defn export-files
|
||||||
[files format]
|
[files format]
|
||||||
(dm/assert!
|
(assert (contains? valid-formats format)
|
||||||
"expected valid files param"
|
"expected valid export format")
|
||||||
(check-export-files files))
|
|
||||||
|
|
||||||
(dm/assert!
|
(let [files (check-export-files files)]
|
||||||
"expected valid format"
|
|
||||||
(contains? valid-formats format))
|
|
||||||
|
|
||||||
(ptk/reify ::export-files
|
(ptk/reify ::export-files
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [features (get state :features)
|
(let [team-id (get state :current-team-id)
|
||||||
team-id (:current-team-id state)
|
evname (if (= format :legacy-zip)
|
||||||
evname (if (= format :legacy-zip)
|
"export-standard-files"
|
||||||
"export-standard-files"
|
"export-binary-files")]
|
||||||
"export-binary-files")]
|
(rx/merge
|
||||||
|
(rx/of (ptk/event ::ev/event {::ev/name evname
|
||||||
(rx/merge
|
::ev/origin "dashboard"
|
||||||
(rx/of (ptk/event ::ev/event {::ev/name evname
|
:format format
|
||||||
::ev/origin "dashboard"
|
:num-files (count files)}))
|
||||||
:format format
|
(->> (rx/from files)
|
||||||
:num-files (count files)}))
|
(rx/mapcat
|
||||||
(->> (rx/from files)
|
(fn [file]
|
||||||
(rx/mapcat
|
(->> (rp/cmd! :has-file-libraries {:file-id (:id file)})
|
||||||
(fn [file]
|
(rx/map #(assoc file :has-libraries %)))))
|
||||||
(->> (rp/cmd! :has-file-libraries {:file-id (:id file)})
|
(rx/reduce conj [])
|
||||||
(rx/map #(assoc file :has-libraries %)))))
|
(rx/map (fn [files]
|
||||||
(rx/reduce conj [])
|
(modal/show
|
||||||
(rx/map (fn [files]
|
{:type ::export-files
|
||||||
(modal/show
|
:team-id team-id
|
||||||
{:type ::export-files
|
:files files
|
||||||
:features features
|
:format format}))))))))))
|
||||||
:team-id team-id
|
|
||||||
:files files
|
|
||||||
:format format})))))))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Team Request
|
;; Team Request
|
||||||
|
|
|
@ -254,7 +254,7 @@
|
||||||
|
|
||||||
(defn add-media
|
(defn add-media
|
||||||
[media]
|
[media]
|
||||||
(let [media (ctf/check-media-object media)]
|
(let [media (ctf/check-file-media media)]
|
||||||
(ptk/reify ::add-media
|
(ptk/reify ::add-media
|
||||||
ev/Event
|
ev/Event
|
||||||
(-data [_] media)
|
(-data [_] media)
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.files.builder :as fb]
|
|
||||||
[app.common.files.changes-builder :as pcb]
|
[app.common.files.changes-builder :as pcb]
|
||||||
[app.common.files.shapes-builder :as sb]
|
[app.common.files.shapes-builder :as sb]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
|
[app.common.media :as media]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
|
@ -137,7 +137,7 @@
|
||||||
(= (.-type blob) "image/svg+xml")))
|
(= (.-type blob) "image/svg+xml")))
|
||||||
|
|
||||||
(prepare-blob [blob]
|
(prepare-blob [blob]
|
||||||
(let [name (or name (if (dmm/file? blob) (fb/strip-image-extension (.-name blob)) "blob"))]
|
(let [name (or name (if (dmm/file? blob) (media/strip-image-extension (.-name blob)) "blob"))]
|
||||||
{:file-id file-id
|
{:file-id file-id
|
||||||
:name name
|
:name name
|
||||||
:is-local local?
|
:is-local local?
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
(t/is (some? data))))
|
(t/is (some? data))))
|
||||||
|
|
||||||
(t/testing "Add empty page (only root-frame)"
|
(t/testing "Add empty page (only root-frame)"
|
||||||
(let [page (-> (fb/create-file {:name "Test"})
|
(let [page (-> (fb/create-state)
|
||||||
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-page {:name "Page 1"})
|
||||||
(fb/get-current-page))
|
(fb/get-current-page))
|
||||||
|
|
||||||
|
@ -36,18 +37,19 @@
|
||||||
(t/is (some? data))))
|
(t/is (some? data))))
|
||||||
|
|
||||||
(t/testing "Create simple shape on root"
|
(t/testing "Create simple shape on root"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-shape
|
(fb/add-page {:name "Page 1"})
|
||||||
{:type :rect
|
(fb/add-shape
|
||||||
:x 0
|
{:type :rect
|
||||||
:y 0
|
:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100}))
|
:width 100
|
||||||
page (fb/get-current-page file)
|
:height 100}))
|
||||||
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
|
||||||
result-x (sd/query data (:id page) uuid/zero :x [0 100])]
|
result-x (sd/query data (:id page) uuid/zero :x [0 100])]
|
||||||
|
|
||||||
|
@ -66,17 +68,18 @@
|
||||||
(t/is (= (first (nth result-x 2)) 100))))
|
(t/is (= (first (nth result-x 2)) 100))))
|
||||||
|
|
||||||
(t/testing "Add page with single empty frame"
|
(t/testing "Add page with single empty frame"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard
|
(fb/add-page {:name "Page 1"})
|
||||||
{:x 0
|
(fb/add-board
|
||||||
:y 0
|
{:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100})
|
:width 100
|
||||||
(fb/close-artboard))
|
:height 100})
|
||||||
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
;; frame-id (::fb/last-id file)
|
;; frame-id (::fb/last-id file)
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
|
@ -91,26 +94,27 @@
|
||||||
|
|
||||||
(t/testing "Add page with some shapes inside frames"
|
(t/testing "Add page with some shapes inside frames"
|
||||||
(with-redefs [uuid/next (get-mocked-uuid)]
|
(with-redefs [uuid/next (get-mocked-uuid)]
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard
|
(fb/add-page {:name "Page 1"})
|
||||||
{:x 0
|
(fb/add-board
|
||||||
:y 0
|
{:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100}))
|
:width 100
|
||||||
|
:height 100}))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
|
|
||||||
file (-> file
|
state (-> state
|
||||||
(fb/add-shape
|
(fb/add-shape
|
||||||
{:type :rect
|
{:type :rect
|
||||||
:x 25
|
:x 25
|
||||||
:y 25
|
:y 25
|
||||||
:width 50
|
:width 50
|
||||||
:height 50})
|
:height 50})
|
||||||
(fb/close-artboard))
|
(fb/close-board))
|
||||||
|
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
@ -123,21 +127,21 @@
|
||||||
(t/is (= (count result-frame-x) 5)))))
|
(t/is (= (count result-frame-x) 5)))))
|
||||||
|
|
||||||
(t/testing "Add a global guide"
|
(t/testing "Add a global guide"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-guide {:position 50 :axis :x})
|
(fb/add-page {:name "Page 1"})
|
||||||
(fb/add-artboard {:x 200 :y 200 :width 100 :height 100})
|
(fb/add-guide {:position 50 :axis :x})
|
||||||
(fb/close-artboard))
|
(fb/add-board {:x 200 :y 200 :width 100 :height 100})
|
||||||
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
;; frame-id (::fb/last-id file)
|
data (-> (sd/make-snap-data)
|
||||||
data (-> (sd/make-snap-data)
|
(sd/add-page page))
|
||||||
(sd/add-page page))
|
|
||||||
|
|
||||||
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
result-zero-y (sd/query data (:id page) uuid/zero :y [0 100])
|
result-zero-y (sd/query data (:id page) uuid/zero :y [0 100])
|
||||||
result-frame-x (sd/query data (:id page) frame-id :x [0 100])
|
result-frame-x (sd/query data (:id page) frame-id :x [0 100])
|
||||||
result-frame-y (sd/query data (:id page) frame-id :y [0 100])]
|
result-frame-y (sd/query data (:id page) frame-id :y [0 100])]
|
||||||
|
|
||||||
|
@ -151,17 +155,18 @@
|
||||||
(t/is (= (count result-frame-y) 0))))
|
(t/is (= (count result-frame-y) 0))))
|
||||||
|
|
||||||
(t/testing "Add a frame guide"
|
(t/testing "Add a frame guide"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard {:x 200 :y 200 :width 100 :height 100})
|
(fb/add-page {:name "Page 1"})
|
||||||
(fb/close-artboard))
|
(fb/add-board {:x 200 :y 200 :width 100 :height 100})
|
||||||
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
|
|
||||||
file (-> file
|
state (-> state
|
||||||
(fb/add-guide {:position 50 :axis :x :frame-id frame-id}))
|
(fb/add-guide {:position 50 :axis :x :frame-id frame-id}))
|
||||||
|
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
@ -182,26 +187,26 @@
|
||||||
|
|
||||||
(t/deftest test-update-index
|
(t/deftest test-update-index
|
||||||
(t/testing "Create frame on root and then remove it."
|
(t/testing "Create frame on root and then remove it."
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard
|
(fb/add-page {:name "Page 1"})
|
||||||
{:x 0
|
(fb/add-board
|
||||||
:y 0
|
{:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100})
|
:width 100
|
||||||
(fb/close-artboard))
|
:height 100})
|
||||||
|
(fb/close-board))
|
||||||
|
|
||||||
shape-id (::fb/last-id file)
|
shape-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
;; frame-id (::fb/last-id file)
|
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
|
||||||
file (-> file
|
state (-> state
|
||||||
(fb/delete-shape shape-id))
|
(fb/delete-shape shape-id))
|
||||||
|
|
||||||
new-page (fb/get-current-page file)
|
new-page (fb/get-current-page state)
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
result-x (sd/query data (:id page) uuid/zero :x [0 100])
|
result-x (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
|
@ -212,25 +217,26 @@
|
||||||
(t/is (= (count result-y) 0))))
|
(t/is (= (count result-y) 0))))
|
||||||
|
|
||||||
(t/testing "Create simple shape on root. Then remove it"
|
(t/testing "Create simple shape on root. Then remove it"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-shape
|
(fb/add-page {:name "Page 1"})
|
||||||
{:type :rect
|
(fb/add-shape
|
||||||
:x 0
|
{:type :rect
|
||||||
:y 0
|
:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100}))
|
:width 100
|
||||||
|
:height 100}))
|
||||||
|
|
||||||
shape-id (::fb/last-id file)
|
shape-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
|
|
||||||
;; frame-id (::fb/last-id file)
|
;; frame-id (::fb/last-id state)
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
|
||||||
file (fb/delete-shape file shape-id)
|
state (fb/delete-shape state shape-id)
|
||||||
|
|
||||||
new-page (fb/get-current-page file)
|
new-page (fb/get-current-page state)
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
result-x (sd/query data (:id page) uuid/zero :x [0 100])
|
result-x (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
|
@ -241,30 +247,31 @@
|
||||||
(t/is (= (count result-y) 0))))
|
(t/is (= (count result-y) 0))))
|
||||||
|
|
||||||
(t/testing "Create shape inside frame, then remove it"
|
(t/testing "Create shape inside frame, then remove it"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard
|
(fb/add-page {:name "Page 1"})
|
||||||
{:x 0
|
(fb/add-board
|
||||||
:y 0
|
{:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100}))
|
:width 100
|
||||||
frame-id (::fb/last-id file)
|
:height 100}))
|
||||||
|
frame-id (::fb/last-id state)
|
||||||
|
|
||||||
file (fb/add-shape file {:type :rect :x 25 :y 25 :width 50 :height 50})
|
state (fb/add-shape state {:type :rect :x 25 :y 25 :width 50 :height 50})
|
||||||
shape-id (::fb/last-id file)
|
shape-id (::fb/last-id state)
|
||||||
|
|
||||||
file (fb/close-artboard file)
|
state (fb/close-board state)
|
||||||
|
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
|
||||||
file (fb/delete-shape file shape-id)
|
state (fb/delete-shape state shape-id)
|
||||||
new-page (fb/get-current-page file)
|
new-page (fb/get-current-page state)
|
||||||
|
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
result-frame-x (sd/query data (:id page) frame-id :x [0 100])]
|
result-frame-x (sd/query data (:id page) frame-id :x [0 100])]
|
||||||
|
|
||||||
(t/is (some? data))
|
(t/is (some? data))
|
||||||
|
@ -272,26 +279,28 @@
|
||||||
(t/is (= (count result-frame-x) 3))))
|
(t/is (= (count result-frame-x) 3))))
|
||||||
|
|
||||||
(t/testing "Create global guide then remove it"
|
(t/testing "Create global guide then remove it"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-guide {:position 50 :axis :x}))
|
(fb/add-page {:name "Page 1"})
|
||||||
|
(fb/add-guide {:position 50 :axis :x}))
|
||||||
|
|
||||||
guide-id (::fb/last-id file)
|
guide-id (::fb/last-id state)
|
||||||
|
|
||||||
file (-> (fb/add-artboard file {:x 200 :y 200 :width 100 :height 100})
|
state (-> (fb/add-board state {:x 200 :y 200 :width 100 :height 100})
|
||||||
(fb/close-artboard))
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
data (-> (sd/make-snap-data) (sd/add-page page))
|
data (-> (sd/make-snap-data)
|
||||||
|
(sd/add-page page))
|
||||||
|
|
||||||
new-page (-> (fb/delete-guide file guide-id)
|
new-page (-> (fb/delete-guide state guide-id)
|
||||||
(fb/get-current-page))
|
(fb/get-current-page))
|
||||||
|
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
result-zero-x (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
result-zero-y (sd/query data (:id page) uuid/zero :y [0 100])
|
result-zero-y (sd/query data (:id page) uuid/zero :y [0 100])
|
||||||
result-frame-x (sd/query data (:id page) frame-id :x [0 100])
|
result-frame-x (sd/query data (:id page) frame-id :x [0 100])
|
||||||
result-frame-y (sd/query data (:id page) frame-id :y [0 100])]
|
result-frame-y (sd/query data (:id page) frame-id :y [0 100])]
|
||||||
|
|
||||||
|
@ -305,10 +314,11 @@
|
||||||
(t/is (= (count result-frame-y) 0))))
|
(t/is (= (count result-frame-y) 0))))
|
||||||
|
|
||||||
(t/testing "Create frame guide then remove it"
|
(t/testing "Create frame guide then remove it"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [file (-> (fb/create-state)
|
||||||
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-page {:name "Page 1"})
|
||||||
(fb/add-artboard {:x 200 :y 200 :width 100 :height 100})
|
(fb/add-board {:x 200 :y 200 :width 100 :height 100})
|
||||||
(fb/close-artboard))
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id file)
|
||||||
file (fb/add-guide file {:position 50 :axis :x :frame-id frame-id})
|
file (fb/add-guide file {:position 50 :axis :x :frame-id frame-id})
|
||||||
|
@ -336,30 +346,31 @@
|
||||||
(t/is (= (count result-frame-y) 0))))
|
(t/is (= (count result-frame-y) 0))))
|
||||||
|
|
||||||
(t/testing "Update frame coordinates"
|
(t/testing "Update frame coordinates"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-artboard
|
(fb/add-page {:name "Page 1"})
|
||||||
{:x 0
|
(fb/add-board
|
||||||
:y 0
|
{:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100})
|
:width 100
|
||||||
(fb/close-artboard))
|
:height 100})
|
||||||
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
data (-> (sd/make-snap-data) (sd/add-page page))
|
data (-> (sd/make-snap-data)
|
||||||
|
(sd/add-page page))
|
||||||
|
|
||||||
file (fb/update-shape file frame-id
|
state (fb/update-shape state frame-id
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(-> shape
|
(-> shape
|
||||||
(dissoc :selrect :points)
|
(dissoc :selrect :points)
|
||||||
(assoc :x 200 :y 200)
|
(assoc :x 200 :y 200)
|
||||||
(cts/setup-shape))))
|
(cts/setup-shape))))
|
||||||
|
|
||||||
|
|
||||||
new-page (fb/get-current-page file)
|
new-page (fb/get-current-page state)
|
||||||
|
data (sd/update-page data page new-page)
|
||||||
data (sd/update-page data page new-page)
|
|
||||||
|
|
||||||
result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100])
|
result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
result-frame-x-1 (sd/query data (:id page) frame-id :x [0 100])
|
result-frame-x-1 (sd/query data (:id page) frame-id :x [0 100])
|
||||||
|
@ -373,28 +384,29 @@
|
||||||
(t/is (= (count result-frame-x-2) 3))))
|
(t/is (= (count result-frame-x-2) 3))))
|
||||||
|
|
||||||
(t/testing "Update shape coordinates"
|
(t/testing "Update shape coordinates"
|
||||||
(let [file (-> (fb/create-file {:name "Test"})
|
(let [state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-shape
|
(fb/add-page {:name "Page 1"})
|
||||||
{:type :rect
|
(fb/add-shape
|
||||||
:x 0
|
{:type :rect
|
||||||
:y 0
|
:x 0
|
||||||
:width 100
|
:y 0
|
||||||
:height 100}))
|
:width 100
|
||||||
|
:height 100}))
|
||||||
|
|
||||||
shape-id (::fb/last-id file)
|
shape-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
data (-> (sd/make-snap-data)
|
data (-> (sd/make-snap-data)
|
||||||
(sd/add-page page))
|
(sd/add-page page))
|
||||||
|
|
||||||
file (fb/update-shape file shape-id
|
state (fb/update-shape state shape-id
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(-> shape
|
(-> shape
|
||||||
(dissoc :selrect :points)
|
(dissoc :selrect :points)
|
||||||
(assoc :x 200 :y 200)
|
(assoc :x 200 :y 200)
|
||||||
(cts/setup-shape))))
|
(cts/setup-shape))))
|
||||||
|
|
||||||
new-page (fb/get-current-page file)
|
new-page (fb/get-current-page state)
|
||||||
;; FIXME: update
|
;; FIXME: update
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
|
@ -406,25 +418,26 @@
|
||||||
(t/is (= (count result-zero-x-2) 3))))
|
(t/is (= (count result-zero-x-2) 3))))
|
||||||
|
|
||||||
(t/testing "Update global guide"
|
(t/testing "Update global guide"
|
||||||
(let [guide {:position 50 :axis :x}
|
(let [guide {:position 50 :axis :x}
|
||||||
file (-> (fb/create-file {:name "Test"})
|
state (-> (fb/create-state)
|
||||||
(fb/add-page {:name "Page 1"})
|
(fb/add-file {:name "Test"})
|
||||||
(fb/add-guide guide))
|
(fb/add-page {:name "Page 1"})
|
||||||
|
(fb/add-guide guide))
|
||||||
|
|
||||||
guide-id (::fb/last-id file)
|
guide-id (::fb/last-id state)
|
||||||
guide (assoc guide :id guide-id)
|
guide (assoc guide :id guide-id)
|
||||||
|
|
||||||
file (-> (fb/add-artboard file {:x 500 :y 500 :width 100 :height 100})
|
state (-> (fb/add-board state {:x 500 :y 500 :width 100 :height 100})
|
||||||
(fb/close-artboard))
|
(fb/close-board))
|
||||||
|
|
||||||
frame-id (::fb/last-id file)
|
frame-id (::fb/last-id state)
|
||||||
page (fb/get-current-page file)
|
page (fb/get-current-page state)
|
||||||
data (-> (sd/make-snap-data) (sd/add-page page))
|
data (-> (sd/make-snap-data) (sd/add-page page))
|
||||||
|
|
||||||
new-page (-> (fb/update-guide file (assoc guide :position 150))
|
new-page (-> (fb/update-guide state (assoc guide :position 150))
|
||||||
(fb/get-current-page))
|
(fb/get-current-page))
|
||||||
|
|
||||||
data (sd/update-page data page new-page)
|
data (sd/update-page data page new-page)
|
||||||
|
|
||||||
result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100])
|
result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100])
|
||||||
result-zero-y-1 (sd/query data (:id page) uuid/zero :y [0 100])
|
result-zero-y-1 (sd/query data (:id page) uuid/zero :y [0 100])
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",
|
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",
|
||||||
"luxon": "^3.6.1",
|
"luxon": "^3.6.1",
|
||||||
"sax": "^1.4.1",
|
|
||||||
"source-map-support": "^0.5.21"
|
"source-map-support": "^0.5.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
library/playground/sample.jpg
Normal file
BIN
library/playground/sample.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -1,47 +1,87 @@
|
||||||
import * as penpot from "../target/library/penpot.js";
|
import * as penpot from "../target/library/penpot.js";
|
||||||
import { writeFile } from 'fs/promises';
|
import { writeFile, readFile } from 'fs/promises';
|
||||||
import { createWriteStream } from 'fs';
|
import { createWriteStream } from 'fs';
|
||||||
import { Writable } from "stream";
|
import { Writable } from "stream";
|
||||||
|
|
||||||
console.log(penpot);
|
// console.log(penpot);
|
||||||
|
|
||||||
(async function() {
|
(async function() {
|
||||||
const file = penpot.createFile({name: "Test"});
|
const context = penpot.createBuildContext();
|
||||||
|
|
||||||
file.addPage({name: "Foo Page"})
|
|
||||||
const boardId = file.addArtboard({name: "Foo Board"})
|
|
||||||
const rectId = file.addRect({name: "Foo Rect", width:100, height: 200})
|
|
||||||
|
|
||||||
file.addLibraryColor({color: "#fabada", opacity: 0.5})
|
|
||||||
|
|
||||||
// console.log("created board", boardId);
|
|
||||||
// console.log("created rect", rectId);
|
|
||||||
|
|
||||||
// const board = file.getShape(boardId);
|
|
||||||
// console.log("=========== BOARD =============")
|
|
||||||
// console.dir(board, {depth: 10});
|
|
||||||
|
|
||||||
// const rect = file.getShape(rectId);
|
|
||||||
// console.log("=========== RECT =============")
|
|
||||||
// console.dir(rect, {depth: 10});
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let result = await penpot.exportAsBytes(file)
|
context.addFile({name: "Test File 1"});
|
||||||
|
context.addPage({name: "Foo Page"})
|
||||||
|
|
||||||
|
// Add image media
|
||||||
|
const buffer = await readFile("./playground/sample.jpg");
|
||||||
|
const blob = new Blob([buffer], { type: 'image/jpeg' });
|
||||||
|
|
||||||
|
const mediaId = context.addFileMedia({
|
||||||
|
name: "avatar.jpg",
|
||||||
|
width: 512,
|
||||||
|
height: 512
|
||||||
|
}, blob);
|
||||||
|
|
||||||
|
// Add image color asset
|
||||||
|
const assetColorId = context.addLibraryColor({
|
||||||
|
name: "Avatar",
|
||||||
|
opacity: 1,
|
||||||
|
image: {
|
||||||
|
...context.getMediaAsImage(mediaId),
|
||||||
|
keepAspectRatio: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const boardId = context.addBoard({
|
||||||
|
name: "Foo Board",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 500,
|
||||||
|
height: 300,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fill = {
|
||||||
|
fillColorRefId: assetColorId,
|
||||||
|
fillColorRefFile: context.currentFileId,
|
||||||
|
fillImage: {
|
||||||
|
...context.getMediaAsImage(mediaId),
|
||||||
|
keepAspectRatio: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context.addRect({
|
||||||
|
name: "Rect 1",
|
||||||
|
x: 20,
|
||||||
|
y: 20,
|
||||||
|
width:100,
|
||||||
|
height:200,
|
||||||
|
fills: [fill]
|
||||||
|
});
|
||||||
|
|
||||||
|
context.closeBoard();
|
||||||
|
context.closeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let result = await penpot.exportAsBytes(context)
|
||||||
await writeFile("sample-sync.zip", result);
|
await writeFile("sample-sync.zip", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// {
|
||||||
// Create a file stream to write the zip to
|
// // Create a file stream to write the zip to
|
||||||
const output = createWriteStream('sample-stream.zip');
|
// const output = createWriteStream('sample-stream.zip');
|
||||||
|
// // Wrap Node's stream in a WHATWG WritableStream
|
||||||
// Wrap Node's stream in a WHATWG WritableStream
|
// const writable = Writable.toWeb(output);
|
||||||
const writable = Writable.toWeb(output);
|
// await penpot.exportStream(context, writable);
|
||||||
|
// }
|
||||||
await penpot.exportStream(file, writable);
|
|
||||||
}
|
|
||||||
|
|
||||||
})().catch((cause) => {
|
})().catch((cause) => {
|
||||||
console.log(cause);
|
console.error(cause);
|
||||||
|
|
||||||
|
const innerCause = cause.cause;
|
||||||
|
if (innerCause) {
|
||||||
|
console.error("Inner cause:", innerCause);
|
||||||
|
}
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
:modules
|
:modules
|
||||||
{:penpot
|
{:penpot
|
||||||
{:exports {BuilderError lib.builder/BuilderError
|
{:exports {BuilderError lib.builder/BuilderError
|
||||||
createFile lib.builder/create-file
|
createBuildContext lib.builder/create-build-context
|
||||||
exportAsBytes lib.export/export-bytes
|
exportAsBytes lib.export/export-bytes
|
||||||
exportAsBlob lib.export/export-blob
|
exportAsBlob lib.export/export-blob
|
||||||
exportStream lib.export/export-stream
|
exportStream lib.export/export-stream
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
:js-options
|
:js-options
|
||||||
{:entry-keys ["module" "browser" "main"]
|
{:entry-keys ["module" "browser" "main"]
|
||||||
:export-conditions ["module" "import", "browser" "require" "default"]
|
:export-conditions ["module" "import", "browser" "require" "default"]
|
||||||
:js-provider :import
|
;; :js-provider :import
|
||||||
;; :external-index "target/library/dependencies.js"
|
;; :external-index "target/library/dependencies.js"
|
||||||
;; :external-index-format :esm
|
;; :external-index-format :esm
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,201 +55,235 @@
|
||||||
(defn- decode-params
|
(defn- decode-params
|
||||||
[params]
|
[params]
|
||||||
(if (obj/plain-object? params)
|
(if (obj/plain-object? params)
|
||||||
(json/->js params)
|
(json/->clj params)
|
||||||
params))
|
params))
|
||||||
|
|
||||||
(defn- create-file-api
|
(defn- get-current-page-id
|
||||||
[file]
|
[state]
|
||||||
(let [state* (volatile! file)
|
(dm/str (get state ::fb/current-page-id)))
|
||||||
api (obj/reify {:name "File"}
|
|
||||||
:id
|
|
||||||
{:get #(dm/str (:id @state*))}
|
|
||||||
|
|
||||||
:currentFrameId
|
(defn- get-last-id
|
||||||
{:get #(dm/str (::fb/current-frame-id @state*))}
|
[state]
|
||||||
|
(dm/str (get state ::fb/last-id)))
|
||||||
|
|
||||||
:currentPageId
|
(defn- create-builder-api
|
||||||
{:get #(dm/str (::fb/current-page-id @state*))}
|
[state]
|
||||||
|
(obj/reify {:name "File"}
|
||||||
|
:currentFileId
|
||||||
|
{:get #(dm/str (get @state ::fb/current-file-id))}
|
||||||
|
|
||||||
:lastId
|
:currentFrameId
|
||||||
{:get #(dm/str (::fb/last-id @state*))}
|
{:get #(dm/str (get @state ::fb/current-frame-id))}
|
||||||
|
|
||||||
:addPage
|
:currentPageId
|
||||||
(fn [params]
|
{:get #(get-current-page-id @state)}
|
||||||
(try
|
|
||||||
(let [params (-> params
|
|
||||||
(decode-params)
|
|
||||||
(fb/decode-page))]
|
|
||||||
(vswap! state* fb/add-page params)
|
|
||||||
(dm/str (::fb/current-page-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:closePage
|
:lastId
|
||||||
(fn []
|
{:get #(get-last-id @state)}
|
||||||
(vswap! state* fb/close-page))
|
|
||||||
|
|
||||||
:addArtboard
|
:addFile
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> params decode-params fb/decode-file)]
|
||||||
(json/->clj)
|
(-> (swap! state fb/add-file params)
|
||||||
(assoc :type :frame)
|
(get ::fb/current-file-id)))
|
||||||
(fb/decode-shape))]
|
(catch :default cause
|
||||||
(vswap! state* fb/add-artboard params)
|
(handle-exception cause))))
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:closeArtboard
|
:closeFile
|
||||||
(fn []
|
(fn []
|
||||||
(vswap! state* fb/close-artboard))
|
(swap! state fb/close-file)
|
||||||
|
nil)
|
||||||
|
|
||||||
:addGroup
|
:addPage
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(fb/decode-page))]
|
||||||
(assoc :type :group)
|
|
||||||
(fb/decode-shape))]
|
|
||||||
(vswap! state* fb/add-group params)
|
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:closeGroup
|
(-> (swap! state fb/add-page params)
|
||||||
(fn []
|
(get-current-page-id)))
|
||||||
(vswap! state* fb/close-group))
|
|
||||||
|
|
||||||
:addBool
|
(catch :default cause
|
||||||
(fn [params]
|
(handle-exception cause))))
|
||||||
(try
|
|
||||||
(let [params (-> params
|
|
||||||
(json/->clj)
|
|
||||||
(fb/decode-add-bool))]
|
|
||||||
(vswap! state* fb/add-bool params)
|
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addRect
|
:closePage
|
||||||
(fn [params]
|
(fn []
|
||||||
(try
|
(swap! state fb/close-page)
|
||||||
(let [params (-> params
|
nil)
|
||||||
(json/->clj)
|
|
||||||
(assoc :type :rect)
|
|
||||||
(fb/decode-shape))]
|
|
||||||
(vswap! state* fb/add-shape params)
|
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addCircle
|
:addBoard
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(assoc :type :frame)
|
||||||
(assoc :type :circle)
|
(fb/decode-shape))]
|
||||||
(fb/decode-shape))]
|
(-> (swap! state fb/add-board params)
|
||||||
(vswap! state* fb/add-shape params)
|
(get-last-id)))
|
||||||
(dm/str (::fb/last-id @state*)))
|
(catch :default cause
|
||||||
(catch :default cause
|
(handle-exception cause))))
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addPath
|
:closeBoard
|
||||||
(fn [params]
|
(fn []
|
||||||
(try
|
(swap! state fb/close-board)
|
||||||
(let [params (-> params
|
nil)
|
||||||
(json/->clj)
|
|
||||||
(assoc :type :path)
|
|
||||||
(fb/decode-shape))]
|
|
||||||
(vswap! state* fb/add-shape params)
|
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addText
|
:addGroup
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(assoc :type :group)
|
||||||
(assoc :type :text)
|
(fb/decode-shape))]
|
||||||
(fb/decode-shape))]
|
(-> (swap! state fb/add-group params)
|
||||||
(vswap! state* fb/add-shape params)
|
(get-last-id)))
|
||||||
(dm/str (::fb/last-id @state*)))
|
(catch :default cause
|
||||||
(catch :default cause
|
(handle-exception cause))))
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addLibraryColor
|
:closeGroup
|
||||||
(fn [params]
|
(fn []
|
||||||
(try
|
(swap! state fb/close-group)
|
||||||
(let [params (-> params
|
nil)
|
||||||
(json/->clj)
|
|
||||||
(fb/decode-library-color)
|
|
||||||
(d/without-nils))]
|
|
||||||
(vswap! state* fb/add-library-color params)
|
|
||||||
(dm/str (::fb/last-id @state*)))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addLibraryTypography
|
:addBool
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(fb/decode-add-bool))]
|
||||||
(fb/decode-library-typography)
|
(-> (swap! state fb/add-bool params)
|
||||||
(d/without-nils))]
|
(get-last-id)))
|
||||||
(vswap! state* fb/add-library-typography params)
|
(catch :default cause
|
||||||
(dm/str (::fb/last-id @state*)))
|
(handle-exception cause))))
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addComponent
|
:addRect
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(assoc :type :rect)
|
||||||
(fb/decode-component)
|
(fb/decode-shape))]
|
||||||
(d/without-nils))]
|
(-> (swap! state fb/add-shape params)
|
||||||
(vswap! state* fb/add-component params)
|
(get-last-id)))
|
||||||
(dm/str (::fb/last-id @state*)))
|
(catch :default cause
|
||||||
(catch :default cause
|
(handle-exception cause))))
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:addComponentInstance
|
:addCircle
|
||||||
(fn [params]
|
(fn [params]
|
||||||
(try
|
(try
|
||||||
(let [params (-> params
|
(let [params (-> (decode-params params)
|
||||||
(json/->clj)
|
(assoc :type :circle)
|
||||||
(fb/decode-add-component-instance)
|
(fb/decode-shape))]
|
||||||
(d/without-nils))]
|
(-> (swap! state fb/add-shape params)
|
||||||
(vswap! state* fb/add-component-instance params)
|
(get-last-id)))
|
||||||
(dm/str (::fb/last-id @state*)))
|
(catch :default cause
|
||||||
(catch :default cause
|
(handle-exception cause))))
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
||||||
:getShape
|
:addPath
|
||||||
(fn [shape-id]
|
(fn [params]
|
||||||
(let [shape-id (uuid/parse shape-id)]
|
(try
|
||||||
(some-> (fb/lookup-shape @state* shape-id)
|
(let [params (-> (decode-params params)
|
||||||
(json/->js))))
|
(assoc :type :path)
|
||||||
|
(fb/decode-shape))]
|
||||||
|
(-> (swap! state fb/add-shape params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
:toMap
|
:addText
|
||||||
(fn []
|
(fn [params]
|
||||||
(-> @state*
|
(try
|
||||||
(d/without-qualified)
|
(let [params (-> (decode-params params)
|
||||||
(json/->js))))]
|
(assoc :type :text)
|
||||||
|
(fb/decode-shape))]
|
||||||
|
(-> (swap! state fb/add-shape params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:addLibraryColor
|
||||||
|
(fn [params]
|
||||||
|
(try
|
||||||
|
(let [params (-> (decode-params params)
|
||||||
|
(fb/decode-library-color)
|
||||||
|
(d/without-nils))]
|
||||||
|
(-> (swap! state fb/add-library-color params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:addLibraryTypography
|
||||||
|
(fn [params]
|
||||||
|
(try
|
||||||
|
(let [params (-> (decode-params params)
|
||||||
|
(fb/decode-library-typography)
|
||||||
|
(d/without-nils))]
|
||||||
|
(-> (swap! state fb/add-library-typography params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:addComponent
|
||||||
|
(fn [params]
|
||||||
|
(try
|
||||||
|
(let [params (-> (decode-params params)
|
||||||
|
(fb/decode-add-component))]
|
||||||
|
(-> (swap! state fb/add-component params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:addComponentInstance
|
||||||
|
(fn [params]
|
||||||
|
(try
|
||||||
|
(let [params (-> (decode-params params)
|
||||||
|
(fb/decode-add-component-instance))]
|
||||||
|
(-> (swap! state fb/add-component-instance params)
|
||||||
|
(get-last-id)))
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:addFileMedia
|
||||||
|
(fn [params blob]
|
||||||
|
|
||||||
|
(when-not (instance? js/Blob blob)
|
||||||
|
(throw (BuilderError. "validation"
|
||||||
|
"invalid-media"
|
||||||
|
"only Blob instance are soported")))
|
||||||
|
(try
|
||||||
|
(let [blob (fb/map->BlobWrapper
|
||||||
|
{:size (.-size ^js blob)
|
||||||
|
:mtype (.-type ^js blob)
|
||||||
|
:blob blob})
|
||||||
|
params
|
||||||
|
(-> (decode-params params)
|
||||||
|
(fb/decode-add-file-media))]
|
||||||
|
|
||||||
|
(-> (swap! state fb/add-file-media params blob)
|
||||||
|
(get-last-id)))
|
||||||
|
|
||||||
|
(catch :default cause
|
||||||
|
(handle-exception cause))))
|
||||||
|
|
||||||
|
:getMediaAsImage
|
||||||
|
(fn [id]
|
||||||
|
(let [id (uuid/parse id)]
|
||||||
|
(when-let [fmedia (get-in @state [::fb/file-media id])]
|
||||||
|
(let [image {:id (get fmedia :id)
|
||||||
|
:width (get fmedia :width)
|
||||||
|
:height (get fmedia :height)
|
||||||
|
:name (get fmedia :name)
|
||||||
|
:mtype (get fmedia :mtype)}]
|
||||||
|
(json/->js (d/without-nils image))))))
|
||||||
|
|
||||||
|
:genId
|
||||||
|
(fn []
|
||||||
|
(dm/str (uuid/next)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn create-build-context
|
||||||
|
"Create an empty builder state context."
|
||||||
|
[]
|
||||||
|
(let [state (atom {})
|
||||||
|
api (create-builder-api state)]
|
||||||
|
|
||||||
(specify! api
|
(specify! api
|
||||||
cljs.core/IDeref
|
cljs.core/IDeref
|
||||||
(-deref [_]
|
(-deref [_] @state))))
|
||||||
(d/without-qualified @state*)))))
|
|
||||||
|
|
||||||
(defn create-file
|
|
||||||
[params]
|
|
||||||
(try
|
|
||||||
(let [params (-> params json/->clj fb/decode-file)
|
|
||||||
file (fb/create-file params)]
|
|
||||||
(create-file-api file))
|
|
||||||
(catch :default cause
|
|
||||||
(handle-exception cause))))
|
|
||||||
|
|
|
@ -8,12 +8,10 @@
|
||||||
"A .penpot export implementation"
|
"A .penpot export implementation"
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
|
||||||
[app.common.files.builder :as fb]
|
[app.common.files.builder :as fb]
|
||||||
[app.common.json :as json]
|
[app.common.json :as json]
|
||||||
|
[app.common.media :as media]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.common.types.color :as types.color]
|
[app.common.types.color :as types.color]
|
||||||
[app.common.types.component :as types.component]
|
[app.common.types.component :as types.component]
|
||||||
[app.common.types.file :as types.file]
|
[app.common.types.file :as types.file]
|
||||||
|
@ -22,8 +20,8 @@
|
||||||
[app.common.types.shape :as types.shape]
|
[app.common.types.shape :as types.shape]
|
||||||
[app.common.types.tokens-lib :as types.tokens-lib]
|
[app.common.types.tokens-lib :as types.tokens-lib]
|
||||||
[app.common.types.typography :as types.typography]
|
[app.common.types.typography :as types.typography]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[app.util.zip :as zip]
|
[app.util.zip :as zip]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[promesa.core :as p]))
|
[promesa.core :as p]))
|
||||||
|
|
||||||
(def ^:private schema:file
|
(def ^:private schema:file
|
||||||
|
@ -43,9 +41,6 @@
|
||||||
(def ^:private encode-component
|
(def ^:private encode-component
|
||||||
(sm/encoder types.component/schema:component sm/json-transformer))
|
(sm/encoder types.component/schema:component sm/json-transformer))
|
||||||
|
|
||||||
;; (def encode-media
|
|
||||||
;; (sm/encoder ::ctf/media sm/json-transformer))
|
|
||||||
|
|
||||||
(def encode-color
|
(def encode-color
|
||||||
(sm/encoder types.color/schema:color sm/json-transformer))
|
(sm/encoder types.color/schema:color sm/json-transformer))
|
||||||
|
|
||||||
|
@ -68,7 +63,6 @@
|
||||||
"file-data-fragment"
|
"file-data-fragment"
|
||||||
"file-change"})
|
"file-change"})
|
||||||
|
|
||||||
;; FIXME: move to types
|
|
||||||
(def ^:private schema:storage-object
|
(def ^:private schema:storage-object
|
||||||
[:map {:title "StorageObject"}
|
[:map {:title "StorageObject"}
|
||||||
[:id ::sm/uuid]
|
[:id ::sm/uuid]
|
||||||
|
@ -80,12 +74,6 @@
|
||||||
(def encode-storage-object
|
(def encode-storage-object
|
||||||
(sm/encoder schema:storage-object sm/json-transformer))
|
(sm/encoder schema:storage-object sm/json-transformer))
|
||||||
|
|
||||||
;; (def encode-file-thumbnail
|
|
||||||
;; (sm/encoder schema:file-thumbnail sm/json-transformer))
|
|
||||||
|
|
||||||
|
|
||||||
;; FIXME: naming
|
|
||||||
|
|
||||||
(def ^:private file-attrs
|
(def ^:private file-attrs
|
||||||
#{:id
|
#{:id
|
||||||
:name
|
:name
|
||||||
|
@ -96,8 +84,6 @@
|
||||||
|
|
||||||
(defn- generate-file-export-procs
|
(defn- generate-file-export-procs
|
||||||
[{:keys [id data] :as file}]
|
[{:keys [id data] :as file}]
|
||||||
;; (prn "generate-file-export-procs")
|
|
||||||
;; (app.common.pprint/pprint file)
|
|
||||||
(cons
|
(cons
|
||||||
(let [file (cond-> (select-keys file file-attrs)
|
(let [file (cond-> (select-keys file file-attrs)
|
||||||
(:options data)
|
(:options data)
|
||||||
|
@ -108,11 +94,11 @@
|
||||||
(concat
|
(concat
|
||||||
(let [pages (get data :pages)
|
(let [pages (get data :pages)
|
||||||
pages-index (get data :pages-index)]
|
pages-index (get data :pages-index)]
|
||||||
|
|
||||||
(->> (d/enumerate pages)
|
(->> (d/enumerate pages)
|
||||||
(mapcat
|
(mapcat
|
||||||
(fn [[index page-id]]
|
(fn [[index page-id]]
|
||||||
(let [path (str "files/" id "/pages/" page-id ".json")
|
(let [page (get pages-index page-id)
|
||||||
page (get pages-index page-id)
|
|
||||||
objects (:objects page)
|
objects (:objects page)
|
||||||
page (-> page
|
page (-> page
|
||||||
(dissoc :objects)
|
(dissoc :objects)
|
||||||
|
@ -155,45 +141,74 @@
|
||||||
(when-let [tokens-lib (get data :tokens-lib)]
|
(when-let [tokens-lib (get data :tokens-lib)]
|
||||||
(list [(str "files/" id "/tokens.json")
|
(list [(str "files/" id "/tokens.json")
|
||||||
(delay (-> tokens-lib
|
(delay (-> tokens-lib
|
||||||
(encode-tokens-lib tokens-lib)
|
encode-tokens-lib
|
||||||
json/encode))])))))
|
json/encode))])))))
|
||||||
|
|
||||||
(defn generate-manifest-procs
|
(defn- generate-files-export-procs
|
||||||
[file]
|
[state]
|
||||||
(let [mdata {:id (:id file)
|
(->> (vals (get state ::fb/files))
|
||||||
:name (:name file)
|
(mapcat generate-file-export-procs)))
|
||||||
:features (:features file)}
|
|
||||||
|
(defn- generate-media-export-procs
|
||||||
|
[state]
|
||||||
|
(->> (get state ::fb/file-media)
|
||||||
|
(mapcat (fn [[file-media-id file-media]]
|
||||||
|
(let [media-id (get file-media :media-id)
|
||||||
|
media (get-in state [::fb/media media-id])
|
||||||
|
blob (get-in state [::fb/blobs media-id])]
|
||||||
|
(list
|
||||||
|
[(str "objects/" media-id (media/mtype->extension (:content-type media)))
|
||||||
|
(delay (get blob :blob))]
|
||||||
|
|
||||||
|
[(str "objects/" media-id ".json")
|
||||||
|
(delay (-> media
|
||||||
|
;; FIXME: proper encode?
|
||||||
|
(json/encode)))]
|
||||||
|
[(str "files/" (:file-id file-media) "/media/" file-media-id ".json")
|
||||||
|
(delay (-> file-media
|
||||||
|
(dissoc :file-id)
|
||||||
|
(json/encode)))]))))))
|
||||||
|
|
||||||
|
(defn- generate-manifest-procs
|
||||||
|
[state]
|
||||||
|
(let [files (->> (get state ::fb/files)
|
||||||
|
(mapv (fn [[file-id file]]
|
||||||
|
{:id file-id
|
||||||
|
:name (:name file)
|
||||||
|
:features (:features file)})))
|
||||||
params {:type "penpot/export-files"
|
params {:type "penpot/export-files"
|
||||||
:version 1
|
:version 1
|
||||||
|
;; FIXME: set proper placeholder for replacement on build
|
||||||
:generated-by "penpot-lib/develop"
|
:generated-by "penpot-lib/develop"
|
||||||
:files [mdata]
|
:files files
|
||||||
:relations []}]
|
:relations []}]
|
||||||
(list
|
["manifest.json" (delay (json/encode params))]))
|
||||||
["manifest.json" (delay (json/encode params))])))
|
|
||||||
|
|
||||||
(defn- export
|
(defn- export
|
||||||
[file writer]
|
[state writer]
|
||||||
(->> (p/reduce (fn [writer [path proc]]
|
(->> (p/reduce (fn [writer [path data]]
|
||||||
(let [data (deref proc)]
|
(let [data (if (delay? data) (deref data) data)]
|
||||||
(js/console.log "export" path)
|
(js/console.log "export" path)
|
||||||
(->> (zip/add writer path data)
|
(->> (zip/add writer path data)
|
||||||
(p/fmap (constantly writer)))))
|
(p/fmap (constantly writer)))))
|
||||||
|
|
||||||
writer
|
writer
|
||||||
(concat
|
(cons (generate-manifest-procs @state)
|
||||||
(generate-manifest-procs @file)
|
(concat
|
||||||
(generate-file-export-procs @file)))
|
(generate-files-export-procs @state)
|
||||||
|
(generate-media-export-procs @state))))
|
||||||
|
|
||||||
(p/mcat (fn [writer]
|
(p/mcat (fn [writer]
|
||||||
(zip/close writer)))))
|
(zip/close writer)))))
|
||||||
|
|
||||||
(defn export-bytes
|
(defn export-bytes
|
||||||
[file]
|
[state]
|
||||||
(export file (zip/writer (zip/bytes-writer))))
|
(export state (zip/writer (zip/bytes-writer))))
|
||||||
|
|
||||||
(defn export-blob
|
(defn export-blob
|
||||||
[file]
|
[state]
|
||||||
(export file (zip/writer (zip/blob-writer))))
|
(export state (zip/writer (zip/blob-writer))))
|
||||||
|
|
||||||
(defn export-stream
|
(defn export-stream
|
||||||
[file stream]
|
[state stream]
|
||||||
(export file (zip/writer stream)))
|
(export state (zip/writer stream)))
|
||||||
|
|
|
@ -59,7 +59,6 @@ __metadata:
|
||||||
concurrently: "npm:^9.1.2"
|
concurrently: "npm:^9.1.2"
|
||||||
luxon: "npm:^3.6.1"
|
luxon: "npm:^3.6.1"
|
||||||
nodemon: "npm:^3.1.9"
|
nodemon: "npm:^3.1.9"
|
||||||
sax: "npm:^1.4.1"
|
|
||||||
shadow-cljs: "npm:3.0.5"
|
shadow-cljs: "npm:3.0.5"
|
||||||
source-map-support: "npm:^0.5.21"
|
source-map-support: "npm:^0.5.21"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
@ -73,11 +72,11 @@ __metadata:
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:^22.12.0":
|
"@types/node@npm:^22.12.0":
|
||||||
version: 22.15.17
|
version: 22.15.18
|
||||||
resolution: "@types/node@npm:22.15.17"
|
resolution: "@types/node@npm:22.15.18"
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: "npm:~6.21.0"
|
undici-types: "npm:~6.21.0"
|
||||||
checksum: 10c0/fb92aa10b628683c5b965749f955bc2322485ecb0ea6c2f4cae5f2c7537a16834607e67083a9e9281faaae8d7dee9ada8d6a5c0de9a52c17d82912ef00c0fdd4
|
checksum: 10c0/e23178c568e2dc6b93b6aa3b8dfb45f9556e527918c947fe7406a4c92d2184c7396558912400c3b1b8d0fa952ec63819aca2b8e4d3545455fc6f1e9623e09ca6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -318,14 +317,14 @@ __metadata:
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
|
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
|
||||||
version: 4.4.0
|
version: 4.4.1
|
||||||
resolution: "debug@npm:4.4.0"
|
resolution: "debug@npm:4.4.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: "npm:^2.1.3"
|
ms: "npm:^2.1.3"
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de
|
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -962,13 +961,6 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"sax@npm:^1.4.1":
|
|
||||||
version: 1.4.1
|
|
||||||
resolution: "sax@npm:1.4.1"
|
|
||||||
checksum: 10c0/6bf86318a254c5d898ede6bd3ded15daf68ae08a5495a2739564eb265cd13bcc64a07ab466fb204f67ce472bb534eb8612dac587435515169593f4fffa11de7c
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"semver@npm:^7.3.5, semver@npm:^7.5.3":
|
"semver@npm:^7.3.5, semver@npm:^7.5.3":
|
||||||
version: 7.7.2
|
version: 7.7.2
|
||||||
resolution: "semver@npm:7.7.2"
|
resolution: "semver@npm:7.7.2"
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fmt:clj:check": "cljfmt check --parallel=true common/src/ common/test/ frontend/src/ frontend/test/ backend/src/ backend/test/ exporter/src/",
|
"fmt:clj:check": "cljfmt check --parallel=true common/src/ common/test/ frontend/src/ frontend/test/ backend/src/ backend/test/ exporter/src/ library/src",
|
||||||
"fmt:clj": "cljfmt fix --parallel=true common/src/ common/test/ frontend/src/ frontend/test/ backend/src/ backend/test/ exporter/src/",
|
"fmt:clj": "cljfmt fix --parallel=true common/src/ common/test/ frontend/src/ frontend/test/ backend/src/ backend/test/ exporter/src/ library/src",
|
||||||
"lint:clj:common": "clj-kondo --parallel=true --lint common/src",
|
"lint:clj:common": "clj-kondo --parallel=true --lint common/src",
|
||||||
"lint:clj:frontend": "clj-kondo --parallel=true --lint frontend/src",
|
"lint:clj:frontend": "clj-kondo --parallel=true --lint frontend/src",
|
||||||
"lint:clj:backend": "clj-kondo --parallel=true --lint backend/src",
|
"lint:clj:backend": "clj-kondo --parallel=true --lint backend/src",
|
||||||
"lint:clj:exporter": "clj-kondo --parallel=true --lint exporter/src",
|
"lint:clj:exporter": "clj-kondo --parallel=true --lint exporter/src",
|
||||||
"lint:clj": "yarn run lint:clj:common && yarn run lint:clj:frontend && yarn run lint:clj:backend && yarn run lint:clj:exporter"
|
"lint:clj:library": "clj-kondo --parallel=true --lint library/src",
|
||||||
|
"lint:clj": "yarn run lint:clj:common && yarn run lint:clj:frontend && yarn run lint:clj:backend && yarn run lint:clj:exporter && yarn run lint:clj:library"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.43.1",
|
"@playwright/test": "^1.43.1",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue