Compare commits

..

11 Commits

Author SHA1 Message Date
Aitor
1299074bd9 wip 2024-01-02 09:11:50 +01:00
Aitor
6b50c17781 wip 2023-12-20 13:29:29 +01:00
Aitor
e08e2bf64a wip 2023-12-19 14:35:57 +01:00
Alejandro Alonso
77f07f9751 Basic multiple fill support 2023-12-18 13:37:56 +01:00
Aitor
189e6b31d0 wip 2023-12-18 13:18:32 +01:00
Alejandro Alonso
4690945f59 WIP 2023-12-18 12:17:15 +01:00
Aitor
f0d0529f58 wip 2023-12-18 12:02:46 +01:00
Aitor
8525508c11 wip 2023-12-18 11:11:46 +01:00
Alejandro Alonso
eaa9aec8bb Read default shader from file 2023-12-18 11:01:50 +01:00
Aitor
23adf483ff wip 2023-12-18 10:38:26 +01:00
Aitor
bcf01abdbe wip 2023-12-18 10:08:42 +01:00
332 changed files with 4452 additions and 9441 deletions

View File

@@ -15,7 +15,7 @@
:hooks
{:analyze-call
{app.common.data.macros/export hooks.export/export
potok.v2.core/reify hooks.export/potok-reify
potok.core/reify hooks.export/potok-reify
app.util.services/defmethod hooks.export/service-defmethod
app.common.record/defrecord hooks.export/penpot-defrecord
app.db/with-atomic hooks.export/penpot-with-atomic

View File

@@ -1,17 +1,5 @@
# CHANGELOG
## :rocket: Next
### :boom: Breaking changes & Deprecations
### :sparkles: New features
### :bug: Bugs fixed
### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!)
## 1.20.0
### :boom: Breaking changes & Deprecations

View File

@@ -30,6 +30,7 @@
<Logger name="app.redis" level="info" />
<Logger name="app.rpc.rlimit" level="info" />
<Logger name="app.rpc.climit" level="info" />
<Logger name="app.rpc.mutations.files" level="info" />
<Logger name="app.common.files.migrations" level="info" />
<Logger name="app.loggers" level="debug" additivity="false">

View File

@@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.files.changes :as cp]
[app.common.files.changes-builder :as fcb]
[app.common.files.helpers :as cfh]
@@ -768,58 +769,58 @@
fdata (migrate-graphics fdata)]
(update fdata :options assoc :components-v2 true)))))
(defn- get-file
[system id]
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
(-> (files/get-file system id :migrate? false)
(update :data assoc :id id)
(update :data fdata/process-pointers deref)
(fmg/migrate-file))))
(defn- validate-file!
[file libs throw-on-validate?]
(try
(cfv/validate-file! file libs)
(cfv/validate-file-schema! file)
(catch Throwable cause
(if throw-on-validate?
(throw cause)
(l/wrn :hint "migrate:file:validation-error" :cause cause)))))
(defn- process-fdata
[fdata id]
(-> fdata
(assoc :id id)
(fdata/process-pointers deref)
(fmg/migrate-data)))
(defn- process-file
[{:keys [::db/conn] :as system} id & {:keys [validate? throw-on-validate?]}]
(let [file (get-file system id)
(binding [pmap/*tracked* (pmap/create-tracked)
pmap/*load-fn* (partial fdata/load-pointer *system* id)]
libs (->> (files/get-file-libraries conn id)
(into [file] (comp (map :id) (map (partial get-file system))))
(d/index-by :id))
(let [file (binding [cfeat/*new* (atom #{})]
(-> (files/get-file system id :migrate? false)
(update :data process-fdata id)
(update :features into (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features)))
file (-> file
(update :data migrate-fdata libs)
(update :features conj "components/v2"))
libs (->> (files/get-file-libraries conn id)
(into [file] (map (fn [{:keys [id]}]
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
(-> (files/get-file system id :migrate? false)
(update :data process-fdata id))))))
(d/index-by :id))
_ (when validate?
(validate-file! file libs throw-on-validate?))
pmap? (contains? (:features file) "fdata/pointer-map")
file (if (contains? (:features file) "fdata/objects-map")
(fdata/enable-objects-map file)
file)
file (-> file
(update :data migrate-fdata libs)
(update :features conj "components/v2")
(cond-> pmap? (fdata/enable-pointer-map)))
]
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (fdata/enable-pointer-map file)]
(fdata/persist-pointers! system id)
file))
file)]
(when validate?
(if throw-on-validate?
(cfv/validate-file! file libs)
(doseq [error (cfv/validate-file file libs)]
(l/wrn :hint "migrate:file:validation-error"
:file-id (str (:id file))
:file-name (:name file)
:error error))))
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" (:features file))
:revn (:revn file)}
{:id (:id file)}
{::db/return-keys? false})
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" (:features file))
:revn (:revn file)}
{:id (:id file)})
(dissoc file :data)))
(when pmap?
(fdata/persist-pointers! system id))
(dissoc file :data))))
(defn migrate-file!
[system file-id & {:keys [validate? throw-on-validate?]}]

View File

@@ -81,7 +81,6 @@
(cond
(or (= code :spec-validation)
(= code :params-validation)
(= code :schema-validation)
(= code :data-validation))
(let [explain (ex/explain data)]
{::rres/status 400

View File

@@ -11,7 +11,7 @@
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.files.defaults :as cfd]
[app.common.files.migrations :as fmg]
[app.common.files.migrations :as pmg]
[app.common.files.validate :as fval]
[app.common.fressian :as fres]
[app.common.logging :as l]
@@ -693,6 +693,17 @@
(vswap! *state* update :index update-index files)
(vswap! *state* assoc :version version :files files)))
(defn- postprocess-file
[file]
(cond-> file
(and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map)
(and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map)))
(defn- get-remaped-thumbnails
[thumbnails file-id]
(mapv (fn [thumbnail]
@@ -701,31 +712,6 @@
(update :object-id #(str/replace-first % #"^(.*?)/" (str file-id "/")))))
thumbnails))
(defn- process-file
[{:keys [id] :as file}]
(-> file
(update :data (fn [fdata]
(-> fdata
(assoc :id id)
(dissoc :recent-colors)
(cond-> (> (:version fdata) cfd/version)
(assoc :version cfd/version))
;; FIXME: We're temporarily activating all
;; migrations because a problem in the
;; environments messed up with the version
;; numbers When this problem is fixed delete
;; the following line
(cond-> (> (:version fdata) 22)
(assoc :version 22)))))
(fmg/migrate-file)
(update :data (fn [fdata]
(-> fdata
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils))))))
(defmethod read-section :v1/files
[{:keys [::db/conn ::input ::project-id ::enabled-features ::timestamp ::overwrite?] :as system}]
@@ -736,7 +722,19 @@
file-id (:id file)
file-id' (lookup-index file-id)
thumbnails (:thumbnails file)]
thumbnails (:thumbnails file)
file (update file :features cfeat/migrate-legacy-features)
features (-> enabled-features
(set/difference cfeat/frontend-only-features)
(set/union (cfeat/check-supported-features! (:features file))))]
;; All features that are enabled and requires explicit migration
;; are added to the state for a posterior migration step
(doseq [feature (-> enabled-features
(set/difference cfeat/no-migration-features)
(set/difference (:features file)))]
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
(when (not= file-id expected-file-id)
(ex/raise :type :validation
@@ -767,62 +765,63 @@
(l/dbg :hint "update media references" ::l/sync? true)
(vswap! *state* update :media into (map #(update % :id lookup-index)) media))
(let [file (-> file
(assoc :id file-id')
(process-file))
(binding [cfeat/*current* features
cfeat/*previous* (:features file)
pmap/*tracked* (atom {})]
;; All features that are enabled and requires explicit migration are
;; added to the state for a posterior migration step.
_ (doseq [feature (-> enabled-features
(set/difference cfeat/no-migration-features)
(set/difference (:features file)))]
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
(let [params (-> file
(assoc :id file-id')
(assoc :features features)
(assoc :project-id project-id)
(assoc :created-at timestamp)
(assoc :modified-at timestamp)
(dissoc :thumbnails)
(update :data (fn [data]
(-> data
(dissoc :recent-colors)
(assoc :id file-id')
(cond-> (> (:version data) cfd/version)
(assoc :version cfd/version))
file (-> file
(assoc :project-id project-id)
(assoc :created-at timestamp)
(assoc :modified-at timestamp)
(dissoc :thumbnails)
(update :features
(fn [features]
(let [features (cfeat/check-supported-features! features)]
(-> enabled-features
(set/difference cfeat/frontend-only-features)
(set/union features))))))
;; FIXME: We're temporarily activating all
;; migrations because a problem in the
;; environments messed up with the version
;; numbers When this problem is fixed delete
;; the following line
(assoc :version 22)
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(pmg/migrate-data)
(d/without-nils)))))
_ (when (contains? cf/flags :file-schema-validation)
(fval/validate-file-schema! file))
params (if (contains? cf/flags :file-schema-validation)
(fval/validate-file-schema! params)
params)
_ (when (contains? cf/flags :soft-file-schema-validation)
(let [result (ex/try! (fval/validate-file-schema! file))]
(when (ex/exception? result)
(l/error :hint "file schema validation error" :cause result))))
_ (when (contains? cf/flags :soft-file-schema-validation)
(try
(fval/validate-file-schema! params)
(catch Throwable cause
(l/error :hint "file schema validation error" :cause cause))))
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! system file-id')
file))
file)
file (-> file
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))]
params (-> params
(postprocess-file)
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))]
(l/dbg :hint "create file" :id (str file-id') ::l/sync? true)
(if overwrite?
(create-or-update-file! conn file)
(db/insert! conn :file file))
(create-or-update-file! conn params)
(db/insert! conn :file params))
(feat.fdata/persist-pointers! system file-id')
(when overwrite?
(db/delete! conn :file-thumbnail {:file-id file-id'}))
file-id'))))
file-id')))))
(defmethod read-section :v1/rels
[{:keys [::db/conn ::input ::timestamp]}]

View File

@@ -34,6 +34,7 @@
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
@@ -224,9 +225,9 @@
(defn- migrate-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)]
pmap/*tracked* (pmap/create-tracked)
cfeat/*new* (atom #{})]
(let [file (fmg/migrate-file file)]
;; NOTE: when file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
@@ -234,26 +235,23 @@
;; NOTE: the following code will not work on read-only mode, it
;; is a known issue; we keep is not implemented until we really
;; need this
(when (fmg/migrated? file)
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" (:features file))}
{:id id}
{::db/return-keys? false})
(when (contains? (:features file) "fdata/pointer-map")
(feat.fdata/persist-pointers! cfg id)))
file)))
(if (fmg/migrated? file)
(let [file (update file :features cfeat/migrate-legacy-features)
features (set/union (deref cfeat/*new*) (:features file))]
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" features)}
{:id id})
(feat.fdata/persist-pointers! cfg id)
(assoc file :features features))
file))))
(defn get-file
[{:keys [::db/conn] :as cfg} id & {:keys [project-id
migrate?
[{:keys [::db/conn] :as cfg} id & {:keys [project-id migrate?
include-deleted?
lock-for-update?]
:or {include-deleted? false
lock-for-update? false
migrate? true}}]
lock-for-update? false}}]
(dm/assert!
"expected cfg with valid connection"
(db/connection-map? cfg))
@@ -708,12 +706,11 @@
(cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file) (:features params)))
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
{:name (:name file)
:components-count (count (ctkl/components-seq (:data file)))
:graphics-count (count (get-in file [:data :media] []))
:colors-count (count (get-in file [:data :colors] []))
:typography-count (count (get-in file [:data :typographies] []))})))
{:name (:name file)
:components-count (count (ctkl/components-seq (:data file)))
:graphics-count (count (get-in file [:data :media] []))
:colors-count (count (get-in file [:data :colors] []))
:typography-count (count (get-in file [:data :typographies] []))}))
(sv/defmethod ::get-file-summary
"Retrieve a file summary by its ID. Only authenticated users."
@@ -859,10 +856,8 @@
(true? (:is-shared params)))
(let [file (assoc file :is-shared true)]
(db/update! conn :file
{:is-shared true}
{:id id}
::db/return-keys? false)
{:is-shared false}
{:id id})
file)
:else

View File

@@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.features :as cfeat]
[app.common.files.helpers :as cfh]
[app.common.files.migrations :as fmg]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.thumbnails :as thc]
@@ -106,12 +105,24 @@
(letfn [;; function responsible on finding the frame marked to be
;; used as thumbnail; the returned frame always have
;; the :page-id set to the page that it belongs.
(get-thumbnail-frame [{:keys [data]}]
(d/seek #(or (:use-for-thumbnail %)
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
(for [page (-> data :pages-index vals)
frame (-> page :objects ctt/get-frames)]
(assoc frame :page-id (:id page)))))
(get-thumbnail-frame [file]
;; NOTE: this is a hack for avoid perform blocking
;; operation inside the for loop, clojure lazy-seq uses
;; synchronized blocks that does not plays well with
;; virtual threads where all rpc methods calls are
;; dispatched, so we need to perform the load operation
;; first. This operation forces all pointer maps load into
;; the memory.
;;
;; FIXME: this is no longer true with clojure>=1.12
(let [{:keys [data]} (update file :data feat.fdata/process-pointers pmap/load!)]
;; Then proceed to find the frame set for thumbnail
(d/seek #(or (:use-for-thumbnail %)
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
(for [page (-> data :pages-index vals)
frame (-> page :objects ctt/get-frames)]
(assoc frame :page-id (:id page))))))
;; function responsible to filter objects data structure of
;; all unneeded shapes if a concrete frame is provided. If no
@@ -155,29 +166,30 @@
objects)))]
(let [frame (get-thumbnail-frame file)
frame-id (:id frame)
page-id (or (:page-id frame)
(-> data :pages first))
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(let [frame (get-thumbnail-frame file)
frame-id (:id frame)
page-id (or (:page-id frame)
(-> data :pages first))
page (dm/get-in data [:pages-index page-id])
page (cond-> page (pmap/pointer-map? page) deref)
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
page (dm/get-in data [:pages-index page-id])
page (cond-> page (pmap/pointer-map? page) deref)
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
thumbs (get-object-thumbnails conn id obj-ids)]
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
thumbs (get-object-thumbnails conn id obj-ids)]
(cond-> page
;; If we have frame, we need to specify it on the page level
;; and remove the all other unrelated objects.
(some? frame-id)
(-> (assoc :thumbnail-frame-id frame-id)
(update :objects filter-objects frame-id))
(cond-> page
;; If we have frame, we need to specify it on the page level
;; and remove the all other unrelated objects.
(some? frame-id)
(-> (assoc :thumbnail-frame-id frame-id)
(update :objects filter-objects frame-id))
;; Assoc the available thumbnails and prune not visible shapes
;; for avoid transfer unnecessary data.
:always
(update :objects assoc-thumbnails page-id thumbs)))))
;; Assoc the available thumbnails and prune not visible shapes
;; for avoid transfer unnecessary data.
:always
(update :objects assoc-thumbnails page-id thumbs))))))
(def ^:private
schema:get-file-data-for-thumbnail
@@ -209,10 +221,7 @@
:profile-id profile-id
:file-id file-id)
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(-> (files/get-file cfg file-id :migrate? false)
(update :data feat.fdata/process-pointers deref)
(fmg/migrate-file)))]
file (files/get-file cfg file-id)]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))

View File

@@ -182,39 +182,39 @@
(defn update-file
[{:keys [::db/conn ::mtx/metrics] :as cfg}
{:keys [id file features changes changes-with-metadata] :as params}]
(let [features (-> features
(set/difference cfeat/frontend-only-features)
(set/union (:features file)))
(binding [cfeat/*current* features
cfeat/*previous* (:features file)]
(let [update-fn (cond-> update-file*
(contains? features "fdata/pointer-map")
(wrap-with-pointer-map-context)
update-fn (cond-> update-file*
(contains? features "fdata/pointer-map")
(wrap-with-pointer-map-context)
(contains? features "fdata/objects-map")
(wrap-with-objects-map-context))
(contains? features "fdata/objects-map")
(wrap-with-objects-map-context))
changes (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec)
(vec changes))
changes (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec)
(vec changes))]
features (-> features
(set/difference cfeat/frontend-only-features)
(set/union (:features file)))]
(when (> (:revn params)
(:revn file))
(ex/raise :type :validation
:code :revn-conflict
:hint "The incoming revision number is greater that stored version."
:context {:incoming-revn (:revn params)
:stored-revn (:revn file)}))
(when (> (:revn params)
(:revn file))
(ex/raise :type :validation
:code :revn-conflict
:hint "The incoming revision number is greater that stored version."
:context {:incoming-revn (:revn params)
:stored-revn (:revn file)}))
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
(when (not= features (:features file))
(let [features (db/create-array conn "text" features)]
(db/update! conn :file
{:features features}
{:id id})))
(when (not= features (:features file))
(let [features (db/create-array conn "text" features)]
(db/update! conn :file
{:features features}
{:id id})))
(binding [cfeat/*current* features
cfeat/*previous* (:features file)]
(let [file (assoc file :features features)
params (-> params
(assoc :file file)
@@ -276,7 +276,9 @@
(try
(val/validate-file-schema! file)
(catch Throwable cause
(l/error :hint "file schema validation error" :cause cause))))
(l/error :hint "file schema validation error" :cause cause)))
file)
(defn- soft-validate-file!
[file libs]
@@ -284,7 +286,8 @@
(val/validate-file! file libs)
(catch Throwable cause
(l/error :hint "file validation error"
:cause cause))))
:cause cause)))
file)
(defn- update-file-data
[{:keys [::db/conn] :as cfg} file changes skip-validate]
@@ -297,8 +300,7 @@
;; WARNING: this ruins performance; maybe we need to find
;; some other way to do general validation
libs (when (and (or (contains? cf/flags :file-validation)
(contains? cf/flags :soft-file-validation))
libs (when (and (contains? cf/flags :file-validation)
(not skip-validate))
(->> (files/get-file-libraries conn (:id file))
(into [file] (map (fn [{:keys [id]}]
@@ -307,37 +309,37 @@
(-> (files/get-file cfg id :migrate? false)
(feat.fdata/process-pointers deref) ; ensure all pointers resolved
(fmg/migrate-file))))))
(d/index-by :id)))
(d/index-by :id)))]
file (-> (files/check-version! file)
(update :revn inc)
(update :data cpc/process-changes changes))]
(-> (files/check-version! file)
(update :revn inc)
(update :data cpc/process-changes changes)
(when (contains? cf/flags :soft-file-validation)
(soft-validate-file! file libs))
;; If `libs` is defined, then full validation is performed
(cond-> (contains? cf/flags :soft-file-validation)
(soft-validate-file! libs))
(when (contains? cf/flags :soft-file-schema-validation)
(soft-validate-file-schema! file))
(cond-> (contains? cf/flags :soft-file-schema-validation)
(soft-validate-file-schema!))
(when (and (contains? cf/flags :file-validation)
(not skip-validate))
(val/validate-file! file libs))
(cond-> (and (contains? cf/flags :file-validation)
(not skip-validate))
(val/validate-file! libs))
(when (and (contains? cf/flags :file-schema-validation)
(not skip-validate))
(val/validate-file-schema! file))
(cond-> (and (contains? cf/flags :file-schema-validation)
(not skip-validate))
(val/validate-file-schema!))
(cond-> file
(and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map)
(cond-> (and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map))
(and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map)
(cond-> (and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map))
(update :data blob/encode))))
:always
(update :data blob/encode))))
(defn- take-snapshot?
"Defines the rule when file `data` snapshot should be saved."

View File

@@ -13,7 +13,6 @@
[app.common.files.migrations :as pmg]
[app.common.schema :as sm]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.features.fdata :as feat.fdata]
[app.http.sse :as sse]
@@ -106,34 +105,27 @@
media
media))
(process-file [{:keys [id] :as file}]
(-> file
(update :data assoc :id id)
(update :data feat.fdata/process-pointers deref)
(pmg/migrate-file)
(update :data (fn [data]
(-> data
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils))))))]
(update-fdata [fdata new-id]
(-> fdata
(assoc :id new-id)
(pmg/migrate-data)
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils)
(feat.fdata/process-pointers pmap/clone)))]
(let [new-id (get index id)
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(-> (assoc file :id new-id)
(process-file)))
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! cfg (:id file))
file))
file)]
file)))
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)
cfeat/*new* (atom #{})]
(let [new-id (get index id)
file (-> file
(assoc :id new-id)
(update :data update-fdata new-id)
(update :features into (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features))]
(feat.fdata/persist-pointers! cfg new-id)
file))))
(def sql:get-used-libraries
"select flr.*
@@ -198,22 +190,20 @@
(db/insert! conn :file
(-> file
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))
{::db/return-keys? false})
(update :data blob/encode)))
(db/insert! conn :file-profile-rel
{:file-id (:id file)
:profile-id profile-id
:is-owner true
:is-admin true
:can-edit true}
{::db/return-keys? false})
:can-edit true})
(doseq [params flibs]
(db/insert! conn :file-library-rel params ::db/return-keys? false))
(db/insert! conn :file-library-rel params))
(doseq [params fmeds]
(db/insert! conn :file-media-object params ::db/return-keys? false))
(db/insert! conn :file-media-object params))
file))
@@ -293,7 +283,7 @@
;; --- COMMAND: Move file
(def sql:get-files
"select id, features, project_id from file where id = ANY(?)")
"select id, project_id from file where id = ANY(?)")
(def sql:move-files
"update file set project_id = ? where id = ANY(?)")
@@ -317,8 +307,7 @@
[{:keys [::db/conn] :as cfg} {:keys [profile-id ids project-id] :as params}]
(let [fids (db/create-array conn "uuid" ids)
files (->> (db/exec! conn [sql:get-files fids])
(map files/decode-row))
files (db/exec! conn [sql:get-files fids])
source (into #{} (map :project-id) files)
pids (->> (conj source project-id)
(db/create-array conn "uuid"))]
@@ -338,12 +327,7 @@
;; Check the team compatibility
(let [orig-team (teams/get-team conn :profile-id profile-id :project-id (first source))
dest-team (teams/get-team conn :profile-id profile-id :project-id project-id)]
(cfeat/check-teams-compatibility! orig-team dest-team)
;; Check if all pending to move files are compaib
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
(doseq [file files]
(cfeat/check-file-features! features (:features file)))))
(cfeat/check-teams-compatibility! orig-team dest-team))
;; move all files to the project
(db/exec-one! conn [sql:move-files project-id fids])
@@ -400,15 +384,7 @@
;; Check the teams compatibility
(let [orig-team (teams/get-team conn :profile-id profile-id :team-id (:team-id project))
dest-team (teams/get-team conn :profile-id profile-id :team-id team-id)]
(cfeat/check-teams-compatibility! orig-team dest-team)
;; Check if all pending to move files are compaib
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
(doseq [file (->> (db/query conn :file
{:project-id project-id}
{:columns [:features]})
(map files/decode-row))]
(cfeat/check-file-features! features (:features file)))))
(cfeat/check-teams-compatibility! orig-team dest-team))
;; move project to the destination team
(db/update! conn :project

View File

@@ -9,7 +9,6 @@
[app.common.spec :as us]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :refer [resolve-public-uri]]
[app.rpc.doc :as-alias doc]
[app.util.services :as sv]
[clojure.spec.alpha :as s]))
@@ -38,15 +37,12 @@
)
select distinct
f.id,
f.revn,
f.project_id,
f.created_at,
f.modified_at,
f.name,
f.is_shared,
ft.media_id
f.is_shared
from file as f
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn)
inner join projects as pr on (f.project_id = pr.id)
where f.name ilike ('%' || ? || '%')
and (f.deleted_at is null or f.deleted_at > now())
@@ -54,16 +50,10 @@
(defn search-files
[conn profile-id team-id search-term]
(->> (db/exec! conn [sql:search-files
profile-id team-id
profile-id team-id
search-term])
(mapv (fn [row]
(if-let [media-id (:media-id row)]
(-> row
(dissoc :media-id)
(assoc :thumbnail-uri (resolve-public-uri media-id)))
(dissoc row :media-id))))))
(db/exec! conn [sql:search-files
profile-id team-id
profile-id team-id
search-term]))
(s/def ::team-id ::us/uuid)
(s/def ::search-files ::us/string)

View File

@@ -105,7 +105,7 @@
(pmg/migrate-file))))))
(d/index-by :id))
errors (validate/validate-file file libs)
changes (repair/repair-file file libs errors)
changes (-> (repair/repair-file (:data file) libs errors) :redo-changes)
file (-> file
(update :revn inc)

View File

@@ -103,7 +103,7 @@
{:on-open
(fn on-open [channel]
(l/dbg :fn "on-open" :conn-id (str id))
(l/trace :fn "on-open" :conn-id id :channel channel)
(let [options (-> options
(assoc ::channel channel)
(on-connect))
@@ -114,10 +114,10 @@
:on-close
(fn on-close [_channel code reason]
(l/dbg :fn "on-close"
:conn-id (str id)
:code code
:reason reason)
(l/info :fn "on-ws-terminate"
:conn-id id
:code code
:reason reason)
(sp/close! close-ch))
:on-error
@@ -132,19 +132,18 @@
:on-pong
(fn on-pong [_channel data]
(l/trace :fn "on-pong" :data data)
(sp/put! hbeat-ch data))}))
(defn- handle-ping!
[{:keys [::id ::beats ::channel] :as wsp} beat-id]
(l/trc :hint "send ping" :beat beat-id :conn-id (str id))
(l/trace :hint "send ping" :beat beat-id :conn-id id)
(rws/ping channel (encode-beat beat-id))
(let [issued (swap! beats conj (long beat-id))]
(not (>= (count issued) max-missed-heartbeats))))
(defn- start-io-loop!
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch
::channel ::handler ::beats ::on-rcv-message ::on-snd-message]
:as wsp}]
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch ::channel ::handler ::beats ::on-rcv-message ::on-snd-message] :as wsp}]
(try
(handler wsp {:type :open})
(loop [i 0]
@@ -155,16 +154,14 @@
(identical? p ping-ch)
(if (handle-ping! wsp i)
(recur (inc i))
(do
(l/trc :hint "closing" :reason "missing to many pings")
(rws/close channel 8802 "missing to many pings")))
(rws/close channel 8802 "missing to many pings"))
(or (identical? p close-ch) (nil? msg))
(do :nothing)
(identical? p heartbeat-ch)
(let [beat (decode-beat msg)]
(l/trc :hint "pong received" :beat beat :conn-id (str id))
;; (l/trace :hint "pong" :beat beat :conn-id id)
(swap! beats disj beat)
(recur i))
@@ -182,6 +179,7 @@
(identical? p output-ch)
(let [message (on-snd-message msg)
message (t/encode-str message {:type :json-verbose})]
;; (l/trace :hint "writing message to output" :message msg)
(rws/send channel message)
(recur i))))))
@@ -190,12 +188,12 @@
(catch java.io.IOException _)
(catch InterruptedException _cause
(l/dbg :hint "websocket thread interrumpted" :conn-id id))
(l/debug :hint "websocket thread interrumpted" :conn-id id))
(catch Throwable cause
(l/err :hint "unhandled exception on websocket thread"
:conn-id id
:cause cause))
(l/error :hint "unhandled exception on websocket thread"
:conn-id id
:cause cause))
(finally
(try
(handler wsp {:type :close})
@@ -214,4 +212,4 @@
(catch Throwable cause
(throw cause)))
(l/trc :hint "websocket thread terminated" :conn-id id))))
(l/trace :hint "websocket thread terminated" :conn-id id))))

View File

@@ -69,12 +69,12 @@
(t/is (not= (:id file1) (:id result)))
;; Check that the new file has a correct file library relation
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
(t/is (= 1 (count rows)))
(t/is (= (:id file2) (:library-file-id item))))
;; Check that the new file has a correct file media objects
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
(t/is (= 1 (count rows)))
;; Check that both items have different ids
@@ -91,7 +91,7 @@
(t/is (not (contains? (get-in result [:data :media]) (:id mobj)))))
;; Check the total number of files
(let [rows (th/db-query :file {:project-id (:id project)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
(t/is (= 3 (count rows))))))))
(t/deftest duplicate-file-with-deleted-relations
@@ -139,15 +139,15 @@
(t/is (not= (:id file1) (:id result)))
;; Check that there are no relation to a deleted library
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
(t/is (= 0 (count rows))))
;; Check that the new file has no media objects
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
(t/is (= 0 (count rows))))
;; Check the total number of files
(let [rows (th/db-query :file {:project-id (:id project)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
(t/is (= 3 (count rows))))))))
(t/deftest duplicate-project
@@ -196,16 +196,16 @@
(t/is (not= (:id project) (:id result)))
;; Check the total number of projects (previously is 2, now is 3)
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
(t/is (= 3 (count rows))))
;; Check that the new project has the same files
(let [p1-files (th/db-query :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (th/db-query :file
{:project-id (:id result)}
{:order-by [:name]})]
(let [p1-files (db/query th/*pool* :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (db/query th/*pool* :file
{:project-id (:id result)}
{:order-by [:name]})]
(t/is (= (count p1-files)
(count p2-files)))
@@ -260,16 +260,16 @@
(t/is (not= (:id project) (:id result)))
;; Check the total number of projects (previously is 2, now is 3)
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
(t/is (= 3 (count rows))))
;; Check that the new project has only the second file
(let [p1-files (th/db-query :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (th/db-query :file
{:project-id (:id result)}
{:order-by [:name]})]
(let [p1-files (db/query th/*pool* :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (db/query th/*pool* :file
{:project-id (:id result)}
{:order-by [:name]})]
(t/is (= (count (rest p1-files))
(count p2-files)))
@@ -318,11 +318,11 @@
(t/is (th/ex-of-code? error :cant-move-to-same-project)))
;; initially project1 should have 2 files
(let [rows (th/db-query :file {:project-id (:id project1)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; initially project2 should be empty
(let [rows (th/db-query :file {:project-id (:id project2)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; move a file1 to project2 (in the same team)
@@ -337,21 +337,21 @@
(t/is (nil? (:result out)))
;; project1 now should contain 1 file
(let [rows (th/db-query :file {:project-id (:id project1)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(t/is (= 1 (count rows))))
;; project2 now should contain 1 file
(let [rows (th/db-query :file {:project-id (:id project2)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 1 (count rows))))
;; file1 should be still linked to file2
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; should be no libraries on file2
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows)))))))
@@ -384,27 +384,27 @@
;; --- initial data checks
;; the project1 should have 3 files
(let [rows (th/db-query :file {:project-id (:id project1)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(t/is (= 3 (count rows))))
;; should be no files on project2
(let [rows (th/db-query :file {:project-id (:id project2)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; the file1 should be linked to file2
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; the file2 should be linked to file3
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file2)))
(t/is (= (:library-file-id item) (:id file3))))
;; should be no libraries on file3
(let [rows (th/db-query :file-library-rel {:file-id (:id file3)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
(t/is (= 0 (count rows))))
;; move to other project in other team
@@ -418,23 +418,23 @@
(t/is (nil? (:result out)))
;; project1 now should have 2 file
(let [[item1 item2 :as rows] (th/db-query :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
;; (clojure.pprint/pprint rows)
(t/is (= 2 (count rows)))
(t/is (= (:id item1) (:id file2))))
;; project2 now should have 1 file
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file1))))
;; the moved file1 should not have any link to libraries
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(t/is (zero? (count rows))))
;; the file2 should still be linked to file3
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file2)))
(t/is (= (:library-file-id item) (:id file3)))))))
@@ -462,21 +462,21 @@
;; --- initial data checks
;; the project1 should have 2 files
(let [rows (th/db-query :file {:project-id (:id project1)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; should be no files on project2
(let [rows (th/db-query :file {:project-id (:id project2)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; the file1 should be linked to file2
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; should be no libraries on file2
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows))))
;; move the library to other project
@@ -490,22 +490,22 @@
(t/is (nil? (:result out)))
;; project1 now should have 1 file
(let [[item :as rows] (th/db-query :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file1))))
;; project2 now should have 1 file
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file2))))
;; the file1 should not have any link to libraries
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(t/is (zero? (count rows))))
;; the file2 should not have any link to libraries
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (zero? (count rows)))))))
(t/deftest move-project
@@ -538,17 +538,16 @@
;; --- initial data checks
;; the project1 should have 2 files
(let [rows (th/db-query :file {:project-id (:id project1)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; the project2 should have 1 file
(let [rows (th/db-query :file {:project-id (:id project2)})]
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 1 (count rows))))
;; the file1 should be linked to file2 and file3
(let [[item1 item2 :as rows] (th/db-query :file-library-rel
{:file-id (:id file1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
{:order-by [:created-at]})]
(t/is (= 2 (count rows)))
(t/is (= (:file-id item1) (:id file1)))
(t/is (= (:library-file-id item1) (:id file2)))
@@ -556,14 +555,15 @@
(t/is (= (:library-file-id item2) (:id file3))))
;; the file2 should not be linked to any file
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows))))
;; the file3 should not be linked to any file
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file3)})]
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
(t/is (= 0 (count rows))))
;; move project1 to other team
;; TODO: correct team change of project
(let [data {::th/type :move-project
::rpc/profile-id (:id profile)
:project-id (:id project1)
@@ -574,25 +574,21 @@
(t/is (nil? (:result out)))
;; project1 now should still have 2 files
(let [[item1 item2 :as rows] (th/db-query :file
{:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
;; (clojure.pprint/pprint rows)
(t/is (= 2 (count rows)))
(t/is (= (:id item1) (:id file1)))
(t/is (= (:id item2) (:id file2))))
;; project2 now should still have 1 file
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
;; (pp/pprint rows)
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file3))))
;; the file1 should be linked to file2 but not file3
(let [[item1 :as rows] (th/db-query :file-library-rel
{:file-id (:id file1)}
{:order-by [:created-at]})]
(let [[item1 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
{:order-by [:created-at]})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item1) (:id file1)))
(t/is (= (:library-file-id item1) (:id file2)))))))

View File

@@ -51,10 +51,7 @@
"layout/grid"})
;; A set of features enabled by default for each file, they are
;; implicit and are enabled by default and can't be disabled. The
;; features listed in this set are mainly freatures addedby file
;; migrations process, so all features referenced in migrations should
;; be here.
;; implicit and are enabled by default and can't be disabled
(def default-enabled-features
#{"fdata/shape-data-type"})
@@ -193,11 +190,7 @@
([enabled-features file-features]
(check-file-features! enabled-features file-features #{}))
([enabled-features file-features client-features]
(let [file-features (into #{} xf-remove-ephimeral file-features)
;; We should ignore all features that does not match with the
;; `no-migration-features` set because we can't enable them
;; as-is, because they probably need migrations
client-features (set/intersection client-features no-migration-features)]
(let [file-features (into #{} xf-remove-ephimeral file-features)]
(let [not-supported (-> enabled-features
(set/union client-features)
(set/difference file-features)
@@ -215,11 +208,15 @@
(check-supported-features! file-features)
(let [not-supported (-> file-features
(set/difference enabled-features)
(set/difference client-features)
(set/difference backend-only-features)
(set/difference frontend-only-features))]
(let [;; We should ignore all features that does not match with
;; the `no-migration-features` set because we can't enable
;; them as-is, because they probably need migrations
client-features (set/intersection client-features no-migration-features)
not-supported (-> file-features
(set/difference enabled-features)
(set/difference client-features)
(set/difference backend-only-features)
(set/difference frontend-only-features))]
(when (seq not-supported)
(ex/raise :type :restriction

View File

@@ -358,13 +358,13 @@
(defn changed-attrs
"Returns the list of attributes that will change when `update-fn` is applied"
[object objects update-fn {:keys [attrs]}]
[object update-fn {:keys [attrs]}]
(let [changed?
(fn [old new attr]
(let [old-val (get old attr)
new-val (get new attr)]
(not= old-val new-val)))
new-obj (update-fn object objects)]
new-obj (update-fn object)]
(when-not (= object new-obj)
(let [attrs (or attrs (d/concat-set (keys object) (keys new-obj)))]
(filter (partial changed? object new-obj) attrs)))))
@@ -412,7 +412,7 @@
update-shape
(fn [changes id]
(let [old-obj (get objects id)
new-obj (update-fn old-obj objects)]
new-obj (update-fn old-obj)]
(if (= old-obj new-obj)
changes
(let [[rops uops] (-> (or attrs (d/concat-set (keys old-obj) (keys new-obj)))

View File

@@ -42,21 +42,18 @@
(reduce migrate-fn data (range (:version data 0) to-version))))))
(defn migrate-file
[{:keys [id data features] :as file}]
(binding [cfeat/*new* (atom #{})]
(let [file (-> file
(update :data assoc :id id)
(update :data migrate-data)
(update :features (fnil into #{}) (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features))]
(if (or (not= (:version data) (:version (:data file)))
(not= features (:features file)))
(vary-meta file assoc ::migrated true)
file))))
[{:keys [id data] :as file}]
(let [data (assoc data :id id)]
(-> file
(assoc ::orig-version (:version data))
(assoc :data (migrate-data data)))))
(defn migrated?
[file]
(true? (-> file meta ::migrated)))
[{:keys [data] :as file}]
(or (::migrated file)
(let [version (:version data)]
(> version
(::orig-version file version)))))
;; Default handler, noop
(defmethod migrate :default [data] data)

View File

@@ -28,14 +28,14 @@
(let [repair-shape
(fn [shape]
; Reset geometry to minimal
(log/debug :hint " -> reset geometry")
(log/debug :hint " -> Reset geometry")
(-> shape
(assoc :x 0)
(assoc :y 0)
(assoc :width 0.01)
(assoc :height 0.01)
(cts/setup-rect)))]
(log/dbg :hint "repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -45,10 +45,10 @@
(let [repair-shape
(fn [shape]
; Set parent to root frame.
(log/debug :hint " -> set to " :parent-id uuid/zero)
(log/debug :hint " -> Set to " :parent-id uuid/zero)
(assoc shape :parent-id uuid/zero))]
(log/dbg :hint "repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -58,10 +58,10 @@
(let [repair-shape
(fn [parent-shape]
; Add shape to parent's children list
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
(log/debug :hint " -> Add children to" :parent-id (:id parent-shape))
(update parent-shape :shapes conj (:id shape)))]
(log/dbg :hint "repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:parent-id shape)] repair-shape))))
@@ -70,17 +70,17 @@
[_ {:keys [shape page-id args] :as error} file-data _]
(let [repair-shape
(fn [parent-shape]
(log/debug :hint " -> remove child" :child-id (:child-id args))
(log/debug :hint " -> Remove child" :child-id (:child-id args))
(update parent-shape :shapes (fn [shapes]
(d/removev #(= (:child-id args) %) shapes))))]
(log/dbg :hint "repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
(defmethod repair-error :invalid-parent
[_ {:keys [shape page-id args] :as error} file-data _]
(log/dbg :hint "repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/change-parent (:parent-id args) [shape] nil {:component-swap true})))
@@ -93,10 +93,10 @@
(let [page (ctpl/get-page file-data page-id)
frame (cfh/get-frame (:objects page) (:parent-id shape))
frame-id (or (:id frame) uuid/zero)]
(log/debug :hint " -> set to " :frame-id frame-id)
(log/debug :hint " -> Set to " :frame-id frame-id)
(assoc shape :frame-id frame-id)))]
(log/dbg :hint "repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -109,10 +109,10 @@
(let [page (ctpl/get-page file-data page-id)
frame (cfh/get-frame (:objects page) (:parent-id shape))
frame-id (or (:id frame) uuid/zero)]
(log/debug :hint " -> set to " :frame-id frame-id)
(log/debug :hint " -> Set to " :frame-id frame-id)
(assoc shape :frame-id frame-id)))]
(log/dbg :hint "repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -122,10 +122,10 @@
(let [repair-shape
(fn [shape]
; Set the :shape as main instance root
(log/debug :hint " -> set :main-instance")
(log/debug :hint " -> Set :main-instance")
(assoc shape :main-instance true))]
(log/dbg :hint "repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -135,13 +135,13 @@
(let [repair-shape
(fn [shape]
; Set :component-file to local file
(log/debug :hint " -> set :component-file to local file")
(log/debug :hint " -> Set :component-file to local file")
(assoc shape :component-file (:id file-data)))]
; There is no solution that may recover it with confidence
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
;; shape)]
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -154,13 +154,13 @@
repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
; There is no solution that may recover it with confidence
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
;; shape)]
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes shape-ids repair-shape))))
@@ -172,15 +172,15 @@
repair-component
(fn [component]
; Assign main instance in the component to current shape
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
(log/debug :hint " -> Assign main-instance-id" :component-id (:id component))
(assoc component :main-instance-id (:id shape)))
detach-shape
(fn [shape]
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/dbg :hint "repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
(if (and (some? component) (not (:deleted component)))
(-> (pcb/empty-changes nil page-id)
(pcb/with-library-data file-data)
@@ -195,9 +195,9 @@
(let [repair-component
(fn [component]
; Assign main instance in the component to current shape
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
(log/debug :hint " -> Assign main-instance-page" :component-id (:id component))
(assoc component :main-instance-page page-id))]
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-library-data file-data)
(pcb/update-component (:component-id shape) repair-component))))
@@ -210,7 +210,7 @@
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
shape)]
(log/dbg :hint "repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -220,10 +220,10 @@
(let [repair-shape
(fn [shape]
; Unset the :shape as main instance root
(log/debug :hint " -> unset :main-instance")
(log/debug :hint " -> Unset :main-instance")
(dissoc shape :main-instance))]
(log/dbg :hint "repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -233,10 +233,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top copy root.
(log/debug :hint " -> set :component-root")
(log/debug :hint " -> Set :component-root")
(assoc shape :component-root true))]
(log/dbg :hint "repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -246,10 +246,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested copy root.
(log/debug :hint " -> unset :component-root")
(log/debug :hint " -> Unset :component-root")
(dissoc shape :component-root))]
(log/dbg :hint "repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -268,17 +268,17 @@
reassign-shape
(fn [shape]
(log/debug :hint " -> reassign shape-ref to" :shape-ref (:id matching-shape))
(log/debug :hint " -> Reassign shape-ref to" :shape-ref (:id matching-shape))
(assoc shape :shape-ref (:id matching-shape)))
detach-shape
(fn [shape]
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
; If the shape still refers to the remote component, try to find the corresponding near one
; and link to it. If not, detach the shape.
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(if (some? matching-shape)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
@@ -294,10 +294,10 @@
(let [repair-shape
(fn [shape]
; Remove shape-ref
(log/debug :hint " -> unset :shape-ref")
(log/debug :hint " -> Unset :shape-ref")
(dissoc shape :shape-ref))]
(log/dbg :hint "repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -307,10 +307,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested main head.
(log/debug :hint " -> unset :component-root")
(log/debug :hint " -> Unset :component-root")
(dissoc shape :component-root))]
(log/dbg :hint "repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -320,10 +320,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top main head.
(log/debug :hint " -> set :component-root")
(log/debug :hint " -> Set :component-root")
(assoc shape :component-root true))]
(log/dbg :hint "repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -333,10 +333,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested copy head.
(log/debug :hint " -> unset :component-root")
(log/debug :hint " -> Unset :component-root")
(dissoc shape :component-root))]
(log/dbg :hint "repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -346,10 +346,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top copy root.
(log/debug :hint " -> set :component-root")
(log/debug :hint " -> Set :component-root")
(assoc shape :component-root true))]
(log/dbg :hint "repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -359,10 +359,10 @@
(let [repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/dbg :hint "repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -372,10 +372,10 @@
(let [repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/dbg :hint "repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -388,7 +388,7 @@
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
shape)]
(log/dbg :hint "repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -398,14 +398,14 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a frame.
(log/debug :hint " -> set :type :frame")
(log/debug :hint " -> Set :type :frame")
(assoc shape :type :frame
:fills []
:hide-in-viewer true
:rx 0
:ry 0))]
(log/dbg :hint "repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(log/info :hint "Repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -417,13 +417,13 @@
; Remove the objects key, or set it to {} if the component is deleted
(if (:deleted component)
(do
(log/debug :hint " -> set :objects {}")
(log/debug :hint " -> Set :objects {}")
(assoc component :objects {}))
(do
(log/debug :hint " -> remove :objects")
(log/debug :hint " -> Remove :objects")
(dissoc component :objects))))]
(log/dbg :hint "repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
(log/info :hint "Repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
(-> (pcb/empty-changes nil)
(pcb/with-library-data file-data)
(pcb/update-component (:id shape) repair-component))))
@@ -434,15 +434,13 @@
file)
(defn repair-file
[{:keys [data id] :as file} libraries errors]
(log/dbg :hint "repairing file" :id (str id) :errors (count errors))
(let [{:keys [redo-changes]}
(reduce (fn [changes error]
(pcb/concat-changes changes
(repair-error (:code error)
error
data
libraries)))
(pcb/empty-changes nil)
errors)]
redo-changes))
[file-data libraries errors]
(log/info :hint "Repairing file" :id (:id file-data) :error-count (count errors))
(reduce (fn [changes error]
(pcb/concat-changes changes
(repair-error (:code error)
error
file-data
libraries)))
(pcb/empty-changes nil)
errors))

View File

@@ -6,7 +6,6 @@
(ns app.common.files.validate
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
@@ -50,50 +49,50 @@
:not-component-not-allowed
:component-nil-objects-not-allowed})
(def ^:private
schema:error
(sm/define
[:map {:title "ValidationError"}
[:code {:optional false} [::sm/one-of error-codes]]
[:hint {:optional false} :string]
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
[:shape-id {:optional true} ::sm/uuid]
[:file-id ::sm/uuid]
[:page-id ::sm/uuid]]))
(def validation-error
[:map {:title "ValidationError"}
[:code {:optional false} [::sm/one-of error-codes]]
[:hint {:optional false} :string]
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
[:file-id ::sm/uuid]
[:page-id ::sm/uuid]])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ERROR HANDLING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:dynamic ^:private *errors* nil)
(def ^:dynamic *errors* nil)
(defn- report-error
(defn report-error!
[code hint shape file page & {:as args}]
(let [error {:code code
:hint hint
:shape shape
:file-id (:id file)
:page-id (:id page)
:shape-id (:id shape)
:args args}]
(if (some? *errors*)
(vswap! *errors* conj {:code code
:hint hint
:shape shape
:file-id (:id file)
:page-id (:id page)
:args args})
(dm/assert!
"expected a valid `*errors*` dynamic binding"
(some? *errors*))
(dm/assert!
"expected valid error"
(sm/check! schema:error error))
(vswap! *errors* conj error)))
(let [explain (str/ffmt "file %, page %, shape %"
(:id file)
(:id page)
(:id shape))]
(ex/raise :type :validation
:code code
:hint hint
:args args
:file-id (:id file)
:page-id (:id page)
:shape-id (:id shape)
::explain explain))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRIVATE API: VALIDATION FUNCTIONS
;; VALIDATION FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare check-shape)
(declare validate-shape!)
(defn- check-geometry
(defn validate-geometry!
"Validate that the shape has valid coordinates, selrect and points."
[shape file page]
(when (and (not (#{:path :bool} (:type shape)))
@@ -103,175 +102,175 @@
(nil? (:height shape))
(nil? (:selrect shape))
(nil? (:points shape))))
(report-error :invalid-geometry
"Shape greometry is invalid"
shape file page)))
(report-error! :invalid-geometry
"Shape greometry is invalid"
shape file page)))
(defn- check-parent-children
(defn validate-parent-children!
"Validate parent and children exists, and the link is bidirectional."
[shape file page]
(let [parent (ctst/get-shape page (:parent-id shape))]
(if (nil? parent)
(report-error :parent-not-found
(str/ffmt "Parent % not found" (:parent-id shape))
shape file page)
(report-error! :parent-not-found
(str/ffmt "Parent % not found" (:parent-id shape))
shape file page)
(do
(when-not (cfh/root? shape)
(when-not (some #{(:id shape)} (:shapes parent))
(report-error :child-not-in-parent
(str/ffmt "Shape % not in parent's children list" (:id shape))
shape file page)))
(report-error! :child-not-in-parent
(str/ffmt "Shape % not in parent's children list" (:id shape))
shape file page)))
(doseq [child-id (:shapes shape)]
(let [child (ctst/get-shape page child-id)]
(if (nil? child)
(report-error :child-not-found
(str/ffmt "Child % not found in parent %" child-id (:id shape))
shape file page
:parent-id (:id shape)
:child-id child-id)
(report-error! :child-not-found
(str/ffmt "Child % not found in parent %" child-id (:id shape))
shape file page
:parent-id (:id shape)
:child-id child-id)
(when (not= (:parent-id child) (:id shape))
(report-error :invalid-parent
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
child file page
:parent-id (:id shape))))))))))
(report-error! :invalid-parent
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
child file page
:parent-id (:id shape))))))))))
(defn- check-frame
(defn validate-frame!
"Validate that the frame-id shape exists and is indeed a frame. Also
it must point to the parent shape (if this is a frame) or to the
frame-id of the parent (if not)."
[shape file page]
(let [frame (ctst/get-shape page (:frame-id shape))]
(if (nil? frame)
(report-error :frame-not-found
(str/ffmt "Frame % not found" (:frame-id shape))
shape file page)
(report-error! :frame-not-found
(str/ffmt "Frame % not found" (:frame-id shape))
shape file page)
(if (not= (:type frame) :frame)
(report-error :invalid-frame
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
shape file page)
(report-error! :invalid-frame
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
shape file page)
(let [parent (ctst/get-shape page (:parent-id shape))]
(when (some? parent)
(if (= (:type parent) :frame)
(when-not (= (:frame-id shape) (:id parent))
(report-error :invalid-frame
(str/ffmt "Frame-id should point to parent %" (:id parent))
shape file page))
(report-error! :invalid-frame
(str/ffmt "Frame-id should point to parent %" (:id parent))
shape file page))
(when-not (= (:frame-id shape) (:frame-id parent))
(report-error :invalid-frame
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
shape file page)))))))))
(report-error! :invalid-frame
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
shape file page)))))))))
(defn- check-component-main-head
(defn validate-component-main-head!
"Validate shape is a main instance head, component exists
and its main-instance points to this shape."
[shape file page libraries]
(when (nil? (:main-instance shape))
(report-error :component-not-main
"Shape expected to be main instance"
shape file page))
(report-error! :component-not-main
"Shape expected to be main instance"
shape file page))
(when-not (= (:component-file shape) (:id file))
(report-error :component-main-external
"Main instance should refer to a component in the same file"
shape file page))
(report-error! :component-main-external
"Main instance should refer to a component in the same file"
shape file page))
(let [component (ctf/resolve-component shape file libraries :include-deleted? true)]
(if (nil? component)
(report-error :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page)
(report-error! :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page)
(do
(when-not (= (:main-instance-id component) (:id shape))
(report-error :invalid-main-instance-id
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
shape file page))
(report-error! :invalid-main-instance-id
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
shape file page))
(when-not (= (:main-instance-page component) (:id page))
(let [component-page (ctf/get-component-page (:data file) component)
main-component (ctst/get-shape component-page (:main-instance-id component))]
;; We must check if the same component has main instances in different pages.
;; In that case one of those instances shouldn't be main
(if (:main-instance main-component)
(report-error :component-main
"Shape not expected to be main instance"
shape file page)
(report-error :invalid-main-instance-page
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
shape file page))))))))
(report-error! :component-main
"Shape not expected to be main instance"
shape file page)
(report-error! :invalid-main-instance-page
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
shape file page))))))))
(defn- check-component-not-main-head
(defn validate-component-not-main-head!
"Validate shape is a not-main instance head, component
exists and its main-instance does not point to this
shape."
[shape file page libraries]
(when (true? (:main-instance shape))
(report-error :component-not-main
"Shape not expected to be main instance"
shape file page))
(report-error! :component-not-main
"Shape not expected to be main instance"
shape file page))
(let [library-exists? (or (= (:component-file shape) (:id file))
(contains? libraries (:component-file shape)))
(contains? libraries (:component-file shape)))
component (when library-exists?
(ctf/resolve-component shape file libraries {:include-deleted? true}))]
(if (nil? component)
(when library-exists?
(report-error :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page))
(report-error! :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page))
(when (and (= (:main-instance-id component) (:id shape))
(= (:main-instance-page component) (:id page)))
(report-error :invalid-main-instance
(str/ffmt "Main instance of component % should not be this shape" (:id component))
shape file page)))))
(report-error! :invalid-main-instance
(str/ffmt "Main instance of component % should not be this shape" (:id component))
shape file page)))))
(defn- check-component-not-main-not-head
(defn validate-component-not-main-not-head!
"Validate that this shape is not main instance and not head."
[shape file page]
(when (true? (:main-instance shape))
(report-error :component-main
"Shape not expected to be main instance"
shape file page))
(report-error! :component-main
"Shape not expected to be main instance"
shape file page))
(when (or (some? (:component-id shape))
(some? (:component-file shape)))
(report-error :component-main
"Shape not expected to be component head"
shape file page)))
(report-error! :component-main
"Shape not expected to be component head"
shape file page)))
(defn- check-component-root
(defn validate-component-root!
"Validate that this shape is an instance root."
[shape file page]
(when (nil? (:component-root shape))
(report-error :should-be-component-root
"Shape should be component root"
shape file page)))
(report-error! :should-be-component-root
"Shape should be component root"
shape file page)))
(defn- check-component-not-root
(defn validate-component-not-root!
"Validate that this shape is not an instance root."
[shape file page]
(when (true? (:component-root shape))
(report-error :should-not-be-component-root
"Shape should not be component root"
shape file page)))
(report-error! :should-not-be-component-root
"Shape should not be component root"
shape file page)))
(defn- check-component-ref
(defn validate-component-ref!
"Validate that the referenced shape exists in the near component."
[shape file page libraries]
(let [library-exists? (or (= (:component-file shape) (:id file))
(contains? libraries (:component-file shape)))
(contains? libraries (:component-file shape)))
ref-shape (when library-exists?
(ctf/find-ref-shape file page libraries shape :include-deleted? true))]
(when (and library-exists? (nil? ref-shape))
(report-error :ref-shape-not-found
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
shape file page))))
(report-error! :ref-shape-not-found
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
shape file page))))
(defn- check-component-not-ref
(defn validate-component-not-ref!
"Validate that this shape does not reference other one."
[shape file page]
(when (some? (:shape-ref shape))
(report-error :shape-ref-in-main
"Shape inside main instance should not have shape-ref"
shape file page)))
(report-error! :shape-ref-in-main
"Shape inside main instance should not have shape-ref"
shape file page)))
(defn- check-shape-main-root-top
(defn validate-shape-main-root-top!
"Root shape of a top main instance:
- :main-instance
@@ -279,78 +278,78 @@
- :component-file
- :component-root"
[shape file page libraries]
(check-component-main-head shape file page libraries)
(check-component-root shape file page)
(check-component-not-ref shape file page)
(validate-component-main-head! shape file page libraries)
(validate-component-root! shape file page)
(validate-component-not-ref! shape file page)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :main-top)))
(validate-shape! child-id file page libraries :context :main-top)))
(defn- check-shape-main-root-nested
(defn validate-shape-main-root-nested!
"Root shape of a nested main instance
- :main-instance
- :component-id
- :component-file"
[shape file page libraries]
(check-component-main-head shape file page libraries)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(validate-component-main-head! shape file page libraries)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :main-nested)))
(validate-shape! child-id file page libraries :context :main-nested)))
(defn- check-shape-copy-root-top
(defn validate-shape-copy-root-top!
"Root shape of a top copy instance
- :component-id
- :component-file
- :component-root
- :shape-ref"
[shape file page libraries]
(check-component-not-main-head shape file page libraries)
(check-component-root shape file page)
(check-component-ref shape file page libraries)
(validate-component-not-main-head! shape file page libraries)
(validate-component-root! shape file page)
(validate-component-ref! shape file page libraries)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :copy-top)))
(validate-shape! child-id file page libraries :context :copy-top)))
(defn- check-shape-copy-root-nested
(defn validate-shape-copy-root-nested!
"Root shape of a nested copy instance
- :component-id
- :component-file
- :shape-ref"
[shape file page libraries]
(check-component-not-main-head shape file page libraries)
(check-component-not-root shape file page)
(check-component-ref shape file page libraries)
(validate-component-not-main-head! shape file page libraries)
(validate-component-not-root! shape file page)
(validate-component-ref! shape file page libraries)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :copy-nested)))
(validate-shape! child-id file page libraries :context :copy-nested)))
(defn- check-shape-main-not-root
(defn validate-shape-main-not-root!
"Not-root shape of a main instance (not any attribute)"
[shape file page libraries]
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :main-any)))
(validate-shape! child-id file page libraries :context :main-any)))
(defn- check-shape-copy-not-root
(defn validate-shape-copy-not-root!
"Not-root shape of a copy instance :shape-ref"
[shape file page libraries]
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-ref shape file page libraries)
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-ref! shape file page libraries)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :copy-any)))
(validate-shape! child-id file page libraries :context :copy-any)))
(defn- check-shape-not-component
(defn validate-shape-not-component!
"Shape is not in a component or is a fostered children (not any
attribute)"
[shape file page libraries]
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(doseq [child-id (:shapes shape)]
(check-shape child-id file page libraries :context :not-component)))
(validate-shape! child-id file page libraries :context :not-component)))
(defn- check-shape
(defn validate-shape!
"Validate referential integrity and semantic coherence of
a shape and all its children. Report all errors found.
@@ -367,140 +366,132 @@
(let [shape (ctst/get-shape page shape-id)]
(when (some? shape)
(do
(check-geometry shape file page)
(check-parent-children shape file page)
(check-frame shape file page)
(validate-geometry! shape file page)
(validate-parent-children! shape file page)
(validate-frame! shape file page)
(if (ctk/instance-head? shape)
(if (not= :frame (:type shape))
(report-error :instance-head-not-frame
"Instance head should be a frame"
shape file page)
(report-error! :instance-head-not-frame
"Instance head should be a frame"
shape file page)
(if (ctk/instance-root? shape)
(if (ctk/main-instance? shape)
(if (not= context :not-component)
(report-error :root-main-not-allowed
"Root main component not allowed inside other component"
shape file page)
(check-shape-main-root-top shape file page libraries))
(report-error! :root-main-not-allowed
"Root main component not allowed inside other component"
shape file page)
(validate-shape-main-root-top! shape file page libraries))
(if (not= context :not-component)
(report-error :root-copy-not-allowed
"Root copy component not allowed inside other component"
shape file page)
(check-shape-copy-root-top shape file page libraries)))
(report-error! :root-copy-not-allowed
"Root copy component not allowed inside other component"
shape file page)
(validate-shape-copy-root-top! shape file page libraries)))
(if (ctk/main-instance? shape)
(if (= context :not-component)
(report-error :nested-main-not-allowed
"Nested main component only allowed inside other component"
shape file page)
(check-shape-main-root-nested shape file page libraries))
(report-error! :nested-main-not-allowed
"Nested main component only allowed inside other component"
shape file page)
(validate-shape-main-root-nested! shape file page libraries))
(if (= context :not-component)
(report-error :nested-copy-not-allowed
"Nested copy component only allowed inside other component"
shape file page)
(check-shape-copy-root-nested shape file page libraries)))))
(report-error! :nested-copy-not-allowed
"Nested copy component only allowed inside other component"
shape file page)
(validate-shape-copy-root-nested! shape file page libraries)))))
(if (ctk/in-component-copy? shape)
(if-not (#{:copy-top :copy-nested :copy-any} context)
(report-error :not-head-copy-not-allowed
"Non-root copy only allowed inside a copy"
shape file page)
(check-shape-copy-not-root shape file page libraries))
(report-error! :not-head-copy-not-allowed
"Non-root copy only allowed inside a copy"
shape file page)
(validate-shape-copy-not-root! shape file page libraries))
(if (ctn/inside-component-main? (:objects page) shape)
(if-not (#{:main-top :main-nested :main-any} context)
(report-error :not-head-main-not-allowed
"Non-root main only allowed inside a main component"
shape file page)
(check-shape-main-not-root shape file page libraries))
(report-error! :not-head-main-not-allowed
"Non-root main only allowed inside a main component"
shape file page)
(validate-shape-main-not-root! shape file page libraries))
(if (#{:main-top :main-nested :main-any} context)
(report-error :not-component-not-allowed
"Not compoments are not allowed inside a main"
shape file page)
(check-shape-not-component shape file page libraries)))))))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
[component file]
(when (and (contains? component :objects) (nil? (:objects component)))
(report-error :component-nil-objects-not-allowed
"Objects list cannot be nil"
component file nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUBLIC API: VALIDATION FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn validate-file
"Validate full referential integrity and semantic coherence on file data.
Return a list of errors or `nil`"
[{:keys [data features] :as file} libraries]
(when (contains? features "components/v2")
(binding [*errors* (volatile! [])]
(doseq [page (filter :id (ctpl/pages-seq data))]
(let [orphans (->> page
:objects
vals
(filter #(not (contains? (:objects page) (:parent-id %))))
(map :id))]
(check-shape uuid/zero file page libraries)
(doseq [shape-id orphans]
(check-shape shape-id file page libraries))))
(doseq [component (vals (:components data))]
(check-component component file))
(-> *errors* deref not-empty))))
(report-error! :not-component-not-allowed
"Not compoments are not allowed inside a main"
shape file page)
(validate-shape-not-component! shape file page libraries)))))))))
(defn validate-shape
"Validate a shape and all its children. Returns a list of errors."
[shape-id file page libraries]
(binding [*errors* (volatile! [])]
(check-shape shape-id file page libraries)
(validate-shape! shape-id file page libraries)
(deref *errors*)))
(defn validate-component!
"Validate semantic coherence of a component. Report all errors found."
[component file]
(when (and (contains? component :objects) (nil? (:objects component)))
(report-error! :component-nil-objects-not-allowed
"Objects list cannot be nil"
component file nil)))
(defn validate-component
"Validate a component. Returns a list of errors."
[component file]
(binding [*errors* (volatile! [])]
(check-component component file)
(validate-component! component file)
(deref *errors*)))
(def ^:private valid-fdata?
(def valid-fdata?
"Structural validation of file data using defined schema"
(sm/lazy-validator ::ctf/data))
(def ^:private get-fdata-explain
(def get-fdata-explain
"Get schema explain data for file data"
(sm/lazy-explainer ::ctf/data))
(defn validate-file-schema!
[{:keys [id data] :as file}]
(when-not (valid-fdata? data)
(ex/raise :type :validation
:code :schema-validation
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id
::sm/explain (get-fdata-explain data))))
(if (some? *errors*)
(vswap! *errors* conj
{:code :invalid-file-data-structure
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id})
(ex/raise :type :validation
:code :data-validation
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id
::sm/explain (get-fdata-explain data))))
file)
(defn validate-file!
"Validate full referential integrity and semantic coherence on file data.
Raises an exception"
Raises a validation exception on first error found."
[{:keys [data features] :as file} libraries]
(when (contains? features "components/v2")
(doseq [page (filter :id (ctpl/pages-seq data))]
(let [orphans (->> page
:objects
vals
(filter #(not (contains? (:objects page) (:parent-id %))))
(map :id))]
(validate-shape! uuid/zero file page libraries)
(doseq [shape-id orphans]
(validate-shape! shape-id file page libraries))))
(doseq [component (vals (:components data))]
(validate-component! component file)))
file)
(defn validate-file
"Validate structure, referencial integrity and semantic coherence of
all contents of a file. Returns a list of errors."
[file libraries]
(when-let [errors (validate-file file libraries)]
(ex/raise :type :validation
:code :referential-integrity
:hint "error on validating file referential integrity"
:file-id (:id file)
:details errors)))
(binding [*errors* (volatile! [])]
(validate-file! file libraries)
(deref *errors*)))

View File

@@ -353,19 +353,3 @@
(mth/max by1 y1)
(mth/min bx2 x2)
(mth/min by2 y2)))))
(defn fix-aspect-ratio
[bounds aspect-ratio]
(if aspect-ratio
(let [width (dm/get-prop bounds :width)
height (dm/get-prop bounds :height)
target-height (* width aspect-ratio)
target-width (* height (/ 1 aspect-ratio))]
(cond-> bounds
(> target-height height)
(-> (assoc :height target-height)
(update :y - (/ (- target-height height ) 2)))
(< target-height height)
(-> (assoc :width target-width)
(update :x - (/ (- target-width width ) 2)))))
bounds))

View File

@@ -1,11 +1,3 @@
/**
* Performance focused pure java implementation of the
* SVG path parser.
*
* @author KALEIDOS INC
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
*/
package app.common.svg.path;
import java.util.Arrays;
@@ -69,11 +61,9 @@ public class Parser {
command = MOVE_TO;
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
break;
case 'Z':
command = CLOSE_PATH;
break;
case 'L':
command = LINE_TO;
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
@@ -646,9 +636,8 @@ public class Parser {
for (int i=0; i<pdata.size(); i++) {
Segment segment = pdata.get(i);
var currentCommand = segment.command;
switch(currentCommand) {
switch(segment.command) {
case 'M':
x = segment.params[0];
y = segment.params[1];
@@ -791,6 +780,16 @@ public class Parser {
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
result.addAll(segments);
// if (rx == 0 || ry == 0) {
// segment.command = 'C';
// segment.params = new double[] {currentX, currentY, x, y, x, y};
// result.add(segment);
// } else if (currentX != x || currentY != y) {
// var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
// result.addAll(segments);
// }
currentX = x;
currentY = y;
@@ -804,7 +803,7 @@ public class Parser {
break;
}
lastCommand = currentCommand;
lastCommand = segment.command;
}
return result;
@@ -871,6 +870,7 @@ public class Parser {
}
private static void processCurve(double[] curve, double cx, double cy, double rx, double ry, double sinPhi, double cosPhi) {
double x0 = curve[0] * rx;
double y0 = curve[1] * ry;
double x1 = curve[2] * rx;
@@ -910,13 +910,7 @@ public class Parser {
double x1p = ((cosPhi * (x1 - x2)) / 2) + ((sinPhi * (y1 - y2)) / 2);
double y1p = ((-sinPhi * (x1 - x2)) / 2) + ((cosPhi * (y1 - y2)) / 2);
if (x1p == 0 && y1p == 0) {
// we're asked to draw line to itself
return new ArrayList<>();
}
if (rx == 0 || ry == 0) {
// one of the radii is zero
if (x1p == 0 || y1p == 0 || rx == 0 || ry == 0) {
return new ArrayList<>();
}

View File

@@ -4,11 +4,9 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.svg.path.legacy-parser2
"The second SVG Path parser implementation.
Written in crossplatform CLJC code. Used meanwhile a hight
performance parser is developed in the 1.20 version."
(ns app.common.svg.path.legacy
"The first svg path parser implementation in pure clojure, used as reference impl
and for tests."
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
@@ -18,6 +16,7 @@
[app.common.svg.path.command :as upc]
[cuerdas.core :as str]))
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
(def regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
@@ -297,10 +296,10 @@
y1p (+ (/ (* (- sin-phi) (- x1 x2)) 2)
(/ (* cos-phi (- y1 y2)) 2))]
(if (or (and (zero? x1p)
(zero? y1p))
(and (zero? rx)
(zero? ry)))
(if (or (zero? x1p)
(zero? y1p)
(zero? rx)
(zero? ry))
[]
(let [
rx (mth/abs rx)
@@ -463,10 +462,19 @@
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
(first))))
(defn parse
[path-str]
(if (empty? path-str)
path-str
(let [commands (re-seq commands-regex path-str)]
(->> (mapcat parse-command commands)
(simplify-commands)))))
(simplify-commands)
(map (fn [segment]
;; (prn "LEGACY:" segment)
segment))))))

View File

@@ -1,325 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.svg.path.legacy-parser1
"The first SVG Path parser implementation.
Written in a mix of CLJS and JS code and used in production until
1.19, used mainly for tests."
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.svg :as csvg]
[app.common.svg.path.arc-to-bezier :as a2b]
[app.common.svg.path.command :as upc]
[cuerdas.core :as str]))
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
;; Matches numbers for path values allows values like... -.01, 10, +12.22
;; 0 and 1 are special because can refer to flags
(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
(def flag-regex #"[01]")
(defn extract-params [cmd-str extract-commands]
(loop [result []
extract-idx 0
current {}
remain (-> cmd-str (subs 1) (str/trim))]
(let [[param type] (nth extract-commands extract-idx)
regex (case type
:flag flag-regex
#_:number num-regex)
match (re-find regex remain)]
(if match
(let [value (-> match first csvg/fix-dot-number d/read-string)
remain (str/replace-first remain regex "")
current (assoc current param value)
extract-idx (inc extract-idx)
[result current extract-idx]
(if (>= extract-idx (count extract-commands))
[(conj result current) {} 0]
[result current extract-idx])]
(recur result
extract-idx
current
remain))
(cond-> result
(seq current) (conj current))))))
;; Path specification
;; https://www.w3.org/TR/SVG11/paths.html
(defmulti parse-command (comp str/upper first))
(defmethod parse-command "M" [cmd]
(let [relative (str/starts-with? cmd "m")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(into [{:command :move-to
:relative relative
:params (first param-list)}]
(for [params (rest param-list)]
{:command :line-to
:relative relative
:params params}))))
(defmethod parse-command "Z" [_]
[{:command :close-path}])
(defmethod parse-command "L" [cmd]
(let [relative (str/starts-with? cmd "l")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(for [params param-list]
{:command :line-to
:relative relative
:params params})))
(defmethod parse-command "H" [cmd]
(let [relative (str/starts-with? cmd "h")
param-list (extract-params cmd [[:value :number]])]
(for [params param-list]
{:command :line-to-horizontal
:relative relative
:params params})))
(defmethod parse-command "V" [cmd]
(let [relative (str/starts-with? cmd "v")
param-list (extract-params cmd [[:value :number]])]
(for [params param-list]
{:command :line-to-vertical
:relative relative
:params params})))
(defmethod parse-command "C" [cmd]
(let [relative (str/starts-with? cmd "c")
param-list (extract-params cmd [[:c1x :number]
[:c1y :number]
[:c2x :number]
[:c2y :number]
[:x :number]
[:y :number]])
]
(for [params param-list]
{:command :curve-to
:relative relative
:params params})))
(defmethod parse-command "S" [cmd]
(let [relative (str/starts-with? cmd "s")
param-list (extract-params cmd [[:cx :number]
[:cy :number]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :smooth-curve-to
:relative relative
:params params})))
(defmethod parse-command "Q" [cmd]
(let [relative (str/starts-with? cmd "q")
param-list (extract-params cmd [[:cx :number]
[:cy :number]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :quadratic-bezier-curve-to
:relative relative
:params params})))
(defmethod parse-command "T" [cmd]
(let [relative (str/starts-with? cmd "t")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(for [params param-list]
{:command :smooth-quadratic-bezier-curve-to
:relative relative
:params params})))
(defmethod parse-command "A" [cmd]
(let [relative (str/starts-with? cmd "a")
param-list (extract-params cmd [[:rx :number]
[:ry :number]
[:x-axis-rotation :number]
[:large-arc-flag :flag]
[:sweep-flag :flag]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :elliptical-arc
:relative relative
:params params})))
(defn smooth->curve
[{:keys [params]} pos handler]
(let [{c1x :x c1y :y} (upg/calculate-opposite-handler pos handler)]
{:c1x c1x
:c1y c1y
:c2x (:cx params)
:c2y (:cy params)}))
(defn quadratic->curve
[sp ep cp]
(let [cp1 (-> (gpt/to-vec sp cp)
(gpt/scale (/ 2 3))
(gpt/add sp))
cp2 (-> (gpt/to-vec ep cp)
(gpt/scale (/ 2 3))
(gpt/add ep))]
{:c1x (:x cp1)
:c1y (:y cp1)
:c2x (:x cp2)
:c2y (:y cp2)}))
(defn arc->beziers*
[from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation]
(a2b/calculateBeziers from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation))
(defn arc->beziers [from-p command]
(let [to-command
(fn [[_ _ c1x c1y c2x c2y x y]]
{:command :curve-to
:relative (:relative command)
:params {:c1x c1x :c1y c1y
:c2x c2x :c2y c2y
:x x :y y}})
{from-x :x from-y :y} from-p
{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} (:params command)
result (arc->beziers* from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation)]
(mapv to-command result)))
(defn simplify-commands
"Removes some commands and convert relative to absolute coordinates"
[commands]
(let [simplify-command
;; prev-pos : previous position for the current path. Necessary for relative commands
;; prev-start : previous move-to necessary for Z commands
;; prev-cc : previous command control point for cubic beziers
;; prev-qc : previous command control point for quadratic curves
(fn [[result prev-pos prev-start prev-cc prev-qc] [command _prev]]
(let [command (assoc command :prev-pos prev-pos)
command
(cond-> command
(:relative command)
(-> (assoc :relative false)
(d/update-in-when [:params :c1x] + (:x prev-pos))
(d/update-in-when [:params :c1y] + (:y prev-pos))
(d/update-in-when [:params :c2x] + (:x prev-pos))
(d/update-in-when [:params :c2y] + (:y prev-pos))
(d/update-in-when [:params :cx] + (:x prev-pos))
(d/update-in-when [:params :cy] + (:y prev-pos))
(d/update-in-when [:params :x] + (:x prev-pos))
(d/update-in-when [:params :y] + (:y prev-pos))
(cond->
(= :line-to-horizontal (:command command))
(d/update-in-when [:params :value] + (:x prev-pos))
(= :line-to-vertical (:command command))
(d/update-in-when [:params :value] + (:y prev-pos)))))
params (:params command)
orig-command command
command
(cond-> command
(= :line-to-horizontal (:command command))
(-> (assoc :command :line-to)
(update :params dissoc :value)
(assoc-in [:params :x] (:value params))
(assoc-in [:params :y] (:y prev-pos)))
(= :line-to-vertical (:command command))
(-> (assoc :command :line-to)
(update :params dissoc :value)
(assoc-in [:params :y] (:value params))
(assoc-in [:params :x] (:x prev-pos)))
(= :smooth-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params dissoc :cx :cy)
(update :params merge (smooth->curve command prev-pos prev-cc)))
(= :quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params dissoc :cx :cy)
(update :params merge (quadratic->curve prev-pos (gpt/point params) (gpt/point (:cx params) (:cy params)))))
(= :smooth-quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params merge (quadratic->curve prev-pos (gpt/point params) (upg/calculate-opposite-handler prev-pos prev-qc)))))
result (if (= :elliptical-arc (:command command))
(into result (arc->beziers prev-pos command))
(conj result command))
next-cc (case (:command orig-command)
:smooth-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
:curve-to
(gpt/point (get-in orig-command [:params :c2x]) (get-in orig-command [:params :c2y]))
(:line-to-horizontal :line-to-vertical)
(gpt/point (get-in command [:params :x]) (get-in command [:params :y]))
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
next-qc (case (:command orig-command)
:quadratic-bezier-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
:smooth-quadratic-bezier-curve-to
(upg/calculate-opposite-handler prev-pos prev-qc)
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
next-pos (if (= :close-path (:command command))
prev-start
(upc/command->point prev-pos command))
next-start (if (= :move-to (:command command)) next-pos prev-start)]
[result next-pos next-start next-cc next-qc]))
start (first commands)
start (cond-> start
(:relative start)
(assoc :relative false))
start-pos (gpt/point (:params start))]
(->> (map vector (rest commands) commands)
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
(first))))
(defn parse [path-str]
(if (empty? path-str)
path-str
(let [clean-path-str
(-> path-str
(str/trim)
;; Change "commas" for spaces
(str/replace #"," " ")
;; Remove all consecutive spaces
(str/replace #"\s+" " "))
commands (re-seq commands-regex clean-path-str)]
(-> (mapcat parse-command commands)
(simplify-commands)))))

View File

@@ -1,11 +1,3 @@
/**
* Performance focused pure javascript implementation of the
* SVG path parser.
*
* @author KALEIDOS INC
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
*/
import cljs from "goog:cljs.core";
const MOVE_TO = cljs.keyword("move-to");
@@ -682,13 +674,7 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
let x1p = (cosPhi * (x1 - x2)) / 2 + (sinPhi * (y1 - y2)) / 2;
let y1p = (-sinPhi * (x1 - x2)) / 2 + (cosPhi * (y1 - y2)) / 2;
if (x1p === 0 && y1p === 0) {
// we're asked to draw line to itself
return [];
}
if (rx === 0 || ry === 0) {
// one of the radii is zero
if (x1p === 0 || y1p === 0 || rx === 0 || ry === 0) {
return [];
}
@@ -725,7 +711,7 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
// commands.
function simplifyPathData(pdata) {
var result = [];
var lastCommand = null;
var lastType = null;
var lastControlX = null;
var lastControlY = null;
@@ -738,9 +724,8 @@ function simplifyPathData(pdata) {
for (let i=0; i<pdata.length; i++) {
const segment = pdata[i];
const currentCommand = segment.command;
switch(currentCommand) {
switch(segment.command) {
case "M":
var x = segment.params[0];
var y = segment.params[1];
@@ -802,7 +787,7 @@ function simplifyPathData(pdata) {
var cx1, cy1;
if (lastCommand === "C" || lastCommand === "S") {
if (lastType === "C" || lastType === "S") {
cx1 = currentX + (currentX - lastControlX);
cy1 = currentY + (currentY - lastControlY);
} else {
@@ -828,7 +813,7 @@ function simplifyPathData(pdata) {
var x1, y1;
if (lastCommand === "Q" || lastCommand === "T") {
if (lastType === "Q" || lastType === "T") {
x1 = currentX + (currentX - lastControlX);
y1 = currentY + (currentY - lastControlY);
} else {
@@ -891,7 +876,6 @@ function simplifyPathData(pdata) {
currentX = x;
currentY = y;
} else if (currentX !== x || currentY !== y) {
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
result.push(...segments);
@@ -907,7 +891,7 @@ function simplifyPathData(pdata) {
break;
}
lastCommand = currentCommand;
lastCommand = segment.command;
}
return result;

View File

@@ -229,7 +229,6 @@
:svg-viewbox selrect
:svg-attrs attrs
:svg-transform transform
:strokes []
:fills []})
(gsh/translate-to-frame origin)))))
@@ -356,9 +355,9 @@
(assoc :svg-attrs props))))))
(defn setup-fill
[shape]
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
color-attr (if (= color-attr "currentColor") clr/black color-attr)
[shape]
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
color-attr (if (= color-attr "currentColor") clr/black color-attr)
color-style (str/trim (dm/get-in shape [:svg-attrs :style :fill]))
color-style (if (= color-style "currentColor") clr/black color-style)]
(cond-> shape
@@ -385,7 +384,6 @@
(update :svg-attrs dissoc :fillOpacity)
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity])
(d/parse-double 1)))))))
(defn- setup-stroke
[shape]
(let [attrs (get shape :svg-attrs)
@@ -424,8 +422,7 @@
(dissoc :stroke)
(dissoc :strokeLinecap)
(dissoc :strokeWidth)
(dissoc :strokeOpacity))))
(d/without-nils))]
(dissoc :strokeOpacity)))))]
(cond-> (assoc shape :svg-attrs attrs)
(some? color)
@@ -437,7 +434,7 @@
(and (some? color) (some? width))
(assoc-in [:strokes 0 :stroke-width] width)
(and (some? linecap) (cfh/path-shape? shape)
(and (some? linecap) (= (:type shape) :path)
(or (= linecap :round) (= linecap :square)))
(assoc :stroke-cap-start linecap
:stroke-cap-end linecap)
@@ -467,6 +464,9 @@
(-> (update-in [:svg-attrs :style] dissoc :mixBlendMode)
(assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :style :mixBlendMode]) assert-valid-blend-mode)))))
(defn tag->name
"Given a tag returns its layer name"
[tag]

View File

@@ -1,18 +1,7 @@
(ns app.common.thumbnails
(:require
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
(:require [cuerdas.core :as str]))
(defn fmt-object-id
"Returns ids formatted as a string (object-id)"
[file-id page-id frame-id tag]
(str/ffmt "%/%/%/%" file-id page-id frame-id tag))
(defn file-id?
"Returns ids formatted as a string (file-id)"
[object-id file-id]
(str/starts-with? object-id (str/concat file-id "/")))
(defn get-file-id
[object-id]
(uuid/uuid (str/slice object-id 0 (str/index-of object-id "/"))))

View File

@@ -676,7 +676,7 @@
[id cell])))
(defn remove-grid-column
[parent index objects]
[parent index]
(let [track-num (inc index)
@@ -692,10 +692,10 @@
(-> parent
(update :layout-grid-columns d/remove-at-index index)
(update :layout-grid-cells update-cells)
(assign-cells objects))))
(assign-cells))))
(defn remove-grid-row
[parent index objects]
[parent index]
(let [track-num (inc index)
decrease-track-num (make-decrease-track-num :row :row-span track-num)
@@ -710,7 +710,7 @@
(-> parent
(update :layout-grid-rows d/remove-at-index index)
(update :layout-grid-cells update-cells)
(assign-cells objects))))
(assign-cells))))
(defn- reorder-grid-tracks
"Swap the positions of the tracks info"
@@ -781,7 +781,6 @@
parent
(reorder-grid-tracks parent tracks-props from-index to-index)]
(cond-> parent
move-content?
(swap-track-content prop from-track to-track))))
@@ -829,24 +828,14 @@
(defn check-deassigned-cells
"Clean the cells whith shapes that are no longer in the layout"
[parent objects]
[parent]
(let [child-set (set (:shapes parent))
(let [child? (set (:shapes parent))
cells (update-vals
(:layout-grid-cells parent)
(fn [cell] (update cell :shapes #(filterv child? %))))]
assigned?
(fn [id]
(and (contains? child-set id)
(not (position-absolute? objects id))))
cells
(update-vals
(:layout-grid-cells parent)
(fn [cell]
(-> cell
(update :shapes #(filterv assigned? %)))))]
(-> parent
(assoc :layout-grid-cells cells))))
(assoc parent :layout-grid-cells cells)))
(defn overlapping-cells
"Find overlapping cells"
@@ -913,12 +902,12 @@
;; - Shape duplication
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
(defn assign-cells
[parent objects]
[parent]
(let [;; TODO: Remove this, shouldn't be happening
;;overlaps (overlapping-cells parent)
;;_ (when (not (empty? overlaps))
;; (.warn js/console "OVERLAPS" overlaps))
parent (cond-> (check-deassigned-cells parent objects)
parent (cond-> (check-deassigned-cells parent)
#_(d/not-empty? overlaps)
#_(fix-overlaps overlaps))
@@ -926,9 +915,7 @@
(into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent))
no-cell-shapes
(->> (:shapes parent)
(remove shape-has-cell?)
(remove (partial position-absolute? objects)))
(->> (:shapes parent) (remove shape-has-cell?))
parent (position-auto-shapes parent)]
@@ -1187,7 +1174,7 @@
(let [;; Temporary remove the children when moving them
frame (-> frame
(update :shapes #(d/removev children %))
(assign-cells objects))
(assign-cells))
children (->> children (remove #(position-absolute? objects %)))]
@@ -1195,7 +1182,7 @@
(update :shapes d/concat-vec children)
(cond-> (some? cell)
(push-into-cell children row column))
(assign-cells objects))))
(assign-cells))))
(defn add-children-to-index
[parent ids objects to-index]

View File

Binary file not shown.

View File

@@ -2,8 +2,7 @@
* Arc to Bezier curves transformer
*
* Is a modified and google closure compatible version of the a2c
* functions by https://github.com/fontello/svgpath used as reference
* implementation for tests
* functions by https://github.com/fontello/svgpath
*
* @author KALEIDOS INC
* @license MIT License <https://opensource.org/licenses/MIT>
@@ -11,11 +10,11 @@
"use strict";
goog.provide("app.common.svg.path.arc_to_bezier");
goog.provide("common_tests.arc_to_bezier");
// https://raw.githubusercontent.com/fontello/svgpath/master/lib/a2c.js
goog.scope(function() {
const self = app.common.svg.path.arc_to_bezier;
const self = common_tests.arc_to_bezier;
var TAU = Math.PI * 2;
@@ -124,7 +123,7 @@ goog.scope(function() {
return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
}
function calculate_beziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
var sin_phi = Math.sin(phi * TAU / 360);
var cos_phi = Math.cos(phi * TAU / 360);
@@ -133,8 +132,6 @@ goog.scope(function() {
var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
// console.log("L", x1p, y1p)
if (x1p === 0 && y1p === 0) {
// we're asked to draw line to itself
return [];
@@ -207,5 +204,5 @@ goog.scope(function() {
});
}
self.calculateBeziers = calculate_beziers;
self.a2c = a2c;
});

View File

@@ -10,9 +10,9 @@
[app.common.pprint :as pp]
[app.common.math :as mth]
[app.common.svg.path :as svg.path]
[app.common.svg.path.legacy-parser2 :as svg.path.legacy2]
[app.common.svg.path.legacy :as svg.path.legacy]
[clojure.test :as t]
#?(:cljs [app.common.svg.path.legacy-parser2 :as svg.path.legacy1])))
#?(:cljs [common-tests.arc-to-bezier :as impl])))
(t/deftest parse-test-1
(let [data (str "m -994.563 4564.1423 149.3086 -52.8821 30.1828 "
@@ -23,25 +23,14 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
result2 (->> (svg.path.legacy/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result3 #?(:cljs (->> (svg.path.legacy1/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
:clj nil)]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 15
(count result1)
(count result2)))
#?(:cljs
(t/is (= 15
(count result1)
(count result3))))
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)]
@@ -51,14 +40,6 @@
(t/is (= (:params item1)
(:params item2)))
#?(:cljs
(let [item3 (nth result3 i)]
(t/is (= (:command item1)
(:command item3)))
(t/is (= (:params item1)
(:params item3)))))
#_(println "------------------------")
#_(pp/pprint (dissoc item1 :relative))
#_(pp/pprint (dissoc item2 :prev-pos :relative))))))
@@ -111,7 +92,7 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
result2 (->> (svg.path.legacy/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
@@ -127,6 +108,7 @@
(t/is (= (:command item1)
(:command item2)))
;; (println "================" (:command item1))
;; (pp/pprint (:params item1))
;; (println "---------")
@@ -142,7 +124,7 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
result2 (->> (svg.path.legacy/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
@@ -160,198 +142,6 @@
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001)))))))
(t/deftest parse-test-4
(let [data (str "m480 839-41-37c-70.512-64.747-128.807-120.601-174.884-167.561C218.039 587.48 181.333 "
"545.5 154 508.5S107.5 438 96.5 408 80 347.667 80 317c0-60.103 20.167-110.296 "
"60.5-150.577C180.833 126.141 230.667 106 290 106c38 0 73.167 9 105.5 27s60.5 44 84.5 "
"78c28-36 57.667-62.5 89-79.5S634 106 670 106c59.333 0 109.167 20.141 149.5 60.423C859.833 "
"206.704 880 256.897 880 317c0 30.667-5.5 61-16.5 91s-30.167 63.5-57.5 100.5-64.039 "
"78.98-110.116 125.939C649.807 681.399 591.512 737.253 521 802l-41 37Zm0-79c67.491-61.997 "
"123.03-115.163 166.618-159.498C690.206 556.167 724.833 517.333 750.5 484s43.667-63.045 "
"54-89.135c10.333-26.091 15.5-51.997 15.5-77.72 0-44.097-14-80.312-42-108.645S714.075 166 "
"670.225 166c-34.349 0-66.141 10.5-95.375 31.5C545.617 218.5 522 248 504 286h-49c-17.333"
"-37.333-40.617-66.667-69.85-88-29.234-21.333-61.026-32-95.375-32C245.925 166 210 180.167 "
"182 208.5s-42 64.605-42 108.816c0 25.789 5.167 51.851 15.5 78.184s28.333 56.333 54 90S270 "
"558 314 602s99.333 96.667 166 158Zm0-297Z")
expect [{:command :move-to, :params {:x 480.0, :y 839.0}}
{:command :line-to, :params {:x 439.0, :y 802.0}}
{:command :curve-to, :params {:c1x 368.488, :c1y 737.253, :c2x 310.193, :c2y 681.399, :x 264.116, :y 634.439}}
{:command :curve-to, :params {:c1x 218.039, :c1y 587.48, :c2x 181.333, :c2y 545.5, :x 154.0, :y 508.5}}
{:command :curve-to, :params {:c1x 126.667, :c1y 471.5, :c2x 107.5, :c2y 438.0, :x 96.5, :y 408.0}}
{:command :curve-to, :params {:c1x 85.5, :c1y 378.0, :c2x 80.0, :c2y 347.667, :x 80.0, :y 317.0}}
{:command :curve-to, :params {:c1x 80.0, :c1y 256.897, :c2x 100.167, :c2y 206.704, :x 140.5, :y 166.423}}
{:command :curve-to, :params {:c1x 180.833, :c1y 126.141, :c2x 230.667, :c2y 106.0, :x 290.0, :y 106.0}}
{:command :curve-to, :params {:c1x 328.0, :c1y 106.0, :c2x 363.16700000000003, :c2y 115.0, :x 395.5, :y 133.0}}
{:command :curve-to, :params {:c1x 427.83299999999997, :c1y 151.0, :c2x 456.0, :c2y 177.0, :x 480.0, :y 211.0}}
{:command :curve-to, :params {:c1x 508.0, :c1y 175.0, :c2x 537.667, :c2y 148.5, :x 569.0, :y 131.5}}
{:command :curve-to, :params {:c1x 600.333, :c1y 114.5, :c2x 634.0, :c2y 106.0, :x 670.0, :y 106.0}}
{:command :curve-to, :params {:c1x 729.333, :c1y 106.0, :c2x 779.167, :c2y 126.14099999999999, :x 819.5, :y 166.423}}
{:command :curve-to, :params {:c1x 859.833, :c1y 206.704, :c2x 880.0, :c2y 256.897, :x 880.0, :y 317.0}}
{:command :curve-to, :params {:c1x 880.0, :c1y 347.66700000000003, :c2x 874.5, :c2y 378.0, :x 863.5, :y 408.0}}
{:command :curve-to, :params {:c1x 852.5, :c1y 438.0, :c2x 833.333, :c2y 471.5, :x 806.0, :y 508.5}}
{:command :curve-to, :params {:c1x 778.667, :c1y 545.5, :c2x 741.961, :c2y 587.48, :x 695.884, :y 634.439}}
{:command :curve-to, :params {:c1x 649.807, :c1y 681.399, :c2x 591.512, :c2y 737.253, :x 521.0, :y 802.0}}
{:command :line-to, :params {:x 480.0, :y 839.0}}
{:command :close-path, :params {}}
{:command :move-to, :params {:x 480.0, :y 760.0}}
{:command :curve-to, :params {:c1x 547.491, :c1y 698.003, :c2x 603.03, :c2y 644.837, :x 646.6179999999999, :y 600.502}}
{:command :curve-to, :params {:c1x 690.206, :c1y 556.167, :c2x 724.833, :c2y 517.333, :x 750.5, :y 484.0}}
{:command :curve-to, :params {:c1x 776.167, :c1y 450.66700000000003, :c2x 794.167, :c2y 420.955, :x 804.5, :y 394.865}}
{:command :curve-to, :params {:c1x 814.833, :c1y 368.774, :c2x 820.0, :c2y 342.868, :x 820.0, :y 317.145}}
{:command :curve-to, :params {:c1x 820.0, :c1y 273.048, :c2x 806.0, :c2y 236.83299999999997, :x 778.0, :y 208.5}}
{:command :curve-to, :params {:c1x 750.0, :c1y 180.16700000000003, :c2x 714.075, :c2y 166.0, :x 670.225, :y 166.0}}
{:command :curve-to, :params {:c1x 635.876, :c1y 166.0, :c2x 604.0840000000001, :c2y 176.5, :x 574.85, :y 197.5}}
{:command :curve-to, :params {:c1x 545.617, :c1y 218.5, :c2x 522.0, :c2y 248.0, :x 504.0, :y 286.0}}
{:command :line-to, :params {:x 455.0, :y 286.0}}
{:command :curve-to, :params {:c1x 437.66700000000003, :c1y 248.667, :c2x 414.383, :c2y 219.333, :x 385.15, :y 198.0}}
{:command :curve-to, :params {:c1x 355.916, :c1y 176.667, :c2x 324.12399999999997, :c2y 166.0, :x 289.775, :y 166.0}}
{:command :curve-to, :params {:c1x 245.925, :c1y 166.0, :c2x 210.0, :c2y 180.167, :x 182.0, :y 208.5}}
{:command :curve-to, :params {:c1x 154.0, :c1y 236.833, :c2x 140.0, :c2y 273.105, :x 140.0, :y 317.31600000000003}}
{:command :curve-to, :params {:c1x 140.0, :c1y 343.105, :c2x 145.167, :c2y 369.16700000000003, :x 155.5, :y 395.5}}
{:command :curve-to,
:params {:c1x 165.833, :c1y 421.83299999999997, :c2x 183.833, :c2y 451.83299999999997, :x 209.5, :y 485.5}}
{:command :curve-to, :params {:c1x 235.167, :c1y 519.167, :c2x 270.0, :c2y 558.0, :x 314.0, :y 602.0}}
{:command :curve-to, :params {:c1x 358.0, :c1y 646.0, :c2x 413.33299999999997, :c2y 698.667, :x 480.0, :y 760.0}}
{:command :close-path, :params {}}
{:command :move-to, :params {:x 480.0, :y 463.0}}
{:command :close-path, :params {}}]
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 41
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
item3 (nth expect i)]
(t/is (= (:command item1)
(:command item2)
(:command item3)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
(t/is (mth/close? v (get-in item3 [:params k]) 0.000000001))
)))))
(t/deftest parse-test-5
(let [data (str "M363 826"
"q11-56 54-93"
"t101-37"
"h176"
"q22-35 34-75.179 12-40.178 12-84.821 0-125.357-87.321-212.679"
"Q565.357 236 440 236"
"t-212.679 87.321"
"Q140 410.643 140 536"
"q0 105 63 184.5T363 826Zm157 190"
"q-58 0-102"
"-36.5T363 888q-122-26-202.5-124T80 536q0-150 105-255t255-105"
"q150 0 255 105t105 "
"255q0 43-9.5 83.5T763 696q66 0 111.5 47T920 856q0 66-47 113t-113 47H520Zm-80"
"-485Zm200 325ZM520 956h240q42 0 71-29t29-71q0-42-29-71t-71-29H520q-42 0-71 29t"
"-29 71q0 42 29 71t71 29Zm-.175-70Q507 886 498.5 877.325"
"q-8.5-8.676-8.5-21.5 0"
"-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 8.5 8.676 8.5 "
"21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q627 886 618.5 877.325q-8.5"
"-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 "
"8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q747 886 "
"738.5 877.325q-8.5-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 "
"12.825 0 21.325 8.675 8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 "
"8.5Z"
)
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 76
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 100})
;; (pp/pprint result2 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
]
(t/is (= (:command item1)
(:command item2)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
)))))
(t/deftest parse-test-6
(let [data (str "M3.078 3.548v16.9a.5.5 0 0 0 1 0v-16.9a.5.5 0 0 0-1 0ZM18.422 11.5"
"H7.582a2.5 2.5 0 0 1-2.5-2.5V6.565a2.5 2.5 0 0 1 2.5-2.5"
"h10.84a2.5 2.5 0 0 1 2.5 2.5V9a2.5 2.5 0 0 1-2.5 2.5Z"
"M7.582 5.065a1.5 1.5 0 0 0-1.5 1.5V9a1.5 1.5 0 0 0 1.5 1.5"
"h10.84a1.5 1.5 0 0 0 1.5-1.5V6.565a1.5 1.5 0 0 0-1.5-1.5Z"
"M13.451 19.938H7.582a2.5 2.5 0 0 1-2.5-2.5V15"
"a2.5 2.5 0 0 1 2.5-2.5h5.869a2.5 2.5 0 0 1 2.5 2.5v2.436"
"a2.5 2.5 0 0 1-2.5 2.502ZM7.582 13.5a1.5 1.5 0 0 0-1.5 1.5v2.436"
"a1.5 1.5 0 0 0 1.5 1.5h5.869a1.5 1.5 0 0 0 1.5-1.5V15"
"a1.5 1.5 0 0 0-1.5-1.5Z")
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 47
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 100})
;; (pp/pprint result2 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
]
(t/is (= (:command item1)
(:command item2)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
)))
#?(:cljs
(let [result3 (svg.path.legacy1/parse data)]
(t/is (= 47
(count result1)
(count result3)))
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item3 (nth result2 i)]
(t/is (= (:command item1)
(:command item3)))
(t/is (= (:params item1)
(:params item3)))))))))
(t/deftest arc-to-bezier-1
(let [expected1 [-1.6697754290362354e-13
-5.258016244624741e-13
@@ -389,7 +179,7 @@
(nth expected2 (+ i 2))
0.0000000001))))
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(let [[result1 result2 :as total] (svg.path.legacy/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
@@ -400,96 +190,7 @@
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001))))
#?(:cljs
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001)))))
))
(t/deftest arc-to-bezier-2
(let [expected1 [3.0779999999999994,
20.448,
3.0780000082296834,
20.724142369096132,
3.3018576309038683,
20.94799998509884,
3.5779999999999994,
20.94799998509884]
expected2 [3.5779999999999994,
20.94799998509884,
3.854142369096131,
20.94799998509884,
4.077999991770315,
20.724142369096132,
4.077999999999999,
20.448]]
(let [[result1 result2 :as total] (->> (svg.path/arc->beziers 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)
(mapv (fn [segment]
(vec (.-params segment)))))]
(t/is (= (count total) 2))
;; (println "================" 11111111)
;; (pp/pprint expected1 {:width 50})
;; (println "------------")
;; (pp/pprint result1 {:width 50})
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 (+ i 2))
0.0000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 (+ i 2))
0.0000000001))))
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
(t/is (= (count total) 2))
;; (println "================" 11111111)
;; (pp/pprint expected1 {:width 50})
;; (println "------------")
;; (pp/pprint (vec result1) {:width 50})
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001))))
#?(:cljs
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001)))))
))
0.000000000001))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -519,14 +220,14 @@
"59.9137 -301.293 -1.0595 -51.375 25.7186 -261.0492 -7.706 ")
pattern [[:x :number] [:y :number]]]
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
(t/deftest extract-params-legacy-2
(let [expected [{:x -994.563, :y 4564.1423 :r 0}]
cmdstr (str "m -994.563 4564.1423 0")
pattern [[:x :number] [:y :number] [:r :flag]]]
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
(t/deftest extract-params-legacy-3
(let [cmdstr (str "a1.42 1.42 0 00-1.415-1.416 1.42 1.42 0 00-1.416 1.416 "
@@ -544,7 +245,7 @@
[:sweep-flag :flag]
[:x :number]
[:y :number]]
result (svg.path.legacy2/extract-params cmdstr pattern)]
result (svg.path.legacy/extract-params cmdstr pattern)]
(t/is (= (nth result 0)
(nth expected 0)))

View File

@@ -6,17 +6,10 @@
org.clojure/clojure {:mvn/version "1.11.1"}
binaryage/devtools {:mvn/version "RELEASE"}
metosin/reitit-core {:mvn/version "0.5.18"}
funcool/beicon {:mvn/version "2021.07.05-1"}
funcool/okulary {:mvn/version "2022.04.11-16"}
funcool/potok2
{:git/tag "v2.0"
:git/sha "2bb377b"
:git/url "https://github.com/funcool/potok.git"}
funcool/beicon2
{:git/tag "v2.0"
:git/sha "e7135e0"
:git/url "https://github.com/funcool/beicon.git"}
funcool/potok {:mvn/version "2022.12.16-71"}
funcool/rumext
{:git/tag "v2.7"

View File

@@ -46,7 +46,6 @@
"@storybook/react": "^7.5.3",
"@storybook/react-vite": "^7.5.3",
"@storybook/testing-library": "^0.2.2",
"@types/node": "^20.10.5",
"animate.css": "^4.1.1",
"autoprefixer": "^10.4.15",
"concurrently": "^8.2.2",
@@ -74,13 +73,13 @@
"sass": "^1.66.1",
"shadow-cljs": "2.26.2",
"storybook": "^7.5.3",
"typescript": "^5.3.3",
"vite": "^5.0.2"
},
"dependencies": {
"date-fns": "^2.30.0",
"draft-js": "^0.11.7",
"eventsource-parser": "^1.1.1",
"gl-matrix": "^3.4.3",
"highlight.js": "^11.8.0",
"js-beautify": "^1.14.9",
"jszip": "^3.10.1",
@@ -92,7 +91,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-virtualized": "^9.22.3",
"rxjs": "8.0.0-alpha.13",
"rxjs": "~7.8.1",
"sax": "^1.2.4",
"source-map-support": "^0.5.21",
"tdigest": "^0.1.2",

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.357 2.813h2.828v2.829m-10.37 4.713v2.829h2.828m7.071-9.9l-9.428 9.429z"/>
</svg>

Before

Width:  |  Height:  |  Size: 206 B

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 273 KiB

View File

@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M2.4 8a6 6 0 111.758 4.242M2.4 8l2.1-2zm0 0L1 5.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 182 B

View File

@@ -148,7 +148,6 @@
stroke: var(--button-tertiary-foreground-color-active);
}
}
&:global(.disabled),
&[disabled],
&:disabled {

View File

@@ -4,7 +4,7 @@
//
// Copyright (c) KALEIDOS INC
@use "sass:color";
@use "sass:color" as color;
:root {
// DARK
@@ -25,19 +25,16 @@
--dark-ok-color: var(--strong-green);
--dark-warning-color: #ff6432;
--dark-pending-color: var(--lilac);
--dark-error-color: #ff3277;
--default-presence-color: #dee563;
--dark-error-color: #ec1f1f;
// LIGHT
--light-gray-1: #fff;
--light-gray-2: #e8eaee;
--light-gray-2-30: rgba(232, 234, 238, 0.3);
--light-gray-2-80: rgba(232, 234, 238, 0.8);
--light-gray-3: #f3f4f6;
--light-gray-4: #eef0f2;
--black: #000;
--off-black: #495e74;
--off-black-30: #{color.change(#495e74, $alpha: 0.3)};
--purple: #6911d4;
--purple-30: rgba(105, 17, 212, 0.2);
--blue: #1345aa;

View File

@@ -157,8 +157,6 @@
.btn-primary {
@extend .button-primary;
text-transform: uppercase;
font-size: $fs-14;
font-weight: $fw400;
}
.btn-secondary {

View File

@@ -14,7 +14,6 @@
--scrollbar-background-color: var(--color-foreground-secondary);
--panel-background-color: var(--color-background-primary);
--panel-title-background-color: var(--color-background-secondary);
--presence-color: var(--default-presence-color);
// BUTTONS
--button-foreground-hover: var(--color-accent-primary);
@@ -180,7 +179,7 @@
--menu-shortcut-foreground-color: var(--color-foreground-secondary);
--menu-shortcut-foreground-color-selected: var(--color-foreground-primary);
--menu-shortcut-foreground-color-hover: var(--color-foreground-primary);
--menu-shadow-color: var(--shadow-color);
--menu-shadow-color: var(--color-background-subtle);
--menu-background-color-disabled: var(--color-background-primary);
--menu-foreground-color-disabled: var(--color-foreground-secondary);
--menu-border-color-disabled: var(--color-background-quaternary);
@@ -298,16 +297,4 @@
--tag-background-color: var(--color-accent-primary);
--link-foreground-color: var(--color-accent-primary);
--resize-area-background-color: var(--color-background-primary);
--resize-area-border-color: var(--color-background-quaternary);
// VIEWER
--viewer-background-color: var(--color-background-secondary);
--viewer-paginator-background-color: var(--color-background-tertiary);
--viewer-controls-background-color: var(--color-background-primary);
--viewer-inspect-border-color: var(--color-background-tertiary);
--viewer-thumbnails-control-foreground-color: var(--color-foreground-secondary);
--viewer-thumbnail-border-color: var(--color-accent-primary);
--viewer-thumbnail-background-color-selected: var(--color-accent-primary-muted);
}

View File

@@ -63,13 +63,6 @@
line-height: 1.2;
}
@mixin codeTypography {
font-family: "robotomono", monospace;
font-size: $fs-12;
font-weight: $fw400;
line-height: 1.2;
}
@mixin textEllipsis {
max-width: 99%;
overflow: hidden;

View File

@@ -21,7 +21,6 @@
--color-accent-primary-muted: var(--green-30);
--color-accent-secondary: var(--lilac);
--color-accent-tertiary: var(--strong-green);
--overlay-color: rgba(0, 0, 0, 0.4);
--ok-color: var(--dark-ok-color);
--warning-color: var(--dark-warning-color);
@@ -29,8 +28,5 @@
--error-color: var(--dark-error-color);
--canvas-color: var(--color-canvas);
--shadow-color: var(--dark-gray-2-30);
--radio-button-box-shadow: 0 0 0 1px var(--dark-gray-2-30) inset;
@include meta.load-css("hljs-dark-theme");
}

View File

@@ -1,124 +1,98 @@
/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
.hljs {
color: #adbac7;
background: #22272e;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #f47067;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #dcbdfb;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #6cb6ff;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #96d0ff;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #f69d50;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #768390;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #8ddb8c;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #adbac7;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #316dca;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #eac55f;
color: #e6e6e6;
background: #2a2c2d;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #adbac7;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #adbac7;
font-weight: bold;
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #b4f1b4;
background-color: #1b4721;
.hljs-link {
text-decoration: underline;
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #ffd8d3;
background-color: #78191b;
.hljs-comment,
.hljs-quote {
color: #bbbbbb;
font-style: italic;
}
.hljs-params {
color: #bbbbbb;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
.hljs-attr {
color: #e6e6e6;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta {
color: #ff4b82;
}
.hljs-operator,
.hljs-char.escape_ {
color: #b084eb;
}
.hljs-keyword,
.hljs-deletion {
color: #ff75b5;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #ff9ac1;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #45a9f9;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #19f9d8;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword,
.hljs-punctuation {
color: #ffb86c;
}

View File

@@ -1,126 +1,94 @@
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
.hljs {
color: #24292e;
background: #ffffff;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #005cc5;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #e36209;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #22863a;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #24292e;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #005cc5;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #735c0f;
color: #2a2c2d;
background: #e6e6e6;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #24292e;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #24292e;
font-weight: bold;
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #22863a;
background-color: #f0fff4;
.hljs-link {
text-decoration: underline;
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #b31d28;
background-color: #ffeef0;
.hljs-comment,
.hljs-quote {
color: #676b79;
font-style: italic;
}
.hljs-params {
color: #676b79;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
color: #6a737d;
.hljs-attr {
color: #2a2c2d;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta,
.hljs-operator,
.hljs-char.escape_ {
color: #c56200;
}
.hljs-keyword,
.hljs-deletion {
color: #d92792;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #cc5e91;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #3787c7;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #0d7d6c;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword {
color: #7641bb;
}

View File

@@ -21,16 +21,11 @@
--color-accent-primary-muted: var(--purple-30);
--color-accent-secondary: var(--blue);
--color-accent-tertiary: var(--strong-purple);
--overlay-color: rgba(255, 255, 255, 0.4);
--ok-color: var(--light-ok-color);
--warning-color: var(--light-warning-color);
--pending-color: var(--light-pending-color);
--error-color: var(--light-error-color);
--canvas-color: var(--color-canvas);
--shadow-color: var(--off-black-30);
--radio-button-box-shadow: 0 0 0 1px var(--light-gray-2) inset;
@include meta.load-css("hljs-light-theme");
}

View File

@@ -16,7 +16,7 @@
[app.util.webapi :as wapi]
[app.util.zip :as uz]
[app.worker.export :as e]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]))
(defn parse-data [data]
@@ -55,7 +55,7 @@
(->> (rx/from (vals media))
(rx/map #(assoc % :file-id file-id))
(rx/merge-map
(rx/flat-map
(fn [media]
(let [file-path (str/concat file-id "/media/" (:id media) (cm/mtype->extension (:mtype media)))
blob (data-uri->blob (:uri media))]
@@ -79,38 +79,38 @@
render-stream
(->> files-stream
(rx/merge-map vals)
(rx/merge-map e/process-pages)
(rx/flat-map vals)
(rx/flat-map e/process-pages)
(rx/observe-on :async)
(rx/merge-map e/get-page-data)
(rx/flat-map e/get-page-data)
(rx/share))
colors-stream
(->> files-stream
(rx/merge-map vals)
(rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :colors])))
(rx/filter #(d/not-empty? (second %)))
(rx/map e/parse-library-color))
typographies-stream
(->> files-stream
(rx/merge-map vals)
(rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :typographies])))
(rx/filter #(d/not-empty? (second %)))
(rx/map e/parse-library-typographies))
media-stream
(->> files-stream
(rx/merge-map vals)
(rx/flat-map vals)
(rx/map #(vector (:id %) (get-in % [:data :media])))
(rx/filter #(d/not-empty? (second %)))
(rx/merge-map parse-library-media))
(rx/flat-map parse-library-media))
components-stream
(->> files-stream
(rx/merge-map vals)
(rx/flat-map vals)
(rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/merge-map e/parse-library-components))
(rx/flat-map e/parse-library-components))
pages-stream
(->> render-stream
@@ -132,7 +132,7 @@
typographies-stream)
(rx/reduce conj [])
(rx/with-latest-from files-stream)
(rx/merge-map (fn [[data _]]
(rx/flat-map (fn [[data _]]
(->> (uz/compress-files data)
(rx/map #(vector file %)))))))))

View File

@@ -8,7 +8,7 @@
(:require
[app.common.uuid :as uuid]
[app.main.render :as r]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[promesa.core :as p]))
(defn render-page-export
@@ -22,7 +22,7 @@
(fn [resolve reject]
(->> (r/render-page data)
(rx/take 1)
(rx/subs! resolve reject))) )))
(rx/subs resolve reject))) )))
(defn exports []
#js {:renderPage render-page-export})

View File

@@ -28,10 +28,10 @@
[app.util.dom :as dom]
[app.util.i18n :as i18n]
[app.util.theme :as theme]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[debug]
[features]
[potok.v2.core :as ptk]
[potok.core :as ptk]
[rumext.v2 :as mf]))
(log/setup! {:app :info})

View File

@@ -9,8 +9,8 @@
(:require
[app.common.exceptions :as ex]
[app.common.transit :as t]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defrecord BroadcastMessage [id type data]
cljs.core/IDeref

View File

@@ -14,8 +14,8 @@
[app.common.uuid :as uuid]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.repo :as rp]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(def ^:private schema:comment-thread
(sm/define

View File

@@ -14,8 +14,8 @@
[app.main.features :as features]
[app.main.repo :as rp]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SHARE LINK
@@ -106,8 +106,9 @@
(:typography-count summary))]
(modal/show
{:type :confirm
:message ""
:title (tr "modals.add-shared-confirm.message" (:name summary))
:message (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
:hint (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
:cancel-label (if (zero? count) (tr "labels.cancel") :omit)
:accept-label (tr "modals.add-shared-confirm.accept")
:accept-style :primary

View File

@@ -30,9 +30,9 @@
[app.util.time :as dt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[clojure.set :as set]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(log/set-level! :warn)
@@ -419,7 +419,7 @@
(rx/map di/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :update-team-photo %))
(rx/tap on-success)
(rx/do on-success)
(rx/map du/fetch-teams)
(rx/catch on-error))))))
@@ -831,15 +831,9 @@
(ptk/reify ::set-file-thumbnail
ptk/UpdateEvent
(update [_ state]
(letfn [(update-search-files [files]
(->> files
(mapv #(cond-> %
(= file-id (:id %))
(assoc :thumbnail-uri thumbnail-uri)))))]
(-> state
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-when :dashboard-search-result update-search-files))))))
(-> state
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)))))
;; --- EVENT: create-file

