diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn
index 0c1db4d050..18daa1480b 100644
--- a/.clj-kondo/config.edn
+++ b/.clj-kondo/config.edn
@@ -15,7 +15,7 @@
:hooks
{:analyze-call
{app.common.data.macros/export hooks.export/export
- potok.core/reify hooks.export/potok-reify
+ potok.v2.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
diff --git a/backend/resources/log4j2-devenv.xml b/backend/resources/log4j2-devenv.xml
index 4fd93925c9..a37eb18010 100644
--- a/backend/resources/log4j2-devenv.xml
+++ b/backend/resources/log4j2-devenv.xml
@@ -30,7 +30,6 @@
-
diff --git a/backend/src/app/features/components_v2.clj b/backend/src/app/features/components_v2.clj
index 19b4f0b9df..99074dd598 100644
--- a/backend/src/app/features/components_v2.clj
+++ b/backend/src/app/features/components_v2.clj
@@ -9,7 +9,6 @@
[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]
@@ -769,12 +768,13 @@
fdata (migrate-graphics fdata)]
(update fdata :options assoc :components-v2 true)))))
-(defn- prepare-fdata
- [fdata id]
- (-> fdata
- (assoc :id id)
- (fdata/process-pointers deref)
- (fmg/migrate-data)))
+(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?]
@@ -788,18 +788,10 @@
(defn- process-file
[{:keys [::db/conn] :as system} id & {:keys [validate? throw-on-validate?]}]
- (let [file (binding [cfeat/*new* (atom #{})
- pmap/*load-fn* (partial fdata/load-pointer system id)]
- (-> (files/get-file system id :migrate? false)
- (update :data prepare-fdata id)
- (update :features into (deref cfeat/*new*))
- (update :features cfeat/migrate-legacy-features)))
+ (let [file (get-file system id)
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 prepare-fdata id))))))
+ (into [file] (comp (map :id) (map (partial get-file system))))
(d/index-by :id))
file (-> file
diff --git a/backend/src/app/http/errors.clj b/backend/src/app/http/errors.clj
index 0e8e065ad2..580cd67039 100644
--- a/backend/src/app/http/errors.clj
+++ b/backend/src/app/http/errors.clj
@@ -81,6 +81,7 @@
(cond
(or (= code :spec-validation)
(= code :params-validation)
+ (= code :schema-validation)
(= code :data-validation))
(let [explain (ex/explain data)]
{::rres/status 400
diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj
index 13a16c2770..7e7327b32e 100644
--- a/backend/src/app/rpc/commands/binfile.clj
+++ b/backend/src/app/rpc/commands/binfile.clj
@@ -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 pmg]
+ [app.common.files.migrations :as fmg]
[app.common.files.validate :as fval]
[app.common.fressian :as fres]
[app.common.logging :as l]
@@ -701,23 +701,29 @@
(update :object-id #(str/replace-first % #"^(.*?)/" (str file-id "/")))))
thumbnails))
-(defn- process-fdata
- [fdata id]
- (-> fdata
- (dissoc :recent-colors)
- (assoc :id id)
- (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
- (assoc :version 22)
- (pmg/migrate-data)
- (update :pages-index relink-shapes)
- (update :components relink-shapes)
- (update :media relink-media)))
+(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
@@ -730,19 +736,7 @@
file-id (:id file)
file-id' (lookup-index file-id)
- 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']))
+ thumbnails (:thumbnails file)]
(when (not= file-id expected-file-id)
(ex/raise :type :validation
@@ -773,16 +767,28 @@
(l/dbg :hint "update media references" ::l/sync? true)
(vswap! *state* update :media into (map #(update % :id lookup-index)) media))
- (let [file (binding [cfeat/*new* (atom #{})]
- (-> 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 process-fdata file-id')
- (update :features into (deref cfeat/*new*))))
+ (let [file (-> file
+ (assoc :id file-id')
+ (process-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']))
+
+ 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))))))
_ (when (contains? cf/flags :file-schema-validation)
(fval/validate-file-schema! file))
@@ -803,7 +809,6 @@
file))
file)
-
file (-> file
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))]
diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj
index a0cc12e438..096e961957 100644
--- a/backend/src/app/rpc/commands/files.clj
+++ b/backend/src/app/rpc/commands/files.clj
@@ -224,11 +224,8 @@
(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)
- cfeat/*new* (atom #{})]
- (let [file (-> (fmg/migrate-file file)
- (update :features into (deref cfeat/*new*))
- (update :features cfeat/migrate-legacy-features))]
+ pmap/*tracked* (pmap/create-tracked)]
+ (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
@@ -250,11 +247,13 @@
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}}]
+ lock-for-update? false
+ migrate? true}}]
(dm/assert!
"expected cfg with valid connection"
(db/connection-map? cfg))
diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj
index c60338349f..19f36072f4 100644
--- a/backend/src/app/rpc/commands/files_thumbnails.clj
+++ b/backend/src/app/rpc/commands/files_thumbnails.clj
@@ -10,6 +10,7 @@
[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]
@@ -105,24 +106,12 @@
(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 [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))))))
+ (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)))))
;; function responsible to filter objects data structure of
;; all unneeded shapes if a concrete frame is provided. If no
@@ -166,30 +155,29 @@
objects)))]
- (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))
+ (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
@@ -221,7 +209,10 @@
:profile-id profile-id
:file-id file-id)
- file (files/get-file cfg 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)))]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
diff --git a/backend/src/app/rpc/commands/management.clj b/backend/src/app/rpc/commands/management.clj
index 2928830ca6..80287e9aeb 100644
--- a/backend/src/app/rpc/commands/management.clj
+++ b/backend/src/app/rpc/commands/management.clj
@@ -106,41 +106,34 @@
media
media))
- (update-fdata [fdata new-id]
- (-> fdata
- (assoc :id new-id)
- (feat.fdata/process-pointers deref)
- (pmg/migrate-data)
- (update :pages-index relink-shapes)
- (update :components relink-shapes)
- (update :media relink-media)
- (d/without-nils)))]
+ (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))))))]
- (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 (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
+ (-> (assoc file :id new-id)
+ (process-file)))
- (let [new-id (get index id)
- file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
- cfeat/*new* (atom #{})]
- (-> file
- (assoc :id new-id)
- (update :data update-fdata new-id)
- (update :features into (deref cfeat/*new*))
- (update :features cfeat/migrate-legacy-features)))
+ file (if (contains? (:features file) "fdata/objects-map")
+ (feat.fdata/enable-objects-map file)
+ 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))))
+ 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)))
(def sql:get-used-libraries
"select flr.*
diff --git a/backend/src/app/rpc/commands/search.clj b/backend/src/app/rpc/commands/search.clj
index 1c4026d6db..5710156458 100644
--- a/backend/src/app/rpc/commands/search.clj
+++ b/backend/src/app/rpc/commands/search.clj
@@ -9,6 +9,7 @@
[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]))
@@ -37,12 +38,15 @@
)
select distinct
f.id,
+ f.revn,
f.project_id,
f.created_at,
f.modified_at,
f.name,
- f.is_shared
+ f.is_shared,
+ ft.media_id
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())
@@ -50,10 +54,16 @@
(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]))
+ (->> (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))))))
(s/def ::team-id ::us/uuid)
(s/def ::search-files ::us/string)
diff --git a/backend/src/app/util/websocket.clj b/backend/src/app/util/websocket.clj
index 094ec37c38..38276e743c 100644
--- a/backend/src/app/util/websocket.clj
+++ b/backend/src/app/util/websocket.clj
@@ -103,7 +103,7 @@
{:on-open
(fn on-open [channel]
- (l/trace :fn "on-open" :conn-id id :channel channel)
+ (l/dbg :fn "on-open" :conn-id (str id))
(let [options (-> options
(assoc ::channel channel)
(on-connect))
@@ -114,10 +114,10 @@
:on-close
(fn on-close [_channel code reason]
- (l/info :fn "on-ws-terminate"
- :conn-id id
- :code code
- :reason reason)
+ (l/dbg :fn "on-close"
+ :conn-id (str id)
+ :code code
+ :reason reason)
(sp/close! close-ch))
:on-error
@@ -132,18 +132,19 @@
: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/trace :hint "send ping" :beat beat-id :conn-id id)
+ (l/trc :hint "send ping" :beat beat-id :conn-id (str 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]
@@ -154,14 +155,16 @@
(identical? p ping-ch)
(if (handle-ping! wsp i)
(recur (inc i))
- (rws/close channel 8802 "missing to many pings"))
+ (do
+ (l/trc :hint "closing" :reason "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/trace :hint "pong" :beat beat :conn-id id)
+ (l/trc :hint "pong received" :beat beat :conn-id (str id))
(swap! beats disj beat)
(recur i))
@@ -179,7 +182,6 @@
(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))))))
@@ -188,12 +190,12 @@
(catch java.io.IOException _)
(catch InterruptedException _cause
- (l/debug :hint "websocket thread interrumpted" :conn-id id))
+ (l/dbg :hint "websocket thread interrumpted" :conn-id id))
(catch Throwable cause
- (l/error :hint "unhandled exception on websocket thread"
- :conn-id id
- :cause cause))
+ (l/err :hint "unhandled exception on websocket thread"
+ :conn-id id
+ :cause cause))
(finally
(try
(handler wsp {:type :close})
@@ -212,4 +214,4 @@
(catch Throwable cause
(throw cause)))
- (l/trace :hint "websocket thread terminated" :conn-id id))))
+ (l/trc :hint "websocket thread terminated" :conn-id id))))
diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc
index aa69184402..7efc11c311 100644
--- a/common/src/app/common/files/changes_builder.cljc
+++ b/common/src/app/common/files/changes_builder.cljc
@@ -358,13 +358,13 @@
(defn changed-attrs
"Returns the list of attributes that will change when `update-fn` is applied"
- [object update-fn {:keys [attrs]}]
+ [object objects 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)]
+ new-obj (update-fn object objects)]
(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)]
+ new-obj (update-fn old-obj objects)]
(if (= old-obj new-obj)
changes
(let [[rops uops] (-> (or attrs (d/concat-set (keys old-obj) (keys new-obj)))
diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc
index 9411566f19..f26a689f4a 100644
--- a/common/src/app/common/files/migrations.cljc
+++ b/common/src/app/common/files/migrations.cljc
@@ -42,18 +42,21 @@
(reduce migrate-fn data (range (:version data 0) to-version))))))
(defn migrate-file
- [{:keys [id data] :as file}]
- (let [data (assoc data :id id)]
- (-> file
- (assoc ::orig-version (:version data))
- (assoc :data (migrate-data data)))))
+ [{: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))))
(defn migrated?
- [{:keys [data] :as file}]
- (or (::migrated file)
- (let [version (:version data)]
- (> version
- (::orig-version file version)))))
+ [file]
+ (true? (-> file meta ::migrated)))
;; Default handler, noop
(defmethod migrate :default [data] data)
diff --git a/common/src/app/common/geom/rect.cljc b/common/src/app/common/geom/rect.cljc
index 62222b707f..5fa77b8d3d 100644
--- a/common/src/app/common/geom/rect.cljc
+++ b/common/src/app/common/geom/rect.cljc
@@ -353,3 +353,19 @@
(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))
diff --git a/common/src/app/common/thumbnails.cljc b/common/src/app/common/thumbnails.cljc
index 8ab11c8ad9..90eb310a0c 100644
--- a/common/src/app/common/thumbnails.cljc
+++ b/common/src/app/common/thumbnails.cljc
@@ -1,7 +1,18 @@
(ns app.common.thumbnails
- (:require [cuerdas.core :as str]))
+ (:require
+ [app.common.uuid :as uuid]
+ [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 "/"))))
diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc
index f384c96e30..30a866b76a 100644
--- a/common/src/app/common/types/shape/layout.cljc
+++ b/common/src/app/common/types/shape/layout.cljc
@@ -676,7 +676,7 @@
[id cell])))
(defn remove-grid-column
- [parent index]
+ [parent index objects]
(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))))
+ (assign-cells objects))))
(defn remove-grid-row
- [parent index]
+ [parent index objects]
(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))))
+ (assign-cells objects))))
(defn- reorder-grid-tracks
"Swap the positions of the tracks info"
@@ -781,6 +781,7 @@
parent
(reorder-grid-tracks parent tracks-props from-index to-index)]
+
(cond-> parent
move-content?
(swap-track-content prop from-track to-track))))
@@ -828,14 +829,24 @@
(defn check-deassigned-cells
"Clean the cells whith shapes that are no longer in the layout"
- [parent]
+ [parent objects]
- (let [child? (set (:shapes parent))
- cells (update-vals
- (:layout-grid-cells parent)
- (fn [cell] (update cell :shapes #(filterv child? %))))]
+ (let [child-set (set (:shapes parent))
- (assoc parent :layout-grid-cells cells)))
+ 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))))
(defn overlapping-cells
"Find overlapping cells"
@@ -902,12 +913,12 @@
;; - Shape duplication
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
(defn assign-cells
- [parent]
+ [parent objects]
(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)
+ parent (cond-> (check-deassigned-cells parent objects)
#_(d/not-empty? overlaps)
#_(fix-overlaps overlaps))
@@ -915,7 +926,9 @@
(into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent))
no-cell-shapes
- (->> (:shapes parent) (remove shape-has-cell?))
+ (->> (:shapes parent)
+ (remove shape-has-cell?)
+ (remove (partial position-absolute? objects)))
parent (position-auto-shapes parent)]
@@ -1174,7 +1187,7 @@
(let [;; Temporary remove the children when moving them
frame (-> frame
(update :shapes #(d/removev children %))
- (assign-cells))
+ (assign-cells objects))
children (->> children (remove #(position-absolute? objects %)))]
@@ -1182,7 +1195,7 @@
(update :shapes d/concat-vec children)
(cond-> (some? cell)
(push-into-cell children row column))
- (assign-cells))))
+ (assign-cells objects))))
(defn add-children-to-index
[parent ids objects to-index]
diff --git a/frontend/deps.edn b/frontend/deps.edn
index b30b74b78f..c96ab0a3ee 100644
--- a/frontend/deps.edn
+++ b/frontend/deps.edn
@@ -6,10 +6,17 @@
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/potok {:mvn/version "2022.12.16-71"}
+
+ 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/rumext
{:git/tag "v2.7"
diff --git a/frontend/package.json b/frontend/package.json
index 2b9c7213a9..fd42e11cd3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -46,6 +46,7 @@
"@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",
@@ -73,6 +74,7 @@
"sass": "^1.66.1",
"shadow-cljs": "2.26.2",
"storybook": "^7.5.3",
+ "typescript": "^5.3.3",
"vite": "^5.0.2"
},
"dependencies": {
@@ -90,7 +92,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-virtualized": "^9.22.3",
- "rxjs": "~7.8.1",
+ "rxjs": "8.0.0-alpha.13",
"sax": "^1.2.4",
"source-map-support": "^0.5.21",
"tdigest": "^0.1.2",
diff --git a/frontend/resources/fonts/RobotoMono-Regular.ttf b/frontend/resources/fonts/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000..6df2b25360
Binary files /dev/null and b/frontend/resources/fonts/RobotoMono-Regular.ttf differ
diff --git a/frontend/resources/fonts/SpaceMono-Bold.ttf b/frontend/resources/fonts/SpaceMono-Bold.ttf
deleted file mode 100644
index 20e3449515..0000000000
Binary files a/frontend/resources/fonts/SpaceMono-Bold.ttf and /dev/null differ
diff --git a/frontend/resources/fonts/SpaceMono-BoldItalic.ttf b/frontend/resources/fonts/SpaceMono-BoldItalic.ttf
deleted file mode 100644
index ff2ea5a5c2..0000000000
Binary files a/frontend/resources/fonts/SpaceMono-BoldItalic.ttf and /dev/null differ
diff --git a/frontend/resources/fonts/SpaceMono-Italic.ttf b/frontend/resources/fonts/SpaceMono-Italic.ttf
deleted file mode 100644
index f36282f261..0000000000
Binary files a/frontend/resources/fonts/SpaceMono-Italic.ttf and /dev/null differ
diff --git a/frontend/resources/fonts/SpaceMono-Regular.ttf b/frontend/resources/fonts/SpaceMono-Regular.ttf
deleted file mode 100644
index 04e56b923f..0000000000
Binary files a/frontend/resources/fonts/SpaceMono-Regular.ttf and /dev/null differ
diff --git a/frontend/resources/images/icons/expand-refactor.svg b/frontend/resources/images/icons/expand-refactor.svg
new file mode 100644
index 0000000000..e63fd74c87
--- /dev/null
+++ b/frontend/resources/images/icons/expand-refactor.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/login-illustration.svg b/frontend/resources/images/icons/login-illustration.svg
index a10e27fd94..6e6b7394a3 100644
--- a/frontend/resources/images/icons/login-illustration.svg
+++ b/frontend/resources/images/icons/login-illustration.svg
@@ -1,666 +1,686 @@
-