mirror of
https://github.com/penpot/penpot.git
synced 2026-02-05 12:12:07 -05:00
Compare commits
23 Commits
1.6.2-alph
...
1.6.4-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb5804cde3 | ||
|
|
7819757759 | ||
|
|
b861e261ed | ||
|
|
17b32d6518 | ||
|
|
d2359046c4 | ||
|
|
8a700170b0 | ||
|
|
8c68e29bf3 | ||
|
|
1a81631886 | ||
|
|
634fe2c458 | ||
|
|
053d46144e | ||
|
|
b2e7bb6be1 | ||
|
|
bae709df5b | ||
|
|
1b495ebad1 | ||
|
|
4e0289b341 | ||
|
|
056fce9187 | ||
|
|
9f034c7e7e | ||
|
|
a6de4e3742 | ||
|
|
2d6a375afc | ||
|
|
585e5d0199 | ||
|
|
71524fe649 | ||
|
|
55d2768807 | ||
|
|
3d7a3f27d5 | ||
|
|
46448bc5c7 |
@@ -1,7 +1,12 @@
|
||||
{:lint-as {potok.core/reify clojure.core/reify
|
||||
promesa.core/let clojure.core/let
|
||||
rumext.alpha/defc clojure.core/defn
|
||||
app.common.data/export clojure.core/def
|
||||
app.db/with-atomic clojure.core/with-open}
|
||||
|
||||
:hooks
|
||||
{:analyze-call {app.common.data/export hooks.export/export}}
|
||||
|
||||
:output
|
||||
{:exclude-files ["data_readers.clj"]}
|
||||
|
||||
|
||||
11
.clj-kondo/hooks/export.clj
Normal file
11
.clj-kondo/hooks/export.clj
Normal file
@@ -0,0 +1,11 @@
|
||||
(ns hooks.export
|
||||
(:require [clj-kondo.hooks-api :as api]))
|
||||
|
||||
(defn export
|
||||
[{:keys [:node]}]
|
||||
(let [[_ sname] (:children node)
|
||||
result (api/list-node
|
||||
[(api/token-node (symbol "def"))
|
||||
(api/token-node (symbol (name (:value sname))))
|
||||
sname])]
|
||||
{:node result}))
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,6 +30,8 @@ node_modules
|
||||
/exporter/target
|
||||
/exporter/.shadow-cljs
|
||||
/docker/images/bundle*
|
||||
/common/.shadow-cljs
|
||||
/common/target
|
||||
/.clj-kondo/.cache
|
||||
/bundle*
|
||||
/media
|
||||
|
||||
29
CHANGES.md
29
CHANGES.md
@@ -1,6 +1,5 @@
|
||||
# CHANGELOG #
|
||||
|
||||
|
||||
## :rocket: Next
|
||||
|
||||
### :sparkles: New features
|
||||
@@ -10,6 +9,34 @@
|
||||
### :boom: Breaking changes
|
||||
### :heart: Community contributions by (Thank you!)
|
||||
|
||||
## 1.6.4-alpha
|
||||
|
||||
### :sparkles: Minor improvements
|
||||
|
||||
- Decrease default bulk buffers on storage tasks.
|
||||
- Reduce file_change preserve interval to 24h.
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Don't allow rename drafts project.
|
||||
- Fix custom font deletion task.
|
||||
- Fix custom font rendering on exporting shapes.
|
||||
- Fix font loading on viewer app.
|
||||
- Fix problem when moving files with drag & drop.
|
||||
- Fix unexpected exception on searching without term.
|
||||
- Properly handle nil values on `update-shapes` function.
|
||||
- Replace frame term usage by artboard on viewer app.
|
||||
|
||||
|
||||
## 1.6.3-alpha
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix problem with merge and join nodes [#990](https://github.com/penpot/penpot/issues/990)
|
||||
- Fix problem with empty path editing.
|
||||
- Fix problem with create component.
|
||||
- Fix problem with move-objects.
|
||||
- Fix problem with merge and join nodes.
|
||||
|
||||
## 1.6.2-alpha
|
||||
|
||||
|
||||
@@ -140,10 +140,13 @@
|
||||
(when-let [[reason batch] (a/<! input)]
|
||||
(let [result (a/<! (update-sessions cfg batch))]
|
||||
(mcnt :inc)
|
||||
(if (ex/exception? result)
|
||||
(cond
|
||||
(ex/exception? result)
|
||||
(l/error :task "updater"
|
||||
:hint "unexpected error on update sessions"
|
||||
:cause result)
|
||||
|
||||
(= :size reason)
|
||||
(l/debug :task "updater"
|
||||
:action "update sessions"
|
||||
:reason (name reason)
|
||||
|
||||
@@ -75,15 +75,12 @@
|
||||
[_ {:keys [enabled] :as cfg}]
|
||||
(when enabled
|
||||
(l/info :msg "intializing audit collector")
|
||||
(let [input (a/chan 1 event-xform)
|
||||
(let [input (a/chan 512 event-xform)
|
||||
buffer (aa/batch input {:max-batch-size 100
|
||||
:max-batch-age (* 5 1000)
|
||||
:max-batch-age (* 10 1000) ; 10s
|
||||
:init []})]
|
||||
(a/go-loop []
|
||||
(when-let [[type events] (a/<! buffer)]
|
||||
(l/debug :action "persist-events (batch)"
|
||||
:reason (name type)
|
||||
:count (count events))
|
||||
(when-let [[_type events] (a/<! buffer)]
|
||||
(let [res (a/<! (persist-events cfg events))]
|
||||
(when (ex/exception? res)
|
||||
(l/error :hint "error on persiting events"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
[_ {:keys [receiver uri] :as cfg}]
|
||||
(when uri
|
||||
(l/info :msg "intializing loki reporter" :uri uri)
|
||||
(let [input (a/chan (a/sliding-buffer 1024))]
|
||||
(let [input (a/chan (a/dropping-buffer 512))]
|
||||
(receiver :sub input)
|
||||
(a/go-loop []
|
||||
(let [msg (a/<! input)]
|
||||
@@ -69,17 +69,23 @@
|
||||
:method :post
|
||||
:headers {"content-type" "application/json"}
|
||||
:body (json/encode payload)})]
|
||||
(if (= (:status response) 204)
|
||||
(cond
|
||||
(= (:status response) 204)
|
||||
true
|
||||
|
||||
(= (:status response) 400)
|
||||
(do
|
||||
(l/error :hint "error on sending log to loki"
|
||||
:try i
|
||||
(l/error :hint "error on sending log to loki (no retry)"
|
||||
:rsp (pr-str response))
|
||||
true)
|
||||
|
||||
:else
|
||||
(do
|
||||
(l/error :hint "error on sending log to loki" :try i
|
||||
:rsp (pr-str response))
|
||||
false)))
|
||||
(catch Exception e
|
||||
(l/error :hint "error on sending message to loki"
|
||||
:cause e
|
||||
:try i)
|
||||
(l/error :hint "error on sending message to loki" :cause e :try i)
|
||||
false)))
|
||||
|
||||
(defn- handle-event
|
||||
|
||||
@@ -166,25 +166,25 @@
|
||||
:tasks (ig/ref :app.worker/registry)
|
||||
:pool (ig/ref :app.db/pool)
|
||||
:schedule
|
||||
[{:cron #app/cron "0 0 0 */1 * ? *" ;; daily
|
||||
[{:cron #app/cron "0 0 0 * * ? *" ;; daily
|
||||
:task :file-media-gc}
|
||||
|
||||
{:cron #app/cron "0 0 */1 * * ?" ;; hourly
|
||||
{:cron #app/cron "0 0 * * * ?" ;; hourly
|
||||
:task :file-xlog-gc}
|
||||
|
||||
{:cron #app/cron "0 0 1 */1 * ?" ;; daily (1 hour shift)
|
||||
{:cron #app/cron "0 0 1 * * ?" ;; daily (1 hour shift)
|
||||
:task :storage-deleted-gc}
|
||||
|
||||
{:cron #app/cron "0 0 2 */1 * ?" ;; daily (2 hour shift)
|
||||
{:cron #app/cron "0 0 2 * * ?" ;; daily (2 hour shift)
|
||||
:task :storage-touched-gc}
|
||||
|
||||
{:cron #app/cron "0 0 3 */1 * ?" ;; daily (3 hour shift)
|
||||
{:cron #app/cron "0 0 3 * * ?" ;; daily (3 hour shift)
|
||||
:task :session-gc}
|
||||
|
||||
{:cron #app/cron "0 0 */1 * * ?" ;; hourly
|
||||
{:cron #app/cron "0 0 * * * ?" ;; hourly
|
||||
:task :storage-recheck}
|
||||
|
||||
{:cron #app/cron "0 0 0 */1 * ?" ;; daily
|
||||
{:cron #app/cron "0 0 0 * * ?" ;; daily
|
||||
:task :tasks-gc}
|
||||
|
||||
(when (cf/get :audit-archive-enabled)
|
||||
@@ -230,31 +230,22 @@
|
||||
|
||||
:app.tasks.tasks-gc/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:max-age cf/deletion-delay
|
||||
:metrics (ig/ref :app.metrics/metrics)}
|
||||
:max-age cf/deletion-delay}
|
||||
|
||||
:app.tasks.delete-object/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:metrics (ig/ref :app.metrics/metrics)}
|
||||
|
||||
:app.tasks.delete-storage-object/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:storage (ig/ref :app.storage/storage)
|
||||
:metrics (ig/ref :app.metrics/metrics)}
|
||||
:storage (ig/ref :app.storage/storage)}
|
||||
|
||||
:app.tasks.delete-profile/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:metrics (ig/ref :app.metrics/metrics)}
|
||||
{:pool (ig/ref :app.db/pool)}
|
||||
|
||||
:app.tasks.file-media-gc/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:metrics (ig/ref :app.metrics/metrics)
|
||||
:max-age cf/deletion-delay}
|
||||
|
||||
:app.tasks.file-xlog-gc/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
:metrics (ig/ref :app.metrics/metrics)
|
||||
:max-age cf/deletion-delay}
|
||||
:max-age (dt/duration {:hours 24})}
|
||||
|
||||
:app.tasks.telemetry/handler
|
||||
{:pool (ig/ref :app.db/pool)
|
||||
|
||||
@@ -112,14 +112,16 @@
|
||||
order by f.created_at asc")
|
||||
|
||||
(s/def ::search-files
|
||||
(s/keys :req-un [::profile-id ::team-id ::search-term]))
|
||||
(s/keys :req-un [::profile-id ::team-id]
|
||||
:opt-un [::search-term]))
|
||||
|
||||
(sv/defmethod ::search-files
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id search-term] :as params}]
|
||||
(db/exec! pool [sql:search-files
|
||||
profile-id team-id
|
||||
profile-id team-id
|
||||
search-term]))
|
||||
(when search-term
|
||||
(db/exec! pool [sql:search-files
|
||||
profile-id team-id
|
||||
profile-id team-id
|
||||
search-term])))
|
||||
|
||||
|
||||
;; --- Query: Files
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
(:require
|
||||
[app.common.spec :as us]
|
||||
[app.db :as db]
|
||||
[app.rpc.queries.files :as files]
|
||||
[app.rpc.queries.projects :as projects]
|
||||
[app.rpc.queries.teams :as teams]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
;; --- Query: Team Font Variants
|
||||
|
||||
;; TODO: deprecated, should be removed on 1.7.x
|
||||
|
||||
(s/def ::team-id ::us/uuid)
|
||||
(s/def ::profile-id ::us/uuid)
|
||||
(s/def ::team-font-variants
|
||||
@@ -27,3 +31,43 @@
|
||||
{:team-id team-id
|
||||
:deleted-at nil})))
|
||||
|
||||
;; --- Query: Font Variants
|
||||
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::project-id ::us/uuid)
|
||||
(s/def ::font-variants
|
||||
(s/and
|
||||
(s/keys :req-un [::profile-id]
|
||||
:opt-un [::team-id
|
||||
::file-id
|
||||
::project-id])
|
||||
(fn [o]
|
||||
(or (contains? o :team-id)
|
||||
(contains? o :file-id)
|
||||
(contains? o :project-id)))))
|
||||
|
||||
(sv/defmethod ::font-variants
|
||||
[{:keys [pool] :as cfg} {:keys [profile-id team-id file-id project-id] :as params}]
|
||||
(with-open [conn (db/open pool)]
|
||||
(cond
|
||||
(uuid? team-id)
|
||||
(do
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(db/query conn :team-font-variant
|
||||
{:team-id team-id
|
||||
:deleted-at nil}))
|
||||
|
||||
(uuid? project-id)
|
||||
(let [project (db/get-by-id conn :project project-id {:columns [:id :team-id]})]
|
||||
(projects/check-read-permissions! conn profile-id project-id)
|
||||
(db/query conn :team-font-variant
|
||||
{:team-id (:team-id project)
|
||||
:deleted-at nil}))
|
||||
|
||||
(uuid? file-id)
|
||||
(let [file (db/get-by-id conn :file file-id {:columns [:id :project-id]})
|
||||
project (db/get-by-id conn :project (:project-id file) {:columns [:id :team-id]})]
|
||||
(files/check-read-permissions! conn profile-id file-id)
|
||||
(db/query conn :team-font-variant
|
||||
{:team-id (:team-id project)
|
||||
:deleted-at nil})))))
|
||||
|
||||
@@ -50,9 +50,14 @@
|
||||
libs (files/retrieve-file-libraries conn false file-id)
|
||||
users (teams/retrieve-users conn (:team-id project))
|
||||
|
||||
fonts (db/query conn :team-font-variant
|
||||
{:team-id (:team-id project)
|
||||
:deleted-at nil})
|
||||
|
||||
bundle {:file file
|
||||
:page page
|
||||
:users users
|
||||
:fonts fonts
|
||||
:project project
|
||||
:libraries libs}]
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@
|
||||
where s.deleted_at is not null
|
||||
and s.deleted_at < (now() - ?::interval)
|
||||
order by s.deleted_at
|
||||
limit 500
|
||||
limit 100
|
||||
)
|
||||
delete from storage_object
|
||||
where id in (select id from items_part)
|
||||
@@ -396,7 +396,7 @@
|
||||
from storage_object as so
|
||||
where so.touched_at is not null
|
||||
order by so.touched_at
|
||||
limit 500;")
|
||||
limit 100;")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Recheck Stalled Task
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
(declare handle-deletion)
|
||||
|
||||
(defmethod ig/pre-init-spec ::handler [_]
|
||||
(s/keys :req-un [::db/pool]))
|
||||
(s/keys :req-un [::db/pool ::sto/storage]))
|
||||
|
||||
(defmethod ig/init-key ::handler
|
||||
[_ {:keys [pool] :as cfg}]
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
(let [interval (db/interval max-age)
|
||||
result (db/exec-one! conn [sql:delete-files-xlog interval])
|
||||
result (:next.jdbc/update-count result)]
|
||||
(l/debug :action "trim file-change table" :removed result)
|
||||
(l/debug :removed result :hint "remove old file changes")
|
||||
result))))
|
||||
|
||||
(def ^:private
|
||||
|
||||
@@ -64,12 +64,14 @@
|
||||
(defn batch
|
||||
[in {:keys [max-batch-size
|
||||
max-batch-age
|
||||
buffer-size
|
||||
init]
|
||||
:or {max-batch-size 200
|
||||
max-batch-age (* 30 1000)
|
||||
buffer-size 128
|
||||
init #{}}
|
||||
:as opts}]
|
||||
(let [out (a/chan)]
|
||||
(let [out (a/chan buffer-size)]
|
||||
(a/go-loop [tch (a/timeout max-batch-age) buf init]
|
||||
(let [[val port] (a/alts! [tch in])]
|
||||
(cond
|
||||
|
||||
@@ -98,11 +98,11 @@
|
||||
;; Terminate the loop if close channel is closed or
|
||||
;; event-loop-fn returns nil.
|
||||
(or (= port close-ch) (nil? val))
|
||||
(l/debug :msg "stop condition found")
|
||||
(l/debug :hint "stop condition found")
|
||||
|
||||
(db/pool-closed? pool)
|
||||
(do
|
||||
(l/debug :msg "eventloop aborted because pool is closed")
|
||||
(l/debug :hint "eventloop aborted because pool is closed")
|
||||
(a/close! close-ch))
|
||||
|
||||
(and (instance? java.sql.SQLException val)
|
||||
@@ -115,7 +115,7 @@
|
||||
(and (instance? java.sql.SQLException val)
|
||||
(= "40001" (.getSQLState ^java.sql.SQLException val)))
|
||||
(do
|
||||
(l/debug :msg "serialization failure (retrying in some instants)")
|
||||
(l/debug :hint "serialization failure (retrying in some instants)")
|
||||
(a/<! (a/timeout poll-ms))
|
||||
(recur))
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
(let [task-fn (get tasks name)]
|
||||
(if task-fn
|
||||
(task-fn item)
|
||||
(l/warn :msg "no task handler found"
|
||||
(l/warn :hint "no task handler found"
|
||||
:name (d/name name)))
|
||||
{:status :completed :task item}))
|
||||
|
||||
@@ -281,19 +281,13 @@
|
||||
[{:keys [tasks]} item]
|
||||
(let [name (d/name (:name item))]
|
||||
(try
|
||||
(l/debug :action "start task"
|
||||
:name name
|
||||
(l/debug :action "execute task"
|
||||
:id (:id item)
|
||||
:name name
|
||||
:retry (:retry-num item))
|
||||
|
||||
(handle-task tasks item)
|
||||
(catch Exception e
|
||||
(handle-exception e item))
|
||||
(finally
|
||||
(l/debug :action "end task"
|
||||
:name name
|
||||
:id (:id item)
|
||||
:retry (:retry-num item))))))
|
||||
(handle-exception e item)))))
|
||||
|
||||
(def sql:select-next-tasks
|
||||
"select * from task as t
|
||||
|
||||
@@ -356,8 +356,8 @@
|
||||
|
||||
([shape {:keys [round-coords?]
|
||||
:or {round-coords? true}}]
|
||||
(let [shape (apply-displacement shape)
|
||||
center (gco/center-shape shape)
|
||||
(let [shape (apply-displacement shape)
|
||||
center (gco/center-shape shape)
|
||||
modifiers (:modifiers shape)]
|
||||
(if (and modifiers center)
|
||||
(let [transform (modifiers->transform center modifiers)]
|
||||
@@ -376,7 +376,7 @@
|
||||
:y (- (:y new-selrect 0) (:y selrect 0))
|
||||
:width (- (:width new-selrect 1) (:width selrect 1))
|
||||
:height (- (:height new-selrect 1) (:height selrect 1))}]
|
||||
|
||||
|
||||
(cond-> group
|
||||
(and (some? svg-viewbox) (some? selrect) (some? new-selrect))
|
||||
(update :svg-viewbox
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
(if (and valid? (seq shapes))
|
||||
(as-> objects $
|
||||
;; Add the new shapes to the parent object.
|
||||
(update $ parent-id #(add-to-parent % index shapes))
|
||||
(d/update-when $ parent-id #(add-to-parent % index shapes))
|
||||
|
||||
;; Update each individual shape link to the new parent
|
||||
(reduce update-parent-id $ shapes)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
;; General purpose events & IMPL
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn team-fonts-loaded
|
||||
(defn fonts-fetched
|
||||
[fonts]
|
||||
(letfn [;; Prepare font to the internal font database format.
|
||||
(prepare-font [[id [item :as items]]]
|
||||
@@ -74,8 +74,8 @@
|
||||
(ptk/reify ::load-team-fonts
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :team-font-variants {:team-id team-id})
|
||||
(rx/map team-fonts-loaded)))))
|
||||
(->> (rp/query :font-variants {:team-id team-id})
|
||||
(rx/map fonts-fetched)))))
|
||||
|
||||
(defn process-upload
|
||||
"Given a seq of blobs and the team id, creates a ready-to-use fonts
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.constants :as c]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.util.avatars :as avatars]
|
||||
[app.util.router :as rt]
|
||||
[beicon.core :as rx]
|
||||
@@ -97,7 +98,10 @@
|
||||
:file-id file-id}
|
||||
(string? token) (assoc :token token))]
|
||||
(->> (rp/query :viewer-bundle params)
|
||||
(rx/map bundle-fetched))))))
|
||||
(rx/mapcat
|
||||
(fn [{:keys [fonts] :as bundle}]
|
||||
(rx/of (df/fonts-fetched fonts)
|
||||
(bundle-fetched bundle)))))))))
|
||||
|
||||
(defn- extract-frames
|
||||
[objects]
|
||||
|
||||
@@ -66,10 +66,10 @@
|
||||
|
||||
(cond-> changes
|
||||
(not (empty? rops))
|
||||
(update :rch conj (assoc change :operations rops))
|
||||
(update :redo-changes conj (assoc change :operations rops))
|
||||
|
||||
(not (empty? uops))
|
||||
(update :uch conj (assoc change :operations uops)))))
|
||||
(update :undo-changes conj (assoc change :operations uops)))))
|
||||
|
||||
(defn update-shapes
|
||||
([ids f] (update-shapes ids f nil))
|
||||
@@ -82,25 +82,26 @@
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
reg-objects {:type :reg-objects :page-id page-id :shapes (vec ids)}
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
changes {:redo-changes []
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? save-undo?}
|
||||
|
||||
{redo-changes :rch undo-changes :uch}
|
||||
(reduce #(update-shape-changes %1 page-id objects f keys %2)
|
||||
{:rch [] :uch []} ids)]
|
||||
ids (into [] (filter some?) ids)
|
||||
|
||||
(when-not (empty? redo-changes)
|
||||
(let [redo-changes (cond-> redo-changes
|
||||
reg-objects? (conj reg-objects))
|
||||
changes (reduce #(update-shape-changes %1 page-id objects f keys %2) changes ids)]
|
||||
|
||||
undo-changes (cond-> undo-changes
|
||||
reg-objects? (conj reg-objects))]
|
||||
|
||||
(rx/of (commit-changes {:redo-changes redo-changes
|
||||
:undo-changes undo-changes
|
||||
:origin it
|
||||
:save-undo? save-undo?})))))))))
|
||||
(when-not (empty? (:redo-changes changes))
|
||||
(let [reg-objs {:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes ids}
|
||||
changes (cond-> changes
|
||||
reg-objects?
|
||||
(-> (update :redo-changes conj reg-objs)
|
||||
(update :undo-changes conj reg-objs)))]
|
||||
(rx/of (commit-changes changes)))))))))
|
||||
|
||||
(defn update-indices
|
||||
[page-id changes]
|
||||
|
||||
@@ -10,24 +10,21 @@
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.messages :as dm]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.libraries-helpers :as dwlh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.color :as color]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.logging :as log]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
@@ -263,10 +260,11 @@
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
selected (cp/clean-loops objects selected)]
|
||||
(when-not (empty? selected)
|
||||
selected (cp/clean-loops objects selected)
|
||||
shapes (dwg/shapes-for-grouping objects selected)]
|
||||
(when-not (empty? shapes)
|
||||
(let [[group rchanges uchanges]
|
||||
(dwlh/generate-add-component selected objects page-id file-id)]
|
||||
(dwlh/generate-add-component shapes objects page-id file-id)]
|
||||
(when-not (empty? rchanges)
|
||||
(rx/of (dch/commit-changes {:redo-changes rchanges
|
||||
:undo-changes uchanges
|
||||
|
||||
@@ -124,10 +124,8 @@
|
||||
"If there is exactly one id, and it's a group, use it as root. Otherwise,
|
||||
create a group that contains all ids. Then, make a component with it,
|
||||
and link all shapes to their corresponding one in the component."
|
||||
[ids objects page-id file-id]
|
||||
(let [shapes (dwg/shapes-for-grouping objects ids)
|
||||
|
||||
[group rchanges uchanges]
|
||||
[shapes objects page-id file-id]
|
||||
(let [[group rchanges uchanges]
|
||||
(if (and (= (count shapes) 1)
|
||||
(= (:type (first shapes)) :group))
|
||||
[(first shapes) [] []]
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
id (get-in state [:workspace-local :edition])
|
||||
old-content (get-in state [:workspace-local :edit-path id :old-content])
|
||||
shape (get-in state (st/get-path state))]
|
||||
(if (and (some? old-content) (some? shape))
|
||||
(if (and (some? old-content) (some? (:id shape)))
|
||||
(let [[rch uch] (generate-path-changes objects page-id shape old-content (:content shape))]
|
||||
(rx/of (dch/commit-changes {:redo-changes rch
|
||||
:undo-changes uch
|
||||
|
||||
@@ -7,26 +7,26 @@
|
||||
(ns app.main.exports
|
||||
"The main logic for SVG export functionality."
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.math :as mth]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.align :as gal]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.util.timers :as ts]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.shapes.circle :as circle]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.frame :as frame]
|
||||
[app.main.ui.shapes.circle :as circle]
|
||||
[app.main.ui.shapes.group :as group]
|
||||
[app.main.ui.shapes.image :as image]
|
||||
[app.main.ui.shapes.path :as path]
|
||||
[app.main.ui.shapes.rect :as rect]
|
||||
[app.main.ui.shapes.text :as text]
|
||||
[app.main.ui.shapes.group :as group]
|
||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||
[app.main.ui.shapes.svg-raw :as svg-raw]
|
||||
[app.main.ui.shapes.shape :refer [shape-container]]))
|
||||
[app.main.ui.shapes.text :as text]
|
||||
[app.util.timers :as ts]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(def ^:private default-color "#E8E9EA") ;; $color-canvas
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
(cond
|
||||
(= 204 status)
|
||||
;; We need to send "something" so the streams listening downstream can act
|
||||
(rx/of :empty)
|
||||
(rx/of nil)
|
||||
|
||||
(= 502 status)
|
||||
(rx/throw {:type :bad-gateway})
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
(fn [e]
|
||||
(reset! dragging? false)
|
||||
(when (not= selected-project project-id)
|
||||
(let [data {:ids selected-files
|
||||
(let [data {:ids (into #{} (keys selected-files))
|
||||
:project-id project-id}
|
||||
mdata {:on-success on-drop-success}]
|
||||
(st/emit! (dd/move-files (with-meta data mdata)))))))]
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
:min-width? true
|
||||
:top top
|
||||
:left left
|
||||
:options [[(tr "labels.rename") on-edit]
|
||||
:options [(when-not (:is-default project)
|
||||
[(tr "labels.rename") on-edit])
|
||||
[(tr "dashboard.duplicate") on-duplicate]
|
||||
[(tr "dashboard.pin-unpin") toggle-pin]
|
||||
(when (seq teams)
|
||||
|
||||
@@ -12,11 +12,14 @@
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.store :as st]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.exports :as exports]
|
||||
[app.main.repo :as repo]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||
[app.main.ui.shapes.text.embed :refer [embed-fontfaces-style]]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
@@ -42,6 +45,9 @@
|
||||
objects (reduce updt-fn objects mod-ids)
|
||||
object (get objects object-id)
|
||||
|
||||
txt-xfm (comp (map #(get objects %))
|
||||
(filter #(= :text (:type %))))
|
||||
txt-objs (into [] txt-xfm mod-ids)
|
||||
|
||||
{:keys [width height]} (gsh/points->selrect (:points object))
|
||||
|
||||
@@ -72,6 +78,9 @@
|
||||
]
|
||||
|
||||
[:& (mf/provider muc/embed-ctx) {:value true}
|
||||
(when (seq txt-objs)
|
||||
[:& embed-fontfaces-style {:shapes txt-objs}])
|
||||
|
||||
[:svg {:id "screenshot"
|
||||
:view-box vbox
|
||||
:width width
|
||||
@@ -79,6 +88,7 @@
|
||||
:version "1.1"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns "http://www.w3.org/2000/svg"}
|
||||
|
||||
(case (:type object)
|
||||
:frame [:& frame-wrapper {:shape object :view-box vbox}]
|
||||
:group [:> shape-container {:shape object}
|
||||
@@ -97,7 +107,6 @@
|
||||
(assoc objects (:id object) object))
|
||||
objects))
|
||||
|
||||
|
||||
;; NOTE: for now, it is ok download the entire file for render only
|
||||
;; single page but in a future we need consider to add a specific
|
||||
;; backend entry point for download only the data of single page.
|
||||
@@ -106,12 +115,19 @@
|
||||
[{:keys [file-id page-id object-id] :as props}]
|
||||
(let [objects (mf/use-state nil)]
|
||||
(mf/use-effect
|
||||
#(let [subs (->> (repo/query! :file {:id file-id})
|
||||
(rx/subs (fn [{:keys [data]}]
|
||||
(let [objs (get-in data [:pages-index page-id :objects])
|
||||
objs (adapt-root-frame objs object-id)]
|
||||
(reset! objects objs)))))]
|
||||
(fn [] (rx/dispose! subs))))
|
||||
(mf/deps file-id page-id object-id)
|
||||
(fn []
|
||||
(->> (rx/zip
|
||||
(repo/query! :font-variants {:file-id file-id})
|
||||
(repo/query! :file {:id file-id}))
|
||||
(rx/subs
|
||||
(fn [[fonts {:keys [data]} :as kaka]]
|
||||
(when (seq fonts)
|
||||
(st/emit! (df/fonts-fetched fonts)))
|
||||
(let [objs (get-in data [:pages-index page-id :objects])
|
||||
objs (adapt-root-frame objs object-id)]
|
||||
(reset! objects objs)))))
|
||||
(constantly nil)))
|
||||
|
||||
(when @objects
|
||||
[:& object-svg {:objects @objects
|
||||
|
||||
@@ -86,12 +86,6 @@
|
||||
:weight weight})]
|
||||
(p/resolved result))))
|
||||
|
||||
(defn- to-promise
|
||||
[observable]
|
||||
(p/create (fn [resolve reject]
|
||||
(->> (rx/take 1 observable)
|
||||
(rx/subs resolve reject)))))
|
||||
|
||||
(defn fetch-font-data
|
||||
"Parses the CSS and retrieves the font data as DataURI."
|
||||
[^string css]
|
||||
@@ -137,10 +131,10 @@
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]}
|
||||
[props]
|
||||
(let [shapes (obj/get props "shapes")
|
||||
node {:children (->> shapes (map :content))}
|
||||
fonts (-> node get-node-fonts memoize)
|
||||
style (mf/use-state nil)]
|
||||
(let [shapes (obj/get props "shapes")
|
||||
node {:children (->> shapes (map :content))}
|
||||
fonts (-> node get-node-fonts memoize)
|
||||
style (mf/use-state nil)]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps fonts)
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
(mf/defc thumbnails-summary
|
||||
[{:keys [on-toggle-expand on-close total] :as props}]
|
||||
[:div.thumbnails-summary
|
||||
[:span.counter (str total " frames")]
|
||||
[:span.counter (tr "labels.num-of-frames" (i18n/c total))]
|
||||
[:span.buttons
|
||||
[:span.btn-expand {:on-click on-toggle-expand} i/arrow-down]
|
||||
[:span.btn-close {:on-click on-close} i/close]]])
|
||||
|
||||
@@ -102,23 +102,20 @@
|
||||
{::mf/wrap [#(mf/memo' % check-frame-props) custom-deferred]
|
||||
::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
objects (unchecked-get props "objects")
|
||||
thumbnail? (unchecked-get props "thumbnail?")
|
||||
(let [shape (unchecked-get props "shape")
|
||||
objects (unchecked-get props "objects")
|
||||
thumbnail? (unchecked-get props "thumbnail?")
|
||||
|
||||
edition (mf/deref refs/selected-edition)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
||||
|
||||
shape (gsh/transform-shape shape)
|
||||
children (mapv #(get objects %) (:shapes shape))
|
||||
text-childs (->> objects
|
||||
vals
|
||||
(filterv #(and (= :text (:type %))
|
||||
(= (:id shape) (:frame-id %)))))
|
||||
shape (gsh/transform-shape shape)
|
||||
children (mapv #(get objects %) (:shapes shape))
|
||||
text-childs (->> (vals objects)
|
||||
(filterv #(and (= :text (:type %))
|
||||
(= (:id shape) (:frame-id %)))))
|
||||
|
||||
ds-modifier (get-in shape [:modifiers :displacement])
|
||||
|
||||
rendered? (mf/use-state false)
|
||||
rendered? (mf/use-state false)
|
||||
|
||||
show-thumbnail? (and thumbnail? (some? (:thumbnail shape)))
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@
|
||||
[content points]
|
||||
|
||||
(let [segments-set (into #{}
|
||||
(map (fn [[p1 p2 _]] [p1 p2]))
|
||||
(map (fn [{:keys [start end]}] [start end]))
|
||||
(get-segments content points))
|
||||
|
||||
create-line-command (fn [point other]
|
||||
@@ -382,7 +382,7 @@
|
||||
|
||||
(defn group-segments [segments]
|
||||
(loop [result []
|
||||
[point-a point-b :as segment] (first segments)
|
||||
{point-a :start point-b :end :as segment} (first segments)
|
||||
segments (rest segments)]
|
||||
|
||||
(if (nil? segment)
|
||||
|
||||
@@ -82,21 +82,24 @@
|
||||
([state label ids] (group-shapes state label ids "Group-"))
|
||||
([state label ids prefix]
|
||||
(let [page (current-page state)
|
||||
shapes (dwg/shapes-for-grouping (:objects page) ids)
|
||||
shapes (dwg/shapes-for-grouping (:objects page) ids)]
|
||||
(if (empty? shapes)
|
||||
state
|
||||
(let [[group rchanges uchanges]
|
||||
(dwg/prepare-create-group (:objects page) (:id page) shapes prefix true)]
|
||||
|
||||
[group rchanges uchanges]
|
||||
(dwg/prepare-create-group (:objects page) (:id page) shapes prefix true)]
|
||||
|
||||
(swap! idmap assoc label (:id group))
|
||||
(update state :workspace-data
|
||||
cp/process-changes rchanges))))
|
||||
(swap! idmap assoc label (:id group))
|
||||
(update state :workspace-data
|
||||
cp/process-changes rchanges))))))
|
||||
|
||||
(defn make-component
|
||||
[state label ids]
|
||||
(let [page (current-page state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
shapes (dwg/shapes-for-grouping objects ids)
|
||||
|
||||
[group rchanges uchanges]
|
||||
(dwlh/generate-add-component ids
|
||||
(dwlh/generate-add-component shapes
|
||||
(:objects page)
|
||||
(:id page)
|
||||
current-file-id)]
|
||||
|
||||
@@ -981,6 +981,11 @@ msgid_plural "labels.num-of-files"
|
||||
msgstr[0] "1 file"
|
||||
msgstr[1] "%s files"
|
||||
|
||||
msgid "labels.num-of-frames"
|
||||
msgid_plural "labels.num-of-frames"
|
||||
msgstr[0] "1 artboard"
|
||||
msgstr[1] "%s artboards"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "labels.num-of-projects"
|
||||
msgid_plural "labels.num-of-projects"
|
||||
@@ -1438,11 +1443,11 @@ msgstr "%s - Penpot"
|
||||
|
||||
#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs
|
||||
msgid "viewer.empty-state"
|
||||
msgstr "No frames found on the page."
|
||||
msgstr "No artboards found on the page."
|
||||
|
||||
#: src/app/main/ui/handoff.cljs, src/app/main/ui/viewer.cljs
|
||||
msgid "viewer.frame-not-found"
|
||||
msgstr "Frame not found."
|
||||
msgstr "Artboard not found."
|
||||
|
||||
#: src/app/main/ui/viewer/header.cljs
|
||||
msgid "viewer.header.dont-show-interactions"
|
||||
|
||||
@@ -983,6 +983,11 @@ msgid_plural "labels.num-of-files"
|
||||
msgstr[0] "1 archivo"
|
||||
msgstr[1] "%s archivos"
|
||||
|
||||
msgid "labels.num-of-frames"
|
||||
msgid_plural "labels.num-of-frames"
|
||||
msgstr[0] "1 tablero"
|
||||
msgstr[1] "%s tableros"
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs
|
||||
msgid "labels.num-of-projects"
|
||||
msgid_plural "labels.num-of-projects"
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.6.2-alpha
|
||||
1.6.4-alpha
|
||||
|
||||
Reference in New Issue
Block a user