View File

@@ -17,10 +17,9 @@
[app.util.object :as obj]
[app.util.storage :refer [storage]]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[beicon.v2.operators :as rxo]
[beicon.core :as rx]
[lambdaisland.uri :as u]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(l/set-level! :info)
@@ -239,7 +238,7 @@
profile (->> (rx/from-atom storage {:emit-current-value? true})
(rx/map :profile)
(rx/map :id)
(rx/pipe (rxo/distinct-contiguous)))]
(rx/dedupe))]
(l/debug :hint "event instrumentation initialized")
@@ -260,12 +259,12 @@
(l/debug :hint "events chunk persisted" :total (count chunk))))
(rx/map (constantly chunk))))))
(rx/take-until stoper)
(rx/subs! (fn [chunk]
(swap! buffer remove-from-buffer (count chunk)))
(fn [cause]
(l/error :hint "unexpected error on audit persistence" :cause cause))
(fn []
(l/debug :hint "audit persistence terminated"))))
(rx/subs (fn [chunk]
(swap! buffer remove-from-buffer (count chunk)))
(fn [cause]
(l/error :hint "unexpected error on audit persistence" :cause cause))
(fn []
(l/debug :hint "audit persistence terminated"))))
(->> stream
(rx/with-latest-from profile)
@@ -291,10 +290,10 @@
(rx/switch-map #(rx/timer (inst-ms session-timeout)))
(rx/take-until stoper)
(rx/subs! (fn [_]
(l/debug :hint "session reinitialized")
(reset! session nil))
(fn [cause]
(l/error :hint "error on event batching stream" :cause cause))
(fn []
(l/debug :hitn "events batching stream terminated")))))))))
(rx/subs (fn [_]
(l/debug :hint "session reinitialized")
(reset! session nil))
(fn [cause]
(l/error :hint "error on event batching stream" :cause cause))
(fn []
(l/debug :hitn "events batching stream terminated")))))))))

