Merge remote-tracking branch 'origin/staging' into develop
|
@ -91,8 +91,8 @@
|
|||
|
||||
:jmx-remote
|
||||
{:jvm-opts ["-Dcom.sun.management.jmxremote"
|
||||
"-Dcom.sun.management.jmxremote.port=9090"
|
||||
"-Dcom.sun.management.jmxremote.rmi.port=9090"
|
||||
"-Dcom.sun.management.jmxremote.port=9091"
|
||||
"-Dcom.sun.management.jmxremote.rmi.port=9091"
|
||||
"-Dcom.sun.management.jmxremote.local.only=false"
|
||||
"-Dcom.sun.management.jmxremote.authenticate=false"
|
||||
"-Dcom.sun.management.jmxremote.ssl=false"
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
[{:id "material-design-3"
|
||||
:name "Material Design 3"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/main/Material%20Design%203.penpot"}
|
||||
{:id "tutorial-for-beginners"
|
||||
[{:id "tutorial-for-beginners"
|
||||
:name "Tutorial for beginners"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/tutorial-for-beginners.penpot"}
|
||||
{:id "penpot-design-system"
|
||||
:name "Penpot Design System"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Penpot-Design-system.penpot"}
|
||||
{:id "flex-layout-playground"
|
||||
:name "Flex Layout Playground"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/main/Flex%20Layout%20Playground.penpot"}
|
||||
{:id "lucide-icons"
|
||||
:name "Lucide Icons"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Lucide-icons.penpot"}
|
||||
{:id "font-awesome"
|
||||
:name "Font Awesome"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Font-Awesome.penpot"}
|
||||
{:id "plants-app"
|
||||
:name "Plants app"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Plants-app.penpot"}
|
||||
{:id "wireframing-kit"
|
||||
:name "Wireframing Kit"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/wireframing-kit.penpot"}
|
||||
{:id "ant-design"
|
||||
:name "Ant Design UI Kit (lite)"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Ant-Design-UI-Kit-Lite.penpot"}
|
||||
{:id "cocomaterial"
|
||||
:name "Cocomaterial"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Cocomaterial.penpot"}
|
||||
{:id "circum-icons"
|
||||
:name "Circum Icons pack"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/CircumIcons.penpot"}
|
||||
{:id "coreui"
|
||||
:name "CoreUI"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/main/CoreUI%20DesignSystem%20(DEMO).penpot"}
|
||||
{:id "black-white-mobile-templates"
|
||||
:name "Black & White Mobile Templates"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Black-White-Mobile-Templates.penpot"}
|
||||
{:id "avataaars"
|
||||
:name "Avataaars"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Avataaars-by-Pablo-Stanley.penpot"}
|
||||
{:id "ux-notes"
|
||||
:name "UX Notes"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/UX-Notes.penpot"}
|
||||
{:id "whiteboarding-kit"
|
||||
:name "Whiteboarding Kit"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Whiteboarding-mapping-kit.penpot"}]
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Whiteboarding-mapping-kit.penpot"}
|
||||
{:id "open-color-scheme"
|
||||
:name "Open Color Scheme"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Open-Color-Scheme.penpot"}
|
||||
{:id "flex-layout-playground"
|
||||
:name "Flex Layout Playground"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/binary-files/Flex-Layout-Playground.penpot"}]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
alwaysWriteExceptions="true" />
|
||||
</Console>
|
||||
|
||||
<RollingFile name="main" fileName="logs/main.log" filePattern="logs/main-%i.log">
|
||||
<RollingFile name="main" fileName="logs/main-latest.log" filePattern="logs/main-%i.log">
|
||||
<PatternLayout pattern="[%d{YYYY-MM-dd HH:mm:ss.SSS}] %level{length=1} %logger{36} - %msg%n"
|
||||
alwaysWriteExceptions="true" />
|
||||
<Policies>
|
||||
|
@ -15,7 +15,7 @@
|
|||
<DefaultRolloverStrategy max="9"/>
|
||||
</RollingFile>
|
||||
|
||||
<RollingFile name="reports" fileName="logs/reports.log" filePattern="logs/reports-%i.log">
|
||||
<RollingFile name="reports" fileName="logs/reports-latest.log" filePattern="logs/reports-%i.log">
|
||||
<PatternLayout pattern="[%d{YYYY-MM-dd HH:mm:ss.SSS}] %level{length=1} %logger{36} - %msg%n"
|
||||
alwaysWriteExceptions="true" />
|
||||
<Policies>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /home/penpot/backend/environ
|
||||
source /home/penpot/environ
|
||||
export PENPOT_FLAGS="$PENPOT_FLAGS disable-backend-worker"
|
||||
|
||||
export OPTIONS="
|
||||
|
@ -12,13 +12,13 @@ export OPTIONS="
|
|||
-J-XX:+UnlockDiagnosticVMOptions \
|
||||
-J-XX:+DebugNonSafepoints \
|
||||
-J-Djdk.tracePinnedThreads=full \
|
||||
-J-XX:+UseTransparentHugePages \
|
||||
-J-XX:ReservedCodeCacheSize=1g \
|
||||
-J-Dpolyglot.engine.WarnInterpreterOnly=false \
|
||||
-J--enable-preview";
|
||||
|
||||
# Setup HEAP
|
||||
#export OPTIONS="$OPTIONS -J-Xms900m -J-Xmx900m -J-XX:+AlwaysPreTouch"
|
||||
export OPTIONS="$OPTIONS -J-Xms1g -J-Xmx25g"
|
||||
#export OPTIONS="$OPTIONS -J-Xms900m -J-Xmx900m -J-XX:+AlwaysPreTouch"
|
||||
export OPTIONS="$OPTIONS -J-Xms320g -J-Xmx320g -J-XX:+AlwaysPreTouch"
|
||||
|
||||
export PENPOT_HTTP_SERVER_IO_THREADS=2
|
||||
export PENPOT_HTTP_SERVER_WORKER_THREADS=2
|
||||
|
@ -33,11 +33,10 @@ export PENPOT_HTTP_SERVER_WORKER_THREADS=2
|
|||
# export OPTIONS="$OPTIONS -J-Xint"
|
||||
|
||||
# Setup GC
|
||||
export OPTIONS="$OPTIONS -J-XX:+UseG1GC -J-Xlog:gc:logs/gc.log"
|
||||
|
||||
export OPTIONS="$OPTIONS -J-XX:+UseG1GC -J-Xlog:gc:logs/gc.log"
|
||||
|
||||
# Setup GC
|
||||
#export OPTIONS="$OPTIONS -J-XX:+UseZGC -J-XX:+ZGenerational -J-Xlog:gc:gc.log"
|
||||
#export OPTIONS="$OPTIONS -J-XX:+UseZGC -J-XX:+ZGenerational -J-Xlog:gc:logs/gc.log"
|
||||
|
||||
# Enable ImageMagick v7.x support
|
||||
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
|
||||
|
@ -46,4 +45,4 @@ export OPTIONS_EVAL="nil"
|
|||
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"
|
||||
|
||||
set -ex
|
||||
exec clojure $OPTIONS -M -e "$OPTIONS_EVAL" -m rebel-readline.main
|
||||
exec clojure $OPTIONS -M -e "$OPTIONS_EVAL" -m rebel-readline.main
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.common.files.migrations :as fmg]
|
||||
[app.common.files.shapes-helpers :as cfsh]
|
||||
[app.common.files.validate :as cfv]
|
||||
[app.common.fressian :as fres]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.rect :as grc]
|
||||
|
@ -48,18 +49,18 @@
|
|||
[app.rpc.commands.files-snapshot :as fsnap]
|
||||
[app.rpc.commands.media :as cmd.media]
|
||||
[app.storage :as sto]
|
||||
[app.storage.impl :as impl]
|
||||
[app.storage.tmp :as tmp]
|
||||
[app.svgo :as svgo]
|
||||
[app.util.blob :as blob]
|
||||
[app.util.cache :as cache]
|
||||
[app.util.events :as events]
|
||||
[app.util.pointer-map :as pmap]
|
||||
[app.util.time :as dt]
|
||||
[buddy.core.codecs :as bc]
|
||||
[clojure.set :refer [rename-keys]]
|
||||
[cuerdas.core :as str]
|
||||
[datoteka.fs :as fs]
|
||||
[datoteka.io :as io]
|
||||
[promesa.exec :as px]
|
||||
[promesa.util :as pu]))
|
||||
|
||||
(def ^:dynamic *stats*
|
||||
|
@ -68,7 +69,7 @@
|
|||
|
||||
(def ^:dynamic *cache*
|
||||
"A dynamic var for setting up a cache instance."
|
||||
nil)
|
||||
false)
|
||||
|
||||
(def ^:dynamic *skip-on-graphic-error*
|
||||
"A dynamic var for setting up the default error behavior for graphics processing."
|
||||
|
@ -100,6 +101,8 @@
|
|||
(some? data)
|
||||
(assoc :data (blob/decode data))))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; FILE PREPARATION BEFORE MIGRATION
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -872,10 +875,11 @@
|
|||
|
||||
(fix-shape [shape]
|
||||
(if (or (nil? (:parent-id shape)) (ctk/instance-head? shape))
|
||||
(let [frame? (= :frame (:type shape))]
|
||||
(assoc shape
|
||||
:type :frame ; Old groups must be converted
|
||||
:fills (or (:fills shape) []) ; to frames and conform to spec
|
||||
(let [frame? (= :frame (:type shape))
|
||||
not-group? (not= :group (:type shape))]
|
||||
(assoc shape ; Old groups must be converted
|
||||
:type :frame ; to frames and conform to spec
|
||||
:fills (if not-group? (d/nilv (:fills shape) []) []) ; Groups never should have fill
|
||||
:shapes (or (:shapes shape) [])
|
||||
:hide-in-viewer (if frame? (boolean (:hide-in-viewer shape)) true)
|
||||
:show-content (if frame? (boolean (:show-content shape)) true)
|
||||
|
@ -1254,7 +1258,7 @@
|
|||
:frame-id frame-id
|
||||
:parent-id frame-id})
|
||||
(assoc
|
||||
:proportion (/ width height)
|
||||
:proportion (float (/ width height))
|
||||
:proportion-lock true))
|
||||
|
||||
img-shape (cts/setup-shape
|
||||
|
@ -1295,7 +1299,7 @@
|
|||
(try
|
||||
(let [item (if (str/starts-with? href "data:")
|
||||
(let [[mtype data] (parse-datauri href)
|
||||
size (alength data)
|
||||
size (alength ^bytes data)
|
||||
path (tmp/tempfile :prefix "penpot.media.download.")
|
||||
written (io/write-to-file! data path :size size)]
|
||||
|
||||
|
@ -1364,27 +1368,49 @@
|
|||
{::sql/columns [:media-id]})]
|
||||
(:media-id fmobject)))
|
||||
|
||||
(defn- get-sobject-content
|
||||
(defn get-sobject-content
|
||||
[id]
|
||||
(let [storage (::sto/storage *system*)
|
||||
sobject (sto/get-object storage id)]
|
||||
|
||||
(when-not sobject
|
||||
(throw (RuntimeException. "sobject is nil")))
|
||||
(when (> (:size sobject) 1135899)
|
||||
(throw (RuntimeException. "svg too big")))
|
||||
|
||||
(with-open [stream (sto/get-object-data storage sobject)]
|
||||
(slurp stream))))
|
||||
|
||||
(defn get-optimized-svg
|
||||
[sid]
|
||||
(let [svg-text (get-sobject-content sid)
|
||||
svg-text (svgo/optimize *system* svg-text)]
|
||||
(csvg/parse svg-text)))
|
||||
|
||||
(def base-path "/data/cache")
|
||||
|
||||
(defn get-sobject-cache-path
|
||||
[sid]
|
||||
(let [path (impl/id->path sid)]
|
||||
(fs/join base-path path)))
|
||||
|
||||
(defn get-cached-svg
|
||||
[sid]
|
||||
(let [path (get-sobject-cache-path sid)]
|
||||
(if (fs/exists? path)
|
||||
(with-open [^java.lang.AutoCloseable stream (io/input-stream path)]
|
||||
(let [reader (fres/reader stream)]
|
||||
(fres/read! reader)))
|
||||
(get-optimized-svg sid))))
|
||||
|
||||
(defn- create-shapes-for-svg
|
||||
[{:keys [id] :as mobj} file-id objects frame-id position]
|
||||
(let [get-svg (fn [sid]
|
||||
(let [svg-text (get-sobject-content sid)
|
||||
svg-text (svgo/optimize *system* svg-text)]
|
||||
(-> (csvg/parse svg-text)
|
||||
(assoc :name (:name mobj)))))
|
||||
|
||||
sid (resolve-sobject-id id)
|
||||
svg-data (if (cache/cache? *cache*)
|
||||
(cache/get *cache* sid (px/wrap-bindings get-svg))
|
||||
(get-svg sid))
|
||||
|
||||
svg-data (collect-and-persist-images svg-data file-id id)]
|
||||
(let [sid (resolve-sobject-id id)
|
||||
svg-data (if *cache*
|
||||
(get-cached-svg sid)
|
||||
(get-optimized-svg sid))
|
||||
svg-data (collect-and-persist-images svg-data file-id id)
|
||||
svg-data (assoc svg-data :name (:name mobj))]
|
||||
|
||||
(sbuilder/create-svg-shapes svg-data position objects frame-id frame-id #{} false)))
|
||||
|
||||
|
@ -1663,6 +1689,7 @@
|
|||
(db/update! conn :file
|
||||
{:data (blob/encode (:data file))
|
||||
:features (db/create-array conn "text" (:features file))
|
||||
:version (:version file)
|
||||
:revn (:revn file)}
|
||||
{:id (:id file)})))
|
||||
|
||||
|
@ -1712,7 +1739,7 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn migrate-file!
|
||||
[system file-id & {:keys [validate? skip-on-graphic-error? label]}]
|
||||
[system file-id & {:keys [validate? skip-on-graphic-error? label rown]}]
|
||||
(let [tpoint (dt/tpoint)
|
||||
err (volatile! false)]
|
||||
|
||||
|
@ -1752,24 +1779,14 @@
|
|||
components (get @*file-stats* :processed-components 0)
|
||||
graphics (get @*file-stats* :processed-graphics 0)]
|
||||
|
||||
(if (cache/cache? *cache*)
|
||||
(let [cache-stats (cache/stats *cache*)]
|
||||
(l/dbg :hint "migrate:file:end"
|
||||
:file-id (str file-id)
|
||||
:graphics graphics
|
||||
:components components
|
||||
:validate validate?
|
||||
:crt (mth/to-fixed (:hit-rate cache-stats) 2)
|
||||
:crq (str (:req-count cache-stats))
|
||||
:error @err
|
||||
:elapsed (dt/format-duration elapsed)))
|
||||
(l/dbg :hint "migrate:file:end"
|
||||
:file-id (str file-id)
|
||||
:graphics graphics
|
||||
:components components
|
||||
:validate validate?
|
||||
:error @err
|
||||
:elapsed (dt/format-duration elapsed)))
|
||||
(l/dbg :hint "migrate:file:end"
|
||||
:file-id (str file-id)
|
||||
:graphics graphics
|
||||
:components components
|
||||
:validate validate?
|
||||
:rown rown
|
||||
:error @err
|
||||
:elapsed (dt/format-duration elapsed))
|
||||
|
||||
(some-> *stats* (swap! update :processed-files (fnil inc 0)))
|
||||
(some-> *team-stats* (swap! update :processed-files (fnil inc 0)))))))))
|
||||
|
@ -1831,21 +1848,9 @@
|
|||
(when-not @err
|
||||
(some-> *stats* (swap! update :processed-teams (fnil inc 0))))
|
||||
|
||||
(if (cache/cache? *cache*)
|
||||
(let [cache-stats (cache/stats *cache*)]
|
||||
(l/dbg :hint "migrate:team:end"
|
||||
:team-id (dm/str team-id)
|
||||
:files files
|
||||
:components components
|
||||
:graphics graphics
|
||||
:crt (mth/to-fixed (:hit-rate cache-stats) 2)
|
||||
:crq (str (:req-count cache-stats))
|
||||
:error @err
|
||||
:elapsed (dt/format-duration elapsed)))
|
||||
|
||||
(l/dbg :hint "migrate:team:end"
|
||||
:team-id (dm/str team-id)
|
||||
:files files
|
||||
:components components
|
||||
:graphics graphics
|
||||
:elapsed (dt/format-duration elapsed)))))))))
|
||||
(l/dbg :hint "migrate:team:end"
|
||||
:team-id (dm/str team-id)
|
||||
:files files
|
||||
:components components
|
||||
:graphics graphics
|
||||
:elapsed (dt/format-duration elapsed))))))))
|
||||
|
|
|
@ -72,11 +72,11 @@
|
|||
;; are not very friendly with virtual threads, and for avoid
|
||||
;; unexpected blocking of other concurrent operations we
|
||||
;; dispatch that operation to a dedicated executor.
|
||||
(let [result (px/submit! executor (partial bf.v1/import-files! cfg input))]
|
||||
(let [result (px/invoke! executor (partial bf.v1/import-files! cfg input))]
|
||||
(db/update! conn :project
|
||||
{:modified-at (dt/now)}
|
||||
{:id project-id})
|
||||
(deref result)))))
|
||||
result))))
|
||||
|
||||
(def ^:private
|
||||
schema:import-binfile
|
||||
|
|
|
@ -134,6 +134,9 @@
|
|||
file))
|
||||
file)]
|
||||
|
||||
;; Delete changes from the changes history
|
||||
(db/delete! conn :file-change {:file-id id})
|
||||
|
||||
(db/update! conn :file
|
||||
{:deleted-at nil
|
||||
:revn 1
|
||||
|
|
|
@ -7,18 +7,19 @@
|
|||
(ns app.srepl.components-v2
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.fressian :as fres]
|
||||
[app.common.logging :as l]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.features.components-v2 :as feat]
|
||||
[app.main :as main]
|
||||
[app.srepl.helpers :as h]
|
||||
[app.svgo :as svgo]
|
||||
[app.util.cache :as cache]
|
||||
[app.util.events :as events]
|
||||
[app.util.time :as dt]
|
||||
[app.worker :as-alias wrk]
|
||||
[cuerdas.core :as str]
|
||||
[datoteka.fs :as fs]
|
||||
[datoteka.io :as io]
|
||||
[promesa.exec :as px]
|
||||
[promesa.exec.semaphore :as ps]
|
||||
[promesa.util :as pu]))
|
||||
|
@ -68,7 +69,8 @@
|
|||
|
||||
(def ^:private sql:get-teams-by-created-at
|
||||
"WITH teams AS (
|
||||
SELECT id, features
|
||||
SELECT id, features,
|
||||
row_number() OVER (ORDER BY created_at) AS rown
|
||||
FROM team
|
||||
WHERE deleted_at IS NULL
|
||||
ORDER BY created_at DESC
|
||||
|
@ -77,6 +79,7 @@
|
|||
(def ^:private sql:get-teams-by-graphics
|
||||
"WITH teams AS (
|
||||
SELECT t.id, t.features,
|
||||
row_number() OVER (ORDER BY t.created_at) AS rown,
|
||||
(SELECT count(*)
|
||||
FROM file_media_object AS fmo
|
||||
JOIN file AS f ON (f.id = fmo.file_id)
|
||||
|
@ -93,6 +96,7 @@
|
|||
(def ^:private sql:get-teams-by-activity
|
||||
"WITH teams AS (
|
||||
SELECT t.id, t.features,
|
||||
row_number() OVER (ORDER BY t.created_at) AS rown,
|
||||
(SELECT coalesce(max(date_trunc('month', f.modified_at)), date_trunc('month', t.modified_at))
|
||||
FROM file AS f
|
||||
JOIN project AS p ON (f.project_id = p.id)
|
||||
|
@ -107,24 +111,16 @@
|
|||
)
|
||||
SELECT * FROM teams %(pred)s")
|
||||
|
||||
(def ^:private sql:get-teams-by-report
|
||||
"WITH teams AS (
|
||||
SELECT t.id t.features, mr.name
|
||||
FROM migration_team_report AS mr
|
||||
JOIN team AS t ON (t.id = mr.team_id)
|
||||
WHERE t.deleted_at IS NULL
|
||||
AND mr.error IS NOT NULL
|
||||
ORDER BY mr.created_at
|
||||
) SELECT id, features FROM teams %(pred)s")
|
||||
|
||||
(def ^:private sql:get-files-by-created-at
|
||||
"SELECT id, features
|
||||
"SELECT id, features,
|
||||
row_number() OVER (ORDER BY created_at DESC) AS rown
|
||||
FROM file
|
||||
WHERE deleted_at IS NULL
|
||||
ORDER BY created_at DESC")
|
||||
|
||||
(def ^:private sql:get-files-by-modified-at
|
||||
"SELECT id, features
|
||||
row_number() OVER (ORDER BY modified_at DESC) AS rown
|
||||
FROM file
|
||||
WHERE deleted_at IS NULL
|
||||
ORDER BY modified_at DESC")
|
||||
|
@ -132,6 +128,7 @@
|
|||
(def ^:private sql:get-files-by-graphics
|
||||
"WITH files AS (
|
||||
SELECT f.id, f.features,
|
||||
row_number() OVER (ORDER BY modified_at) AS rown,
|
||||
(SELECT count(*) FROM file_media_object AS fmo
|
||||
WHERE fmo.mtype = 'image/svg+xml'
|
||||
AND fmo.is_local = false
|
||||
|
@ -141,16 +138,6 @@
|
|||
ORDER BY 3 ASC
|
||||
) SELECT * FROM files %(pred)s")
|
||||
|
||||
(def ^:private sql:get-files-by-report
|
||||
"WITH files AS (
|
||||
SELECT f.id, f.features, mr.label
|
||||
FROM migration_file_report AS mr
|
||||
JOIN file AS f ON (f.id = mr.file_id)
|
||||
WHERE f.deleted_at IS NULL
|
||||
AND mr.error IS NOT NULL
|
||||
ORDER BY mr.created_at
|
||||
) SELECT id, features FROM files %(pred)s")
|
||||
|
||||
(defn- read-pred
|
||||
[entries]
|
||||
(let [entries (if (and (vector? entries)
|
||||
|
@ -181,8 +168,7 @@
|
|||
sql (case query
|
||||
:created-at sql:get-teams-by-created-at
|
||||
:activity sql:get-teams-by-activity
|
||||
:graphics sql:get-teams-by-graphics
|
||||
:report sql:get-teams-by-report)
|
||||
:graphics sql:get-teams-by-graphics)
|
||||
sql (if pred
|
||||
(let [[pred-sql & pred-params] (read-pred pred)]
|
||||
(apply vector
|
||||
|
@ -193,8 +179,7 @@
|
|||
(->> (db/cursor conn sql {:chunk-size 500})
|
||||
(map feat/decode-row)
|
||||
(remove (fn [{:keys [features]}]
|
||||
(contains? features "components/v2")))
|
||||
(map :id))))
|
||||
(contains? features "components/v2"))))))
|
||||
|
||||
(defn- get-files
|
||||
[conn query pred]
|
||||
|
@ -202,8 +187,7 @@
|
|||
sql (case query
|
||||
:created-at sql:get-files-by-created-at
|
||||
:modified-at sql:get-files-by-modified-at
|
||||
:graphics sql:get-files-by-graphics
|
||||
:report sql:get-files-by-report)
|
||||
:graphics sql:get-files-by-graphics)
|
||||
sql (if pred
|
||||
(let [[pred-sql & pred-params] (read-pred pred)]
|
||||
(apply vector
|
||||
|
@ -214,60 +198,7 @@
|
|||
(->> (db/cursor conn sql {:chunk-size 500})
|
||||
(map feat/decode-row)
|
||||
(remove (fn [{:keys [features]}]
|
||||
(contains? features "components/v2")))
|
||||
(map :id))))
|
||||
|
||||
(def ^:private sql:team-report-table
|
||||
"CREATE UNLOGGED TABLE IF NOT EXISTS migration_team_report (
|
||||
id bigserial NOT NULL,
|
||||
label text NOT NULL,
|
||||
team_id UUID NOT NULL,
|
||||
error text NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
elapsed bigint NOT NULL,
|
||||
PRIMARY KEY (label, created_at, id))")
|
||||
|
||||
(def ^:private sql:file-report-table
|
||||
"CREATE UNLOGGED TABLE IF NOT EXISTS migration_file_report (
|
||||
id bigserial NOT NULL,
|
||||
label text NOT NULL,
|
||||
file_id UUID NOT NULL,
|
||||
error text NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
elapsed bigint NOT NULL,
|
||||
PRIMARY KEY (label, created_at, id))")
|
||||
|
||||
(defn- create-report-tables!
|
||||
[system]
|
||||
(db/exec-one! system [sql:team-report-table])
|
||||
(db/exec-one! system [sql:file-report-table]))
|
||||
|
||||
(defn- clean-team-reports!
|
||||
[system label]
|
||||
(db/delete! system :migration-team-report {:label label}))
|
||||
|
||||
(defn- team-report!
|
||||
[system team-id label elapsed error]
|
||||
(db/insert! system :migration-team-report
|
||||
{:label label
|
||||
:team-id team-id
|
||||
:elapsed (inst-ms elapsed)
|
||||
:error error}
|
||||
{::db/return-keys false}))
|
||||
|
||||
(defn- clean-file-reports!
|
||||
[system label]
|
||||
(db/delete! system :migration-file-report {:label label}))
|
||||
|
||||
(defn- file-report!
|
||||
[system file-id label elapsed error]
|
||||
(db/insert! system :migration-file-report
|
||||
{:label label
|
||||
:file-id file-id
|
||||
:elapsed (inst-ms elapsed)
|
||||
:error error}
|
||||
{::db/return-keys false}))
|
||||
|
||||
(contains? features "components/v2"))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PUBLIC API
|
||||
|
@ -280,11 +211,7 @@
|
|||
skip-on-graphic-error? true}}]
|
||||
(l/dbg :hint "migrate:start" :rollback rollback?)
|
||||
(let [tpoint (dt/tpoint)
|
||||
file-id (h/parse-uuid file-id)
|
||||
cache (if (int? cache)
|
||||
(cache/create :executor (::wrk/executor main/system)
|
||||
:max-items cache)
|
||||
nil)]
|
||||
file-id (h/parse-uuid file-id)]
|
||||
|
||||
(binding [feat/*stats* (atom {})
|
||||
feat/*cache* cache]
|
||||
|
@ -315,12 +242,7 @@
|
|||
|
||||
(let [team-id (h/parse-uuid team-id)
|
||||
stats (atom {})
|
||||
tpoint (dt/tpoint)
|
||||
|
||||
cache (if (int? cache)
|
||||
(cache/create :executor (::wrk/executor main/system)
|
||||
:max-items cache)
|
||||
nil)]
|
||||
tpoint (dt/tpoint)]
|
||||
|
||||
(add-watch stats :progress-report (report-progress-files tpoint))
|
||||
|
||||
|
@ -347,7 +269,7 @@
|
|||
"A REPL helper for migrate all teams.
|
||||
|
||||
This function starts multiple concurrent team migration processes
|
||||
until thw maximum number of jobs is reached which by default has the
|
||||
until the maximum number of jobs is reached which by default has the
|
||||
value of `1`. This is controled with the `:max-jobs` option.
|
||||
|
||||
If you want to run this on multiple machines you will need to specify
|
||||
|
@ -383,41 +305,30 @@
|
|||
sjobs (ps/create :permits max-jobs)
|
||||
sprocs (ps/create :permits max-procs)
|
||||
|
||||
cache (if (int? cache)
|
||||
(cache/create :executor (::wrk/executor main/system)
|
||||
:max-items cache)
|
||||
nil)
|
||||
migrate-team
|
||||
(fn [team-id]
|
||||
(let [tpoint (dt/tpoint)]
|
||||
(try
|
||||
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
||||
(fn [system]
|
||||
(db/exec-one! system ["SET idle_in_transaction_session_timeout = 0"])
|
||||
(feat/migrate-team! system team-id
|
||||
:label label
|
||||
:validate? validate?
|
||||
:skip-on-graphic-error? skip-on-graphic-error?)))
|
||||
(try
|
||||
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
||||
(fn [system]
|
||||
(db/exec-one! system ["SET LOCAL idle_in_transaction_session_timeout = 0"])
|
||||
(feat/migrate-team! system team-id
|
||||
:label label
|
||||
:validate? validate?
|
||||
:skip-on-graphic-error? skip-on-graphic-error?)))
|
||||
|
||||
(when (string? label)
|
||||
(team-report! main/system team-id label (tpoint) nil))
|
||||
(catch Throwable cause
|
||||
(l/wrn :hint "unexpected error on processing team (skiping)"
|
||||
:team-id (str team-id))
|
||||
|
||||
(catch Throwable cause
|
||||
(l/wrn :hint "unexpected error on processing team (skiping)"
|
||||
:team-id (str team-id))
|
||||
(events/tap :error
|
||||
(ex-info "unexpected error on processing team (skiping)"
|
||||
{:team-id team-id}
|
||||
cause))
|
||||
|
||||
(events/tap :error
|
||||
(ex-info "unexpected error on processing team (skiping)"
|
||||
{:team-id team-id}
|
||||
cause))
|
||||
(swap! stats update :errors (fnil inc 0)))
|
||||
|
||||
(swap! stats update :errors (fnil inc 0))
|
||||
|
||||
(when (string? label)
|
||||
(team-report! main/system team-id label (tpoint) (ex-message cause))))
|
||||
|
||||
(finally
|
||||
(ps/release! sjobs)))))
|
||||
(finally
|
||||
(ps/release! sjobs))))
|
||||
|
||||
process-team
|
||||
(fn [team-id]
|
||||
|
@ -445,23 +356,18 @@
|
|||
feat/*cache* cache
|
||||
svgo/*semaphore* sprocs]
|
||||
(try
|
||||
(when (string? label)
|
||||
(create-report-tables! main/system)
|
||||
(clean-team-reports! main/system label))
|
||||
|
||||
(db/tx-run! main/system
|
||||
(fn [{:keys [::db/conn] :as system}]
|
||||
(db/exec! conn ["SET statement_timeout = 0"])
|
||||
(db/exec! conn ["SET idle_in_transaction_session_timeout = 0"])
|
||||
(db/exec! conn ["SET LOCAL statement_timeout = 0"])
|
||||
(db/exec! conn ["SET LOCAL idle_in_transaction_session_timeout = 0"])
|
||||
|
||||
(run! process-team
|
||||
(->> (get-teams conn query pred)
|
||||
(filter (fn [team-id]
|
||||
(filter (fn [{:keys [rown]}]
|
||||
(if (int? partitions)
|
||||
(= current-partition (-> (uuid/hash-int team-id)
|
||||
(mod partitions)
|
||||
(inc)))
|
||||
(= current-partition (inc (mod rown partitions)))
|
||||
true)))
|
||||
(map :id)
|
||||
(take max-items)))
|
||||
|
||||
;; Close and await tasks
|
||||
|
@ -480,7 +386,6 @@
|
|||
:rollback rollback?
|
||||
:elapsed elapsed)))))))
|
||||
|
||||
|
||||
(defn migrate-files!
|
||||
"A REPL helper for migrate all files.
|
||||
|
||||
|
@ -521,56 +426,45 @@
|
|||
sjobs (ps/create :permits max-jobs)
|
||||
sprocs (ps/create :permits max-procs)
|
||||
|
||||
cache (if (int? cache)
|
||||
(cache/create :executor (::wrk/executor main/system)
|
||||
:max-items cache)
|
||||
nil)
|
||||
|
||||
migrate-file
|
||||
(fn [file-id]
|
||||
(let [tpoint (dt/tpoint)]
|
||||
(try
|
||||
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
||||
(fn [system]
|
||||
(db/exec-one! system ["SET idle_in_transaction_session_timeout = 0"])
|
||||
(feat/migrate-file! system file-id
|
||||
:label label
|
||||
:validate? validate?
|
||||
:skip-on-graphic-error? skip-on-graphic-error?)))
|
||||
(fn [file-id rown]
|
||||
(try
|
||||
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
||||
(fn [system]
|
||||
(db/exec-one! system ["SET LOCAL idle_in_transaction_session_timeout = 0"])
|
||||
(feat/migrate-file! system file-id
|
||||
:rown rown
|
||||
:label label
|
||||
:validate? validate?
|
||||
:skip-on-graphic-error? skip-on-graphic-error?)))
|
||||
|
||||
(when (string? label)
|
||||
(file-report! main/system file-id label (tpoint) nil))
|
||||
(catch Throwable cause
|
||||
(l/wrn :hint "unexpected error on processing file (skiping)"
|
||||
:file-id (str file-id))
|
||||
|
||||
(catch Throwable cause
|
||||
(l/wrn :hint "unexpected error on processing file (skiping)"
|
||||
:file-id (str file-id))
|
||||
(events/tap :error
|
||||
(ex-info "unexpected error on processing file (skiping)"
|
||||
{:file-id file-id}
|
||||
cause))
|
||||
|
||||
(events/tap :error
|
||||
(ex-info "unexpected error on processing file (skiping)"
|
||||
{:file-id file-id}
|
||||
cause))
|
||||
(swap! stats update :errors (fnil inc 0)))
|
||||
|
||||
(swap! stats update :errors (fnil inc 0))
|
||||
|
||||
(when (string? label)
|
||||
(file-report! main/system file-id label (tpoint) (ex-message cause))))
|
||||
|
||||
(finally
|
||||
(ps/release! sjobs)))))
|
||||
(finally
|
||||
(ps/release! sjobs))))
|
||||
|
||||
process-file
|
||||
(fn [file-id]
|
||||
(fn [{:keys [id rown]}]
|
||||
(ps/acquire! sjobs)
|
||||
(let [ts (tpoint)]
|
||||
(if (and mtime (neg? (compare mtime ts)))
|
||||
(do
|
||||
(l/inf :hint "max time constraint reached"
|
||||
:file-id (str file-id)
|
||||
:file-id (str id)
|
||||
:elapsed (dt/format-duration ts))
|
||||
(ps/release! sjobs)
|
||||
(reduced nil))
|
||||
|
||||
(px/run! executor (partial migrate-file file-id)))))]
|
||||
(px/run! executor (partial migrate-file id rown)))))]
|
||||
|
||||
(l/dbg :hint "migrate:start"
|
||||
:label label
|
||||
|
@ -584,22 +478,16 @@
|
|||
feat/*cache* cache
|
||||
svgo/*semaphore* sprocs]
|
||||
(try
|
||||
(when (string? label)
|
||||
(create-report-tables! main/system)
|
||||
(clean-file-reports! main/system label))
|
||||
|
||||
(db/tx-run! main/system
|
||||
(fn [{:keys [::db/conn] :as system}]
|
||||
(db/exec! conn ["SET statement_timeout = 0"])
|
||||
(db/exec! conn ["SET idle_in_transaction_session_timeout = 0"])
|
||||
(db/exec! conn ["SET LOCAL statement_timeout = 0"])
|
||||
(db/exec! conn ["SET LOCAL idle_in_transaction_session_timeout = 0"])
|
||||
|
||||
(run! process-file
|
||||
(->> (get-files conn query pred)
|
||||
(filter (fn [file-id]
|
||||
(filter (fn [{:keys [rown] :as row}]
|
||||
(if (int? partitions)
|
||||
(= current-partition (-> (uuid/hash-int file-id)
|
||||
(mod partitions)
|
||||
(inc)))
|
||||
(= current-partition (inc (mod rown partitions)))
|
||||
true)))
|
||||
(take max-items)))
|
||||
|
||||
|
@ -619,6 +507,100 @@
|
|||
:rollback rollback?
|
||||
:elapsed elapsed)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; CACHE POPULATE
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def sql:sobjects-for-cache
|
||||
"SELECT id,
|
||||
row_number() OVER (ORDER BY created_at) AS index
|
||||
FROM storage_object
|
||||
WHERE (metadata->>'~:bucket' = 'file-media-object' OR
|
||||
metadata->>'~:bucket' IS NULL)
|
||||
AND metadata->>'~:content-type' = 'image/svg+xml'
|
||||
AND deleted_at IS NULL
|
||||
AND size < 1135899
|
||||
ORDER BY created_at ASC")
|
||||
|
||||
(defn populate-cache!
|
||||
"A REPL helper for migrate all files.
|
||||
|
||||
This function starts multiple concurrent file migration processes
|
||||
until thw maximum number of jobs is reached which by default has the
|
||||
value of `1`. This is controled with the `:max-jobs` option.
|
||||
|
||||
If you want to run this on multiple machines you will need to specify
|
||||
the total number of partitions and the current partition.
|
||||
|
||||
In order to get the report table populated, you will need to provide
|
||||
a correct `:label`. That label is also used for persist a file
|
||||
snaphot before continue with the migration."
|
||||
[& {:keys [max-jobs] :or {max-jobs 1}}]
|
||||
|
||||
(let [tpoint (dt/tpoint)
|
||||
|
||||
factory (px/thread-factory :virtual false :prefix "penpot/cache/")
|
||||
executor (px/cached-executor :factory factory)
|
||||
|
||||
sjobs (ps/create :permits max-jobs)
|
||||
|
||||
retrieve-sobject
|
||||
(fn [id index]
|
||||
(let [path (feat/get-sobject-cache-path id)
|
||||
parent (fs/parent path)]
|
||||
|
||||
(try
|
||||
(when-not (fs/exists? parent)
|
||||
(fs/create-dir parent))
|
||||
|
||||
(if (fs/exists? path)
|
||||
(l/inf :hint "create cache entry" :status "exists" :index index :id (str id) :path (str path))
|
||||
(let [svg-data (feat/get-optimized-svg id)]
|
||||
(with-open [^java.lang.AutoCloseable stream (io/output-stream path)]
|
||||
(let [writer (fres/writer stream)]
|
||||
(fres/write! writer svg-data)))
|
||||
|
||||
(l/inf :hint "create cache entry" :status "created"
|
||||
:index index
|
||||
:id (str id)
|
||||
:path (str path))))
|
||||
|
||||
(catch Throwable cause
|
||||
(l/wrn :hint "create cache entry"
|
||||
:status "error"
|
||||
:index index
|
||||
:id (str id)
|
||||
:path (str path)
|
||||
:cause cause))
|
||||
|
||||
(finally
|
||||
(ps/release! sjobs)))))
|
||||
|
||||
process-sobject
|
||||
(fn [{:keys [id index]}]
|
||||
(ps/acquire! sjobs)
|
||||
(px/run! executor (partial retrieve-sobject id index)))]
|
||||
|
||||
(l/dbg :hint "migrate:start"
|
||||
:max-jobs max-jobs)
|
||||
|
||||
(try
|
||||
(binding [feat/*system* main/system]
|
||||
(run! process-sobject
|
||||
(db/exec! main/system [sql:sobjects-for-cache]))
|
||||
|
||||
;; Close and await tasks
|
||||
(pu/close! executor))
|
||||
|
||||
{:elapsed (dt/format-duration (tpoint))}
|
||||
|
||||
(catch Throwable cause
|
||||
(l/dbg :hint "populate:error" :cause cause))
|
||||
|
||||
(finally
|
||||
(let [elapsed (dt/format-duration (tpoint))]
|
||||
(l/dbg :hint "populate:end"
|
||||
:elapsed elapsed))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; FILE PROCESS HELPERS
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
#?(:clj (set! *warn-on-reflection* true))
|
||||
|
||||
(declare reduce-objects)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -327,12 +329,9 @@
|
|||
"Selects the shape that will be the base to add the shapes over"
|
||||
[objects selected]
|
||||
(let [;; Gets the tree-index for all the shapes
|
||||
indexed-shapes (indexed-shapes objects)
|
||||
|
||||
indexed-shapes (indexed-shapes objects selected)
|
||||
;; Filters the selected and retrieve a list of ids
|
||||
sorted-ids (->> indexed-shapes
|
||||
(filter (comp selected second))
|
||||
(map second))]
|
||||
sorted-ids (map val indexed-shapes)]
|
||||
|
||||
;; The first id will be the top-most
|
||||
(get objects (first sorted-ids))))
|
||||
|
@ -486,43 +485,62 @@
|
|||
|
||||
(reduce add-element (d/ordered-set) ids)))
|
||||
|
||||
(defn indexed-shapes
|
||||
"Retrieves a list with the indexes for each element in the layer tree.
|
||||
This will be used for shift+selection."
|
||||
[objects]
|
||||
(letfn [(red-fn [cur-idx id]
|
||||
(let [[prev-idx _] (first cur-idx)
|
||||
prev-idx (or prev-idx 0)
|
||||
cur-idx (conj cur-idx (d/vec2 (inc prev-idx) id))]
|
||||
(rec-index cur-idx id)))
|
||||
(rec-index [cur-idx id]
|
||||
(let [object (get objects id)]
|
||||
(reduce red-fn cur-idx (reverse (:shapes object)))))]
|
||||
(into {} (rec-index '() uuid/zero))))
|
||||
(defn- indexed-shapes
|
||||
"Retrieves a vector with the indexes for each element in the layer
|
||||
tree. This will be used for shift+selection."
|
||||
[objects selected]
|
||||
(loop [index 1
|
||||
result (transient [])
|
||||
;; Flag to start adding elements to the index
|
||||
add? false
|
||||
;; Only add elements while we're in the selection, we finish when the selection is over
|
||||
pending (set selected)
|
||||
shapes (-> objects
|
||||
(get uuid/zero)
|
||||
(get :shapes)
|
||||
(rseq))]
|
||||
|
||||
(let [shape-id (first shapes)]
|
||||
(if (and (d/not-empty? pending) shape-id)
|
||||
(let [shape (get objects shape-id)
|
||||
add? (or add? (contains? selected shape-id))
|
||||
pending (disj pending shape-id)
|
||||
result (if add?
|
||||
(conj! result (d/vec2 index shape-id))
|
||||
result)]
|
||||
(if-let [children (get shape :shapes)]
|
||||
(recur (inc index)
|
||||
result
|
||||
add?
|
||||
pending
|
||||
(concat (rseq children) (rest shapes)))
|
||||
(recur (inc index)
|
||||
result
|
||||
add?
|
||||
pending
|
||||
(rest shapes))))
|
||||
(persistent! result)))))
|
||||
|
||||
(defn expand-region-selection
|
||||
"Given a selection selects all the shapes between the first and last in
|
||||
an indexed manner (shift selection)"
|
||||
[objects selection]
|
||||
(let [indexed-shapes (indexed-shapes objects)
|
||||
filter-indexes (->> indexed-shapes
|
||||
(filter (comp selection second))
|
||||
(map first))
|
||||
|
||||
from (apply min filter-indexes)
|
||||
to (apply max filter-indexes)]
|
||||
(->> indexed-shapes
|
||||
(filter (fn [[idx _]] (and (>= idx from) (<= idx to))))
|
||||
(map second)
|
||||
(into #{}))))
|
||||
(let [selection (if (set? selection) selection (set selection))
|
||||
indexed-shapes (indexed-shapes objects selection)
|
||||
indexes (map key indexed-shapes)
|
||||
from (apply min indexes)
|
||||
to (apply max indexes)
|
||||
xform (comp
|
||||
(filter (fn [[idx _]] (and (>= idx from) (<= idx to))))
|
||||
(map val))]
|
||||
(into #{} xform indexed-shapes)))
|
||||
|
||||
(defn order-by-indexed-shapes
|
||||
[objects ids]
|
||||
(let [ids (if (set? ids) ids (set ids))]
|
||||
(->> (indexed-shapes objects)
|
||||
(filter (fn [o] (contains? ids (val o))))
|
||||
(sort-by key)
|
||||
(map val))))
|
||||
"Retrieves a ordered vector for each element in the layer tree and
|
||||
filted by selected set"
|
||||
[objects selected]
|
||||
(let [selected (if (set? selected) selected (set selected))]
|
||||
(sequence (map val) (indexed-shapes objects selected))))
|
||||
|
||||
(defn get-index-replacement
|
||||
"Given a collection of shapes, calculate their positions
|
||||
|
|
|
@ -13,27 +13,27 @@
|
|||
(defn assign-proportions
|
||||
[shape]
|
||||
(let [{:keys [width height]} (:selrect shape)]
|
||||
(assoc shape :proportion (/ width height))))
|
||||
|
||||
;; --- Setup Proportions
|
||||
|
||||
(assoc shape :proportion (float (/ width height))))) ; Note: we need to convert explicitly to float.
|
||||
; In Clojure (not clojurescript) when we divide
|
||||
;; --- Setup Proportions ; two integers it does not create a float, but
|
||||
; a clojure.lang.Ratio object.
|
||||
(defn setup-proportions-image
|
||||
[{:keys [metadata] :as shape}]
|
||||
(let [{:keys [width height]} metadata]
|
||||
(assoc shape
|
||||
:proportion (/ width height)
|
||||
:proportion (float (/ width height))
|
||||
:proportion-lock true)))
|
||||
|
||||
(defn setup-proportions-size
|
||||
[{{:keys [width height]} :selrect :as shape}]
|
||||
(assoc shape
|
||||
:proportion (/ width height)
|
||||
:proportion (float (/ width height))
|
||||
:proportion-lock true))
|
||||
|
||||
(defn setup-proportions-const
|
||||
[shape]
|
||||
(assoc shape
|
||||
:proportion 1
|
||||
:proportion 1.0
|
||||
:proportion-lock false))
|
||||
|
||||
(defn setup-proportions
|
||||
|
|
|
@ -483,8 +483,8 @@
|
|||
(defn- setup-image
|
||||
[{:keys [metadata] :as shape}]
|
||||
(-> shape
|
||||
(assoc :proportion (/ (:width metadata)
|
||||
(:height metadata)))
|
||||
(assoc :proportion (float (/ (:width metadata)
|
||||
(:height metadata))))
|
||||
(assoc :proportion-lock true)))
|
||||
|
||||
(defn setup-shape
|
||||
|
|
|
@ -543,19 +543,21 @@
|
|||
(or (:layout-item-z-index shape) 0)))
|
||||
|
||||
(defn- comparator-layout-z-index
|
||||
[[idx-a child-a] [idx-b child-b]]
|
||||
[reverse? [idx-a child-a] [idx-b child-b]]
|
||||
(cond
|
||||
(> (layout-z-index child-a) (layout-z-index child-b)) 1
|
||||
(< (layout-z-index child-a) (layout-z-index child-b)) -1
|
||||
(and (< idx-a idx-b) reverse?) -1
|
||||
(and (> idx-a idx-b) reverse?) 1
|
||||
(< idx-a idx-b) 1
|
||||
(> idx-a idx-b) -1
|
||||
:else 0))
|
||||
|
||||
(defn sort-layout-children-z-index
|
||||
[children]
|
||||
[children reverse?]
|
||||
(->> children
|
||||
(d/enumerate)
|
||||
(sort comparator-layout-z-index)
|
||||
(sort (partial comparator-layout-z-index reverse?))
|
||||
(mapv second)))
|
||||
|
||||
(defn change-h-sizing?
|
||||
|
@ -1292,11 +1294,14 @@
|
|||
(->> (range start-index (inc to-index))
|
||||
(map vector shape-ids)
|
||||
(reduce (fn [[parent cells] [shape-id idx]]
|
||||
(let [[parent cells] (free-cell-push parent cells idx)]
|
||||
[(update-in parent [:layout-grid-cells (get-in cells [idx :id])]
|
||||
assoc :position :manual
|
||||
:shapes [shape-id])
|
||||
cells]))
|
||||
;; If the shape to put in a cell is the same that is already in the cell we do nothing
|
||||
(if (= shape-id (get-in parent [:layout-grid-cells (get-in cells [idx :id]) :shapes 0]))
|
||||
[parent cells]
|
||||
(let [[parent cells] (free-cell-push parent cells idx)]
|
||||
[(update-in parent [:layout-grid-cells (get-in cells [idx :id])]
|
||||
assoc :position :manual
|
||||
:shapes [shape-id])
|
||||
cells])))
|
||||
[parent cells])
|
||||
(first)))
|
||||
parent)))
|
||||
|
|
BIN
frontend/resources/images/thumbnails/template-avataaars.jpg
Normal file
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 91 KiB |
BIN
frontend/resources/images/thumbnails/template-font-awesome.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
frontend/resources/images/thumbnails/template-lucide-icons.jpg
Normal file
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 39 KiB |
BIN
frontend/resources/images/thumbnails/template-plants-app.jpg
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
frontend/resources/images/thumbnails/template-ux-notes.jpg
Normal file
After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 176 KiB |
|
@ -273,7 +273,8 @@
|
|||
;; color
|
||||
attrs (cond-> attrs
|
||||
(:gradient attrs) (get-in [:gradient :stops 0]))
|
||||
new-attrs (merge (get-in shape [:shadow index :color]) attrs)]
|
||||
new-attrs (-> (merge (get-in shape [:shadow index :color]) attrs)
|
||||
(d/without-nils))]
|
||||
(assoc-in shape [:shadow index :color] new-attrs))))))))
|
||||
|
||||
(defn add-shadow
|
||||
|
@ -602,7 +603,8 @@
|
|||
(merge data)
|
||||
(materialize-color-components))))
|
||||
(-> state
|
||||
(dissoc :gradient :stops :editing-stop)))))))
|
||||
(dissoc :gradient :stops :editing-stop)
|
||||
(assoc :type :color)))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when add-recent?
|
||||
|
|
|
@ -60,8 +60,7 @@
|
|||
(let [local (:comments-local state)]
|
||||
(cond
|
||||
(:draft local) (rx/of (dcm/close-thread))
|
||||
(:open local) (rx/of (dcm/close-thread))
|
||||
:else (rx/of #(dissoc % :workspace-drawing)))))))
|
||||
(:open local) (rx/of (dcm/close-thread)))))))
|
||||
|
||||
;; Event responsible of the what should be executed when user clicked
|
||||
;; on the comments layer. An option can be create a new draft thread,
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-layout (fn [workspace-layout]
|
||||
(if (= tool :comments)
|
||||
(disj workspace-layout :document-history)
|
||||
workspace-layout)))
|
||||
(update :workspace-drawing assoc :tool tool)
|
||||
;; When changing drawing tool disable "scale text" mode
|
||||
;; automatically, to help users that ignore how this
|
||||
|
|
|
@ -585,7 +585,6 @@
|
|||
:else
|
||||
[move-vector nil])]
|
||||
|
||||
|
||||
(-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
|
||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index cell-data)
|
||||
(dwm/set-modifiers false false {:snap-ignore-axis snap-ignore-axis}))))))
|
||||
|
@ -608,11 +607,11 @@
|
|||
(->> move-stream
|
||||
(rx/last)
|
||||
(rx/mapcat
|
||||
(fn [[_ target-frame drop-index cell-data]]
|
||||
(fn [[_ target-frame drop-index drop-cell]]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dwm/apply-modifiers {:undo-transation? false})
|
||||
(move-shapes-to-frame ids target-frame drop-index cell-data)
|
||||
(move-shapes-to-frame ids target-frame drop-index drop-cell)
|
||||
(finish-transform)
|
||||
(dwu/commit-undo-transaction undo-id))))))))))))))
|
||||
|
||||
|
|
|
@ -239,6 +239,7 @@
|
|||
(let [form (or (unchecked-get props "form")
|
||||
(mf/use-ctx form-ctx))
|
||||
name (unchecked-get props "name")
|
||||
image (unchecked-get props "image")
|
||||
|
||||
current-value (or (dm/get-in @form [:data name] "")
|
||||
(unchecked-get props "value"))
|
||||
|
@ -260,7 +261,9 @@
|
|||
|
||||
(when (fn? on-change)
|
||||
(on-change name value)))))]
|
||||
[:div {:class (dm/str class " " (stl/css :custom-radio))}
|
||||
[:div {:class (if image
|
||||
class
|
||||
(dm/str class " " (stl/css :custom-radio)))}
|
||||
(for [{:keys [image icon value label area]} options]
|
||||
(let [image? (some? image)
|
||||
icon? (some? icon)
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
{:label (tr "questions.never-used-one") :area "image6" :value "never-used-a-tool" :icon i/curve}
|
||||
{:label (tr "questions.other") :value "other" :area "other"}]
|
||||
:name :experience-design-tool
|
||||
:image true
|
||||
:class (stl/css :image-radio)
|
||||
:on-change on-design-tool-change}]
|
||||
|
||||
|
|
|
@ -168,9 +168,10 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
reverse? (and (ctl/flex-layout? shape) (ctl/reverse? shape))
|
||||
childs (cond-> childs
|
||||
(ctl/any-layout? shape)
|
||||
(ctl/sort-layout-children-z-index))]
|
||||
(ctl/sort-layout-children-z-index reverse?))]
|
||||
|
||||
[:> frame-container props
|
||||
[:g.frame-children
|
||||
|
|
|
@ -92,7 +92,8 @@
|
|||
(fn []
|
||||
(if from-viewer
|
||||
(st/emit! (dcm/update-options {:show-sidebar? false}))
|
||||
(st/emit! :interrupt (dw/deselect-all true)))))
|
||||
(st/emit! (dw/clear-edition-mode)
|
||||
(dw/deselect-all true)))))
|
||||
|
||||
tgroups (->> threads
|
||||
(dcm/group-threads-by-page))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.main.data.events :as ev]
|
||||
[app.main.data.shortcuts :as scd]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.drawing.common :as dwc]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -21,7 +22,6 @@
|
|||
[app.main.ui.workspace.presence :refer [active-sessions]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.timers :as ts]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -141,7 +141,7 @@
|
|||
(mf/defc right-header
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [file layout page-id]}]
|
||||
(let [file-id (:id file)
|
||||
(let [file-id (:id file)
|
||||
|
||||
zoom (mf/deref refs/selected-zoom)
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
|
@ -169,33 +169,29 @@
|
|||
|
||||
active-comments
|
||||
(mf/use-fn
|
||||
(mf/deps layout)
|
||||
(fn []
|
||||
(st/emit! :interrupt
|
||||
(dw/clear-edition-mode))
|
||||
;; Delay so anything that launched :interrupt can finish
|
||||
(ts/schedule 100 #(st/emit! (dw/select-for-drawing :comments)))))
|
||||
(dw/clear-edition-mode)
|
||||
(-> (dw/remove-layout-flag :document-history)
|
||||
(vary-meta assoc ::ev/origin "workspace-header"))
|
||||
(dw/select-for-drawing :comments))))
|
||||
|
||||
toggle-comments
|
||||
(mf/use-fn
|
||||
(mf/deps selected-drawtool)
|
||||
(fn [_]
|
||||
(when (contains? layout :document-history)
|
||||
(st/emit! (-> (dw/remove-layout-flag :document-history)
|
||||
(vary-meta assoc ::ev/origin "workspace-header"))))
|
||||
|
||||
(if (= :comments selected-drawtool)
|
||||
(st/emit! :interrupt)
|
||||
(if (= selected-drawtool :comments)
|
||||
(st/emit! (dwc/clear-drawing))
|
||||
(active-comments))))
|
||||
|
||||
toggle-history
|
||||
(mf/use-fn
|
||||
(mf/deps selected-drawtool)
|
||||
(fn []
|
||||
|
||||
(when (= :comments selected-drawtool)
|
||||
(st/emit! :interrupt
|
||||
(-> (dw/toggle-layout-flag :comments)
|
||||
(vary-meta assoc ::ev/origin "workspace-header"))))
|
||||
(dw/clear-edition-mode)))
|
||||
|
||||
(st/emit! (-> (dw/toggle-layout-flag :document-history)
|
||||
(vary-meta assoc ::ev/origin "workspace-header")))))]
|
||||
|
|
|
@ -266,7 +266,7 @@
|
|||
|
||||
rule-area-size (/ rulers/ruler-area-size zoom)]
|
||||
|
||||
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only?)
|
||||
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only? drawing-tool drawing-path?)
|
||||
(hooks/setup-viewport-size vport viewport-ref)
|
||||
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? workspace-read-only?)
|
||||
(hooks/setup-keyboard alt? mod? space? z? shift?)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.grid-layout.shortcuts :as gsc]
|
||||
[app.main.data.workspace.path.shortcuts :as psc]
|
||||
[app.main.data.workspace.shortcuts :as wsc]
|
||||
|
@ -39,13 +40,26 @@
|
|||
[rumext.v2 :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(defn setup-dom-events [zoom disable-paste in-viewport? workspace-read-only?]
|
||||
(defn setup-dom-events [zoom disable-paste in-viewport? workspace-read-only? drawing-tool drawing-path?]
|
||||
(let [on-key-down (actions/on-key-down)
|
||||
on-key-up (actions/on-key-up)
|
||||
on-mouse-wheel (actions/on-mouse-wheel zoom)
|
||||
on-paste (actions/on-paste disable-paste in-viewport? workspace-read-only?)
|
||||
on-pointer-down (mf/use-fn
|
||||
(mf/deps drawing-tool drawing-path?)
|
||||
(fn [_]
|
||||
(when drawing-path?
|
||||
(st/emit! (dwe/clear-edition-mode)))))
|
||||
on-blur (mf/use-fn #(st/emit! (mse/->BlurEvent)))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps drawing-tool drawing-path?)
|
||||
(fn []
|
||||
(let [keys [(events/listen js/window EventType.POINTERDOWN on-pointer-down)]]
|
||||
(fn []
|
||||
(doseq [key keys]
|
||||
(events/unlistenByKey key))))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps on-key-down on-key-up on-mouse-wheel on-paste workspace-read-only?)
|
||||
(fn []
|
||||
|
|
|
@ -197,6 +197,12 @@
|
|||
bs (* 4 zoom-inverse)]
|
||||
[:*
|
||||
[:g.viewport-frame-background
|
||||
;; Fix for a Firefox bug that shows some strange artifacts when creating shape
|
||||
[:rect {:x 0 :y 0 :width 1 :height 1
|
||||
:fill "none"
|
||||
:stroke-width 0.1
|
||||
:stroke "rgba(0,0,0,0)"}]
|
||||
|
||||
;; This goes behind because if it goes in front the background bleeds through
|
||||
[:path {:d (rulers-inside-path x1 y1 x2 y2 br bw)
|
||||
:fill "none"
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
([objects shape level]
|
||||
(when (and (some? shape) (some? (:selrect shape)))
|
||||
(let [indent (str/repeat " " level)
|
||||
maybe-reverse (if (ctl/any-layout? shape) reverse identity)
|
||||
|
||||
shape-html
|
||||
(cond
|
||||
|
@ -65,15 +64,18 @@
|
|||
indent)
|
||||
|
||||
:else
|
||||
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||
indent
|
||||
(dm/str (d/name (:type shape)) " "
|
||||
(cgc/shape->selector shape))
|
||||
(->> (:shapes shape)
|
||||
(maybe-reverse)
|
||||
(map #(generate-html objects (get objects %) (inc level)))
|
||||
(str/join "\n"))
|
||||
indent))
|
||||
(let [children (->> shape :shapes (map #(get objects %)))
|
||||
reverse? (ctl/any-layout? shape)
|
||||
;; The order for layout elements is the reverse of SVG order
|
||||
children (cond-> children reverse? reverse)]
|
||||
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||
indent
|
||||
(dm/str (d/name (:type shape)) " "
|
||||
(cgc/shape->selector shape))
|
||||
(->> children
|
||||
(map #(generate-html objects % (inc level)))
|
||||
(str/join "\n"))
|
||||
indent)))
|
||||
|
||||
shape-html
|
||||
(if (cgc/has-wrapper? objects shape)
|
||||
|
|
|
@ -614,14 +614,14 @@
|
|||
(rx/tap #(rx/end! progress-str)))]))
|
||||
|
||||
(defn create-files
|
||||
[{:keys [features] :as context} files]
|
||||
[{:keys [system-features] :as context} files]
|
||||
|
||||
(let [data (group-by :file-id files)]
|
||||
(rx/concat
|
||||
(->> (rx/from files)
|
||||
(rx/map #(merge context %))
|
||||
(rx/merge-map (fn [context]
|
||||
(->> (create-file context features)
|
||||
(->> (create-file context system-features)
|
||||
(rx/map #(vector % (first (get data (:file-id context)))))))))
|
||||
|
||||
(->> (rx/from files)
|
||||
|
@ -694,7 +694,7 @@
|
|||
|
||||
(let [context {:project-id project-id
|
||||
:resolve (resolve-factory)
|
||||
:features features}
|
||||
:system-features features}
|
||||
zip-files (filter #(= "application/zip" (:type %)) files)
|
||||
binary-files (filter #(= "application/octet-stream" (:type %)) files)]
|
||||
|
||||
|
|