View File

@@ -15,8 +15,8 @@
[app.util.dom :as dom]
[app.util.time :as dt]
[app.util.websocket :as ws]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(def default-timeout 5000)

View File

@@ -19,9 +19,9 @@
[app.util.i18n :refer [tr]]
[app.util.storage :refer [storage]]
[app.util.webapi :as wa]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General purpose events & IMPL

View File

@@ -11,7 +11,7 @@
[app.main.data.messages :as dm]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]))

View File

@@ -9,8 +9,8 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.schema :as sm]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(declare hide)
(declare show)

View File

@@ -10,7 +10,7 @@
[app.common.uuid :as uuid]
[app.main.store :as st]
[cljs.core :as c]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(defonce components (atom {}))

View File

@@ -16,10 +16,10 @@
[app.main.refs :as refs]
[app.util.code-gen :as cg]
[app.util.timers :as ts]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[clojure.set :as set]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(def style-type "css")
(def markup-type "html")
@@ -80,7 +80,7 @@
(rx/merge-map fonts/fetch-font-css)
(rx/reduce conj [])
(rx/map #(str/join "\n" %))
(rx/subs!
(rx/subs
(fn [fontfaces-css]
(let [style-code
(dm/str

View File

@@ -13,7 +13,7 @@
[app.common.schema :as sm]
[app.config :as cf]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(log/set-level! :warn)

View File

@@ -21,8 +21,8 @@
[app.util.i18n :as i18n]
[app.util.router :as rt]
[app.util.storage :refer [storage]]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; --- SCHEMAS
@@ -434,7 +434,7 @@
(rx/map di/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :update-profile-photo %))
(rx/tap on-success)
(rx/do on-success)
(rx/map (constantly (fetch-profile)))
(rx/catch on-error))))))

View File

@@ -21,8 +21,8 @@
[app.main.repo :as rp]
[app.util.globals :as ug]
[app.util.router :as rt]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; --- Local State Initialization

View File

@@ -11,8 +11,8 @@
[app.common.uri :as u]
[app.config :as cf]
[app.util.websocket :as ws]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(l/set-level! :error)
@@ -44,7 +44,7 @@
(ptk/reify ::initialize
ptk/WatchEvent
(watch [_ state stream]
(l/trace :hint "initialize" :fn "watch")
(l/trace :hint "event:initialize" :fn "watch")
(let [sid (:session-id state)
uri (prepare-uri {:session-id sid})
ws (ws/create uri)]

View File

@@ -78,10 +78,10 @@
[app.util.router :as rt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(def default-workspace-local {:zoom 1})
@@ -444,7 +444,7 @@
uris (into #{} xform (wsh/lookup-page-objects state page-id))]
(->> (rx/from uris)
(rx/subs! #(http/fetch-data-uri % false)))))))
(rx/subs #(http/fetch-data-uri % false)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Workspace Page CRUD
@@ -872,10 +872,10 @@
(pcb/update-shapes [parent-id] #(ctl/add-children-to-index % ids objects to-index)))
(pcb/update-shapes parents
(fn [parent objects]
(fn [parent]
(cond-> parent
(ctl/grid-layout? parent)
(ctl/assign-cells objects))))
(ctl/assign-cells))))
(pcb/reorder-grid-children parents)

View File

@@ -18,9 +18,9 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(defn selected-shapes-idx
[state]

View File

@@ -20,8 +20,8 @@
[app.main.data.workspace.undo :as dwu]
[app.main.store :as st]
[app.main.worker :as uw]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn)
@@ -70,7 +70,7 @@
update-layout-ids
(->> ids
(map (d/getf objects))
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs})))
(filter #(some update-layout-attr? (pcb/changed-attrs % update-fn {:attrs attrs})))
(map :id))
changes (reduce

View File

@@ -8,7 +8,7 @@
(:require
[app.common.files.helpers :as cfh]
[app.common.uuid :as uuid]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
;; --- Shape attrs (Layers Sidebar)

View File

@@ -23,8 +23,8 @@
[app.main.data.workspace.undo :as dwu]
[app.util.color :as uc]
[app.util.storage :refer [storage]]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; A set of keys that are used for shared state identifiers
(def ^:const colorpicker-selected-broadcast-key ::colorpicker-selected)
@@ -353,7 +353,7 @@
;; Stream that updates the stroke/width and stops if `esc` pressed
(->> sub
(rx/take-until stop?)
(rx/merge-map update-events))
(rx/flat-map update-events))
;; Hide the modal if the stop event is emitted
(->> stop?

View File

@@ -22,8 +22,8 @@
[app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.router :as rt]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(declare handle-interrupt)
(declare handle-comment-layer-click)

View File

@@ -13,8 +13,8 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.util.router :as rt]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn)

View File

@@ -14,8 +14,8 @@
[app.main.data.workspace.drawing.common :as common]
[app.main.data.workspace.drawing.curve :as curve]
[app.main.data.workspace.path :as path]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(declare start-drawing)
(declare handle-drawing)

View File

@@ -24,10 +24,9 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.snap :as snap]
[app.main.streams :as ms]
[app.util.array :as array]
[app.util.mouse :as mse]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn adjust-ratio
[point initial]
@@ -129,11 +128,12 @@
(->> ms/mouse-position
(rx/filter #(> (gpt/distance % initial) (/ 2 zoom)))
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-mod)
(rx/with-latest vector ms/mouse-position-shift)
(rx/with-latest conj ms/mouse-position-mod)
(rx/switch-map
(fn [[point :as current]]
(->> (snap/closest-snap-point page-id [shape] objects layout zoom focus point)
(rx/map (partial array/conj current)))))
(rx/map #(conj current %)))))
(rx/map
(fn [[_ shift? mod? point]]
#(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-prec)) shift? mod?)))))

View File

@@ -16,8 +16,8 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.worker :as uw]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn clear-drawing
[]

View File

@@ -23,8 +23,8 @@
[app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.path.simplify-curve :as ups]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(def simplify-tolerance 0.3)

View File

@@ -10,8 +10,8 @@
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn interrupt? [e] (= e :interrupt))

View File

@@ -10,8 +10,8 @@
[app.common.geom.shapes :as gsh]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; This event will update the file so the boolean data has a pre-generated path data
;; to increase performance.

View File

@@ -7,8 +7,8 @@
(ns app.main.data.workspace.fix-broken-shapes
(:require
[app.main.data.workspace.changes :as dch]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn- generate-broken-link-changes
[attr {:keys [objects id] :as container}]

View File

@@ -12,8 +12,8 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.fonts :as fonts]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; This event will update the file so the texts with non existing custom fonts try to be fixed.
;; This can happen when:

View File

@@ -12,8 +12,8 @@
[app.common.files.changes-builder :as pcb]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Grid

View File

@@ -9,7 +9,7 @@
[app.common.geom.rect :as grc]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.state-helpers :as wsh]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(defn hover-grid-cell
[grid-id cell-id add-to-set]

View File

@@ -10,8 +10,8 @@
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.store :as st]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts

View File

@@ -19,8 +19,8 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn shapes-for-grouping
[objects selected]

View File

@@ -13,8 +13,8 @@
[app.common.types.page :as ctp]
[app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn make-update-guide [guide]
(fn [other]

View File

@@ -8,7 +8,7 @@
(:require
[app.common.data.macros :as dm]
[clojure.set :as set]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
;; --- Manage shape's highlight status

View File

@@ -20,8 +20,8 @@
[app.main.data.workspace.undo :as dwu]
[app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; --- Flows

View File

@@ -11,9 +11,9 @@
[app.common.math :as mth]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
;; -- Opacity ----------------------------------------------------------

View File

@@ -10,7 +10,7 @@
[app.common.data.macros :as dm]
[app.util.storage :refer [storage]]
[clojure.set :as set]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(def valid-flags
#{:sitemap

View File

@@ -45,9 +45,9 @@
[app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
(log/set-level! :warn)
@@ -754,18 +754,6 @@
(dch/commit-changes (assoc nonlocal-changes
:file-id file-id)))))))))))
(defn- update-component-thumbnail-sync
[state component-id file-id tag]
(let [current-file-id (:current-file-id state)
current-file? (= current-file-id file-id)
data (if current-file?
(get state :workspace-data)
(get-in state [:workspace-libraries file-id :data]))
component (ctkl/get-component data component-id)
page-id (:main-instance-page component)
root-id (:main-instance-id component)]
(dwt/request-thumbnail file-id page-id root-id tag)))
(defn update-component-sync
([shape-id file-id] (update-component-sync shape-id file-id nil))
([shape-id file-id undo-group]
@@ -773,18 +761,14 @@
ptk/WatchEvent
(watch [_ state _]
(let [current-file-id (:current-file-id state)
current-file? (= current-file-id file-id)
page (wsh/lookup-page state)
shape (ctn/get-shape page shape-id)
component-id (:component-id shape)
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(update-component shape-id undo-group)
(sync-file current-file-id file-id :components (:component-id shape) undo-group)
(update-component-thumbnail-sync state component-id file-id "frame")
(update-component-thumbnail-sync state component-id file-id "component")
(when (not current-file?)
(when (not= current-file-id file-id)
(sync-file file-id file-id :components (:component-id shape) undo-group))
(dwu/commit-undo-transaction undo-id)))))))
@@ -811,8 +795,7 @@
(ptk/reify ::update-component-thumbnail
ptk/WatchEvent
(watch [_ state _]
(rx/of (update-component-thumbnail-sync state component-id file-id "component"))
#_(let [data (get state :workspace-data)
(let [data (get state :workspace-data)
component (ctkl/get-component data component-id)
page-id (:main-instance-page component)
root-id (:main-instance-id component)]

View File

@@ -182,10 +182,10 @@
(-> changes
(pcb/update-shapes
[(:parent-id first-shape)]
(fn [shape objects]
(fn [shape]
(-> shape
(ctl/push-into-cell [(:id first-shape)] row column)
(ctl/assign-cells objects))))
(ctl/assign-cells))))
(pcb/reorder-grid-children [(:parent-id first-shape)])))
changes)
@@ -603,23 +603,6 @@
(generate-detach-instance changes container shape-id))))
changes)))
(defn- find-main-container
"Find the container that has the main shape."
[container-inst shape-inst shape-main library component]
(loop [shape-inst' shape-inst
component' component]
(let [container (ctf/get-component-container library component')] ; TODO: this won't work if some intermediate component is in a different library
(if (some? (ctn/get-shape container (:id shape-main))) ; for this to work we need to have access to the libraries list here
container
(let [parent (ctn/get-shape container-inst (:parent-id shape-inst'))
shape-inst' (ctn/get-head-shape (:objects container-inst) parent)
component' (or (ctkl/get-component library (:component-id shape-inst'))
(ctkl/get-deleted-component library (:component-id shape-inst')))]
(if (some? component)
(recur shape-inst'
component')
nil))))))
(defn- generate-sync-shape-direct-recursive
[changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2]
(log/debug :msg "Sync shape direct recursive"
@@ -656,7 +639,7 @@
set-remote-synced?
(change-remote-synced shape-inst container true))
component-container (find-main-container container shape-inst shape-main library component)
component-container (ctf/get-component-container library component)
children-inst (vec (ctn/get-direct-children container shape-inst))
children-main (vec (ctn/get-direct-children component-container shape-main))
@@ -965,11 +948,11 @@
(:id parent-shape)
(:frame-id parent-shape)))
set-remote-synced?
(assoc :remote-synced true)
(nil? (:shape-ref original-shape))
(assoc :shape-ref (:id original-shape))
:always
(assoc :shape-ref (:id original-shape)))))
set-remote-synced?
(assoc :remote-synced true))))
update-original-shape (fn [original-shape _new-shape]
original-shape)

View File

@@ -30,9 +30,9 @@
[app.main.store :as st]
[app.util.http :as http]
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.v2.core :as ptk]
[potok.core :as ptk]
[promesa.core :as p]
[tubax.core :as tubax]))
@@ -122,13 +122,13 @@
(->> (rx/from uris)
(rx/filter (comp not svg-url?))
(rx/mapcat upload)
(rx/tap on-image))
(rx/do on-image))
(->> (rx/from uris)
(rx/filter svg-url?)
(rx/merge-map (partial fetch-svg name))
(rx/merge-map svg->clj)
(rx/tap on-svg)))))
(rx/do on-svg)))))
(defn- process-blobs
[{:keys [file-id local? name blobs force-media on-image on-svg]}]
@@ -154,14 +154,14 @@
(rx/filter (comp not svg-blob?))
(rx/map prepare-blob)
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
(rx/tap on-image))
(rx/do on-image))
(->> (rx/from blobs)
(rx/map dmm/validate-file)
(rx/filter svg-blob?)
(rx/merge-map extract-content)
(rx/merge-map svg->clj)
(rx/tap on-svg)))))
(rx/do on-svg)))))
(defn handle-media-error [error on-error]
(if (ex/ex-info? error)
@@ -278,7 +278,7 @@
(rx/map dmm/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
(rx/tap on-upload-success)
(rx/do on-upload-success)
(rx/catch handle-media-error))))))
;; --- Upload File Media objects
@@ -423,7 +423,7 @@
:timeout nil
:tag :media-loading}))
(->> (rp/cmd! :clone-file-media-object params)
(rx/tap on-success)
(rx/do on-success)
(rx/catch on-error)
(rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))

View File

@@ -27,8 +27,8 @@
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
;; -- temporary modifiers -------------------------------------------
@@ -191,14 +191,14 @@
;; Temporary remove the children when moving them
frame (-> frame
(update :shapes #(d/removev ids %))
(ctl/assign-cells objects))
(ctl/assign-cells))
ids (->> ids (remove #(ctl/position-absolute? objects %)))
frame (-> frame
(update :shapes d/concat-vec ids)
(cond-> (some? cell)
(ctl/push-into-cell ids row column))
(ctl/assign-cells objects))]
(ctl/assign-cells))]
(-> modifiers
(ctm/change-property :layout-grid-rows (:layout-grid-rows frame))

View File

@@ -19,11 +19,10 @@
[app.util.globals :refer [global]]
[app.util.mouse :as mse]
[app.util.object :as obj]
[app.util.rxops :as rxs]
[app.util.time :as dt]
[beicon.v2.core :as rx]
[beicon.core :as rx]
[clojure.set :as set]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(declare process-message)
(declare handle-presence)
@@ -83,7 +82,7 @@
;; position changes.
(->> stream
(rx/filter mse/pointer-event?)
(rx/pipe (rxs/throttle 100))
(rx/sample 50)
(rx/map #(handle-pointer-send file-id (:pt %)))))
(rx/take-until stoper))]
@@ -133,7 +132,7 @@
"#faa6b7" ; salmon
"#f9b489" ; orange
"#fdcd79" ; soft-orange
"#dee563" ; yellow -> default presence color
"#dee563" ; yellow
"#b1e96f" ; yellow-green
})

View File

@@ -13,8 +13,8 @@
[app.main.data.workspace.path.helpers :as helpers]
[app.main.data.workspace.path.state :as st]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
[beicon.core :as rx]
[potok.core :as ptk]))
(defn generate-path-changes
"Generates changes to update the new content of the shape"

View File

@@ -8,7 +8,7 @@
(:require
[app.common.schema :as sm]
[app.main.data.workspace.path.state :as st]
[potok.v2.core :as ptk]))
[potok.core :as ptk]))
(def valid-commands
#{:move-to

Some files were not shown because too many files have changed in this diff Show More