mirror of
https://github.com/penpot/penpot.git
synced 2026-01-20 04:10:41 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
accd5226d7 | ||
|
|
16a1fd14e5 | ||
|
|
824bb19c7e | ||
|
|
d0f3e0f0b0 | ||
|
|
43ba2b05e8 | ||
|
|
d5ccb704b2 | ||
|
|
6d21fcc9de | ||
|
|
77741b49a7 | ||
|
|
a7e0cfc609 | ||
|
|
50a6355537 | ||
|
|
264aef277d | ||
|
|
78d0e6d059 | ||
|
|
6d41d36b3a | ||
|
|
bb97df373e | ||
|
|
a41af032cd | ||
|
|
86ee4f55c5 | ||
|
|
63cd3ae025 | ||
|
|
cafb7abb53 | ||
|
|
e5b6c4a9e0 | ||
|
|
1d5bad5523 | ||
|
|
96d6868b45 | ||
|
|
b739d8bd0c | ||
|
|
dd803dc1de | ||
|
|
b627c10737 | ||
|
|
95f4a9bd29 | ||
|
|
a2b8f19ff3 | ||
|
|
898182e3d5 | ||
|
|
e03c822b51 | ||
|
|
a3aabf3b7d |
129
.github/workflows/build-bundles.yml
vendored
Normal file
129
.github/workflows/build-bundles.yml
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
name: Build and Upload Penpot Bundles non-prod
|
||||
|
||||
on:
|
||||
# Create bundler for every tag
|
||||
push:
|
||||
tags:
|
||||
- '**' # Pattern matched against refs/tags
|
||||
# Create bundler every hour between 5:00 and 20:00 on working days
|
||||
schedule:
|
||||
- cron: '0 5-20 * * 1-5'
|
||||
# Create bundler from manual action
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
zip_mode:
|
||||
# zip_mode defines how the build artifacts are packaged:
|
||||
# - 'individual': creates one ZIP file per component (frontend, backend, exporter)
|
||||
# - 'all': creates a single ZIP containing all components
|
||||
# - null: for the rest of cases (non-manual events)
|
||||
description: 'Bundle packaging mode'
|
||||
required: false
|
||||
default: 'individual'
|
||||
type: choice
|
||||
options:
|
||||
- individual
|
||||
- all
|
||||
|
||||
jobs:
|
||||
build-bundles:
|
||||
name: Build and Upload Penpot Bundles
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract somer useful variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "gh_branch=${{ github.base_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Set up Docker Buildx for multi-arch build
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Run manage.sh build-bundle from host
|
||||
run: ./manage.sh build-bundle
|
||||
|
||||
- name: Prepare directories for zipping
|
||||
run: |
|
||||
mkdir zips
|
||||
mv bundles penpot
|
||||
|
||||
- name: Create zip bundles for zip_mode == 'all'
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
run: |
|
||||
echo "📦 Packaging Penpot 'all' bundles..."
|
||||
zip -r zips/penpot-all-bundles.zip penpot
|
||||
|
||||
- name: Create zip bundles for zip_mode != 'all'
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
run: |
|
||||
echo "📦 Packaging Penpot 'individual' bundles..."
|
||||
zip -r zips/penpot-frontend.zip penpot/frontend
|
||||
zip -r zips/penpot-backend.zip penpot/backend
|
||||
zip -r zips/penpot-exporter.zip penpot/exporter
|
||||
|
||||
- name: Upload unified 'all' bundle
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: penpot-all-bundles
|
||||
path: zips/penpot-all-bundles.zip
|
||||
|
||||
- name: Upload individual bundles
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: penpot-individual-bundles
|
||||
path: |
|
||||
zips/penpot-frontend.zip
|
||||
zips/penpot-backend.zip
|
||||
zips/penpot-exporter.zip
|
||||
|
||||
- name: Upload unified 'all' bundle to S3
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
run: |
|
||||
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.gh_branch}}.zip
|
||||
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.commit_hash }}.zip
|
||||
|
||||
- name: Upload 'individual' bundles to S3
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
run: |
|
||||
for name in penpot-frontend penpot-backend penpot-exporter; do
|
||||
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.gh_branch }}-latest.zip
|
||||
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.commit_hash }}.zip
|
||||
done
|
||||
|
||||
- name: Notify Mattermost about automatic bundles
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
TEXT: |
|
||||
📦 *Penpot bundle automatically generated*
|
||||
📄 PR: ${{ github.event.pull_request.title }}
|
||||
🔁 From: \`${{ github.head_ref }}\` to \`{{ github.base_ref }}\`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
- name: Notify Mattermost about manual bundles
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
TEXT: |
|
||||
📦 *Penpot bundle manually generated*
|
||||
📄 Triggered from branch: `${{ github.ref_name}}`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
- name: Print artifact summary URL
|
||||
run: |
|
||||
echo "📦 Artifacts available at:"
|
||||
echo "🔗 https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
@@ -39,6 +39,7 @@ on-premises instances** that want to keep up to date.
|
||||
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
|
||||
- Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220)
|
||||
- Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144)
|
||||
- Fix problem when double click on hidden shapes [Taiga #11314](https://tree.taiga.io/project/penpot/issue/11314)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
@@ -61,6 +62,8 @@ on-premises instances** that want to keep up to date.
|
||||
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
|
||||
- Fix entering long project name [Taiga #11417](https://tree.taiga.io/project/penpot/issue/11417)
|
||||
- Fix slow color picker [Taiga #11019](https://tree.taiga.io/project/penpot/issue/11019)
|
||||
- Fix tooltip position after click [Taiga #11405](https://tree.taiga.io/project/penpot/issue/11405)
|
||||
- Fix incorrect media translation on paste text with fill images [Github #6845](https://github.com/penpot/penpot/pull/6845)
|
||||
|
||||
## 2.7.2
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.binfile.common :as bfc]
|
||||
[app.binfile.migrations :as bfm]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.migrations :as-alias fmg]
|
||||
@@ -54,7 +53,7 @@
|
||||
[:map {:title "Manifest"}
|
||||
[:version ::sm/int]
|
||||
[:type :string]
|
||||
|
||||
[:referer {:optional true} :string]
|
||||
[:generated-by {:optional true} :string]
|
||||
|
||||
[:files
|
||||
@@ -373,6 +372,7 @@
|
||||
params {:type "penpot/export-files"
|
||||
:version 1
|
||||
:generated-by (str "penpot/" (:full cf/version))
|
||||
:refer "penpot"
|
||||
:files (vec (vals files))
|
||||
:relations rels}]
|
||||
(write-entry! output "manifest.json" params))))
|
||||
@@ -878,13 +878,8 @@
|
||||
(defn- import-files
|
||||
[{:keys [::bfc/timestamp ::bfc/input ::bfc/name] :or {timestamp (dt/now)} :as cfg}]
|
||||
|
||||
(dm/assert!
|
||||
"expected zip file"
|
||||
(instance? ZipFile input))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid instant"
|
||||
(dt/instant? timestamp))
|
||||
(assert (instance? ZipFile input) "expected zip file")
|
||||
(assert (dt/instant? timestamp) "expected valid instant")
|
||||
|
||||
(let [manifest (-> (read-manifest input)
|
||||
(validate-manifest))
|
||||
@@ -896,6 +891,7 @@
|
||||
:hint "unexpected type on manifest"
|
||||
:manifest manifest))
|
||||
|
||||
|
||||
;; Check if all files referenced on manifest are present
|
||||
(doseq [{file-id :id features :features} (:files manifest)]
|
||||
(let [path (str "files/" file-id ".json")]
|
||||
@@ -956,14 +952,13 @@
|
||||
|
||||
[{:keys [::bfc/ids] :as cfg} output]
|
||||
|
||||
(dm/assert!
|
||||
"expected a set of uuid's for `::bfc/ids` parameter"
|
||||
(and (set? ids)
|
||||
(every? uuid? ids)))
|
||||
(assert
|
||||
(and (set? ids) (every? uuid? ids))
|
||||
"expected a set of uuid's for `::bfc/ids` parameter")
|
||||
|
||||
(dm/assert!
|
||||
"expected instance of jio/IOFactory for `input`"
|
||||
(satisfies? jio/IOFactory output))
|
||||
(assert
|
||||
(satisfies? jio/IOFactory output)
|
||||
"expected instance of jio/IOFactory for `input`")
|
||||
|
||||
(let [id (uuid/next)
|
||||
tp (dt/tpoint)
|
||||
@@ -1002,14 +997,14 @@
|
||||
(defn import-files!
|
||||
[{:keys [::bfc/input] :as cfg}]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid profile-id and project-id on `cfg`"
|
||||
(assert
|
||||
(and (uuid? (::bfc/profile-id cfg))
|
||||
(uuid? (::bfc/project-id cfg))))
|
||||
(uuid? (::bfc/project-id cfg)))
|
||||
"expected valid profile-id and project-id on `cfg`")
|
||||
|
||||
(dm/assert!
|
||||
"expected instance of jio/IOFactory for `input`"
|
||||
(io/coercible? input))
|
||||
(assert
|
||||
(io/coercible? input)
|
||||
"expected instance of jio/IOFactory for `input`")
|
||||
|
||||
(let [id (uuid/next)
|
||||
tp (dt/tpoint)
|
||||
@@ -1029,3 +1024,9 @@
|
||||
:id (str id)
|
||||
:elapsed (dt/format-duration (tp))
|
||||
:error? (some? @cs))))))
|
||||
|
||||
(defn get-manifest
|
||||
[path]
|
||||
(with-open [input (ZipFile. (fs/file path))]
|
||||
(-> (read-manifest input)
|
||||
(validate-manifest))))
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
(if (or (instance? java.util.concurrent.CompletionException cause)
|
||||
(instance? java.util.concurrent.ExecutionException cause))
|
||||
(-> record
|
||||
(assoc ::trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false))
|
||||
(assoc ::trace (ex/format-throwable cause :data? true :explain? false :header? false :summary? false))
|
||||
(assoc ::l/cause (ex-cause cause))
|
||||
(record->report))
|
||||
|
||||
@@ -64,18 +64,18 @@
|
||||
message))
|
||||
@message)
|
||||
:trace (or (::trace record)
|
||||
(some-> cause (ex/format-throwable :data? false :explain? false :header? false :summary? false)))}
|
||||
(some-> cause (ex/format-throwable :data? true :explain? false :header? false :summary? false)))}
|
||||
|
||||
(when-let [params (or (:request/params context) (:params context))]
|
||||
{:params (pp/pprint-str params :length 30 :level 13)})
|
||||
{:params (pp/pprint-str params :length 20 :level 20)})
|
||||
|
||||
(when-let [value (:value context)]
|
||||
{:value (pp/pprint-str value :length 30 :level 12)})
|
||||
{:value (pp/pprint-str value :length 30 :level 13)})
|
||||
|
||||
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
|
||||
{:data (pp/pprint-str data :length 30 :level 12)})
|
||||
{:data (pp/pprint-str data :length 30 :level 13)})
|
||||
|
||||
(when-let [explain (ex/explain data :length 30 :level 12)]
|
||||
(when-let [explain (ex/explain data :length 30 :level 13)]
|
||||
{:explain explain})))))
|
||||
|
||||
(defn error-record?
|
||||
|
||||
@@ -134,11 +134,18 @@
|
||||
::webhooks/event? true
|
||||
::sse/stream? true
|
||||
::sm/params schema:import-binfile}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version file] :as params}]
|
||||
(projects/check-edition-permissions! pool profile-id project-id)
|
||||
(let [params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :version (or version 1)))]
|
||||
(let [version (or version 1)
|
||||
params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :version version))
|
||||
manifest (case (int version)
|
||||
1 nil
|
||||
3 (bf.v3/get-manifest (:path file)))]
|
||||
|
||||
(with-meta
|
||||
(sse/response (partial import-binfile cfg params))
|
||||
{::audit/props {:file nil}})))
|
||||
{::audit/props {:file nil
|
||||
:generated-by (:generated-by manifest)
|
||||
:referer (:referer manifest)}})))
|
||||
|
||||
@@ -559,6 +559,8 @@
|
||||
f.project_id,
|
||||
f.created_at,
|
||||
f.modified_at,
|
||||
f.data_backend,
|
||||
f.data_ref_id,
|
||||
f.name,
|
||||
f.version,
|
||||
f.is_shared,
|
||||
|
||||
@@ -47,10 +47,26 @@
|
||||
`(try ~@exprs (catch Throwable e# nil))))
|
||||
|
||||
(defmacro try!
|
||||
[& exprs]
|
||||
(if (:ns &env)
|
||||
`(try ~@exprs (catch :default e# e#))
|
||||
`(try ~@exprs (catch Throwable e# e#))))
|
||||
[expr & {:keys [reraise-with on-exception]}]
|
||||
(let [ex-sym
|
||||
(gensym "exc")
|
||||
|
||||
generate-catch
|
||||
(fn []
|
||||
(cond
|
||||
(map? reraise-with)
|
||||
`(ex/raise ~@(mapcat identity reraise-with) :cause ~ex-sym)
|
||||
|
||||
on-exception
|
||||
`(let [handler# ~on-exception]
|
||||
(handler# ~ex-sym))
|
||||
|
||||
:else
|
||||
ex-sym))]
|
||||
|
||||
(if (:ns &env)
|
||||
`(try ~expr (catch :default ~ex-sym ~(generate-catch)))
|
||||
`(try ~expr (catch Throwable ~ex-sym ~(generate-catch))))))
|
||||
|
||||
(defn ex-info?
|
||||
[v]
|
||||
|
||||
@@ -487,7 +487,9 @@
|
||||
(cts/shape? shape-new))
|
||||
(ex/raise :type :assertion
|
||||
:code :data-validation
|
||||
:hint "invalid shape found after applying changes"
|
||||
:hint (str "invalid shape found after applying changes on file "
|
||||
(:id data-new))
|
||||
:file-id (:id data-new)
|
||||
::sm/explain (cts/explain-shape shape-new))))))]
|
||||
|
||||
(->> (into #{} (map :page-id) items)
|
||||
|
||||
@@ -1319,18 +1319,35 @@
|
||||
(d/update-when :components d/update-vals update-container)
|
||||
(d/without-nils))))
|
||||
|
||||
(defmethod migrate-data "0003-convert-path-content"
|
||||
(defmethod migrate-data "0003-convert-path-content-v2"
|
||||
[data _]
|
||||
(some-> cfeat/*new* (swap! conj "fdata/path-data"))
|
||||
|
||||
(letfn [(update-object [object]
|
||||
(if (or (cfh/bool-shape? object)
|
||||
(cfh/path-shape? object))
|
||||
(update object :content path/content)
|
||||
object))
|
||||
(let [decode-segments
|
||||
(sm/decoder path/schema:segments sm/json-transformer)
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
update-object
|
||||
(fn [object]
|
||||
(if (or (cfh/bool-shape? object)
|
||||
(cfh/path-shape? object))
|
||||
(let [content (get object :content)
|
||||
content (cond
|
||||
(path/content? content)
|
||||
content
|
||||
|
||||
(nil? content)
|
||||
(path/content [])
|
||||
|
||||
:else
|
||||
(-> content
|
||||
(decode-segments)
|
||||
(path/content)))]
|
||||
(assoc object :content content))
|
||||
object))
|
||||
|
||||
update-container
|
||||
(fn [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
@@ -1558,7 +1575,7 @@
|
||||
"0002-normalize-bool-content-v2"
|
||||
"0002-clean-shape-interactions"
|
||||
"0003-fix-root-shape"
|
||||
"0003-convert-path-content"
|
||||
"0003-convert-path-content-v2"
|
||||
"0004-clean-shadow-color"
|
||||
"0005-deprecate-image-type"
|
||||
"0006-fix-old-texts-fills"
|
||||
|
||||
@@ -170,19 +170,6 @@
|
||||
item))
|
||||
root)))
|
||||
|
||||
(defn xform-nodes
|
||||
"The same as transform but instead of receiving a funcion, receives
|
||||
a transducer."
|
||||
[xf root]
|
||||
(let [rf (fn [_ v] v)]
|
||||
(walk/postwalk
|
||||
(fn [item]
|
||||
(let [rf (xf rf)]
|
||||
(if (is-node? item)
|
||||
(d/nilv (rf nil item) item)
|
||||
item)))
|
||||
root)))
|
||||
|
||||
(defn update-text-content
|
||||
[shape pred-fn update-fn attrs]
|
||||
(let [update-attrs-fn #(update-fn % attrs)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.helpers :as cpf]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
@@ -26,6 +27,9 @@
|
||||
(def ^:const bool-style-properties bool/style-properties)
|
||||
(def ^:const default-bool-fills bool/default-fills)
|
||||
|
||||
(def schema:content impl/schema:content)
|
||||
(def schema:segments impl/schema:segments)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TRANSFORMATIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -48,9 +52,9 @@
|
||||
[data]
|
||||
(impl/from-string data))
|
||||
|
||||
(defn check-path-content
|
||||
(defn check-content
|
||||
[content]
|
||||
(impl/check-content-like content))
|
||||
(impl/check-content content))
|
||||
|
||||
(defn get-byte-size
|
||||
"Get byte size of a path content"
|
||||
@@ -76,7 +80,7 @@
|
||||
(defn apply-content-modifiers
|
||||
"Apply delta modifiers over the path content"
|
||||
[content modifiers]
|
||||
(assert (impl/check-content-like content))
|
||||
(assert (impl/check-content content))
|
||||
|
||||
(letfn [(apply-to-index [content [index params]]
|
||||
(if (contains? content index)
|
||||
@@ -196,7 +200,18 @@
|
||||
contents
|
||||
(sequence extract-content-xf (:shapes shape))]
|
||||
|
||||
(bool/calculate-content (:bool-type shape) contents)))
|
||||
(ex/try!
|
||||
(bool/calculate-content (:bool-type shape) contents)
|
||||
|
||||
:on-exception
|
||||
(fn [cause]
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-path-content
|
||||
:hint (str "unable to calculate bool content for shape " (:id shape))
|
||||
:shapes (:shapes shape)
|
||||
:type (:bool-type shape)
|
||||
:content (vec contents)
|
||||
:cause cause)))))
|
||||
|
||||
(defn calc-bool-content
|
||||
"Calculate the boolean content from shape and objects. Returns a
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
|
||||
(defn calculate-content
|
||||
"Create a bool content from a collection of contents and specified
|
||||
type."
|
||||
type. Returns plain segments"
|
||||
[bool-type contents]
|
||||
;; We apply the boolean operation in to each pair and the result to the next
|
||||
;; element
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
|
||||
(defn make-move-to [to]
|
||||
{:command :move-to
|
||||
:relative false
|
||||
:params {:x (:x to)
|
||||
:y (:y to)}})
|
||||
|
||||
(defn make-line-to [to]
|
||||
{:command :line-to
|
||||
:relative false
|
||||
:params {:x (:x to)
|
||||
:y (:y to)}})
|
||||
|
||||
@@ -65,7 +63,6 @@
|
||||
(defn make-curve-to
|
||||
[to h1 h2]
|
||||
{:command :curve-to
|
||||
:relative false
|
||||
:params (make-curve-params to h1 h2)})
|
||||
|
||||
(defn prefix->coords [prefix]
|
||||
@@ -98,7 +95,7 @@
|
||||
(defn segment->point
|
||||
([segment] (segment->point segment :x))
|
||||
([segment coord]
|
||||
(when-let [params (get segment :params)]
|
||||
(when-let [params (not-empty (get segment :params))]
|
||||
(case coord
|
||||
:c1 (gpt/point (get params :c1x)
|
||||
(get params :c1y))
|
||||
|
||||
@@ -507,18 +507,6 @@
|
||||
(= (:command e1) :move-to))))}
|
||||
schema:segment])
|
||||
|
||||
(def schema:content-like
|
||||
[:sequential schema:segment])
|
||||
|
||||
(def check-content-like
|
||||
(sm/check-fn schema:content-like))
|
||||
|
||||
(def check-segment
|
||||
(sm/check-fn schema:segment))
|
||||
|
||||
(def ^:private check-segments
|
||||
(sm/check-fn schema:segments))
|
||||
|
||||
(defn path-data?
|
||||
[o]
|
||||
(instance? PathData o))
|
||||
@@ -526,37 +514,40 @@
|
||||
(declare from-string)
|
||||
(declare from-plain)
|
||||
|
||||
;; Mainly used on backend: features/components_v2.clj
|
||||
(sm/register! ::path/segment schema:segment)
|
||||
(sm/register! ::path/segments schema:segments)
|
||||
(def schema:content
|
||||
(sm/type-schema
|
||||
{:type ::path/content
|
||||
:compile
|
||||
(fn [_ _ _]
|
||||
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
|
||||
generator (->> (sg/generator schema:segments)
|
||||
(sg/filter not-empty)
|
||||
(sg/fmap from-plain))]
|
||||
{:pred path-data?
|
||||
:type-properties
|
||||
{:gen/gen generator
|
||||
:encode/json identity
|
||||
:decode/json (fn [s]
|
||||
(cond
|
||||
(string? s)
|
||||
(from-string s)
|
||||
|
||||
(sm/register!
|
||||
{:type ::path/content
|
||||
:compile
|
||||
(fn [_ _ _]
|
||||
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
|
||||
generator (->> (sg/generator schema:segments)
|
||||
(sg/filter not-empty)
|
||||
(sg/fmap from-plain))]
|
||||
{:pred path-data?
|
||||
:type-properties
|
||||
{:gen/gen generator
|
||||
:encode/json identity
|
||||
:decode/json (fn [s]
|
||||
(cond
|
||||
(string? s)
|
||||
(from-string s)
|
||||
(vector? s)
|
||||
(let [decode-fn (deref decoder)]
|
||||
(-> (decode-fn s)
|
||||
(from-plain)))
|
||||
|
||||
(vector? s)
|
||||
(let [decode-fn (deref decoder)]
|
||||
(-> (decode-fn s)
|
||||
(from-plain)))
|
||||
:else
|
||||
s))}}))}))
|
||||
|
||||
:else
|
||||
s))}}))})
|
||||
(def check-plain-content
|
||||
(sm/check-fn schema:segments))
|
||||
|
||||
(def check-path-content
|
||||
(sm/check-fn ::path/content))
|
||||
(def check-segment
|
||||
(sm/check-fn schema:segment))
|
||||
|
||||
(def check-content
|
||||
(sm/check-fn schema:content))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; CONSTRUCTORS & PREDICATES
|
||||
@@ -617,7 +608,7 @@
|
||||
(defn from-plain
|
||||
"Create a PathData instance from plain data structures"
|
||||
[segments]
|
||||
(assert (check-segments segments))
|
||||
(assert (check-plain-content segments))
|
||||
|
||||
(let [total (count segments)
|
||||
buffer (buf/allocate (* total SEGMENT-BYTE-SIZE))]
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
[:map {:title "BoolAttrs"}
|
||||
[:shapes [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]
|
||||
[:bool-type [::sm/one-of bool-types]]
|
||||
[:content ::path/content]])
|
||||
[:content path/schema:content]])
|
||||
|
||||
(def ^:private schema:rect-attrs
|
||||
[:map {:title "RectAttrs"}])
|
||||
@@ -271,7 +271,7 @@
|
||||
|
||||
(def ^:private schema:path-attrs
|
||||
[:map {:title "PathAttrs"}
|
||||
[:content ::path/content]])
|
||||
[:content path/schema:content]])
|
||||
|
||||
(def ^:private schema:text-attrs
|
||||
[:map {:title "TextAttrs"}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.transit :as trans]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.path.bool :as path.bool]
|
||||
[app.common.types.path.helpers :as path.helpers]
|
||||
[app.common.types.path.impl :as path.impl]
|
||||
[app.common.types.path.segment :as path.segment]
|
||||
@@ -414,3 +415,89 @@
|
||||
result1 (get-handlers sample-content-large)
|
||||
result2 (path.segment/get-handlers content)]
|
||||
(t/is (= result1 result2))))
|
||||
|
||||
|
||||
(def contents-for-bool
|
||||
[[{:command :move-to, :params {:x 1682.9000244140625, :y 48.0}}
|
||||
{:command :line-to, :params {:x 1682.9000244140625, :y 44.0}}
|
||||
{:command :curve-to, :params {:x 1683.9000244140625, :y 43.0, :c1x 1682.9000244140625, :c1y 43.400001525878906, :c2x 1683.300048828125, :c2y 43.0}}
|
||||
{:command :line-to, :params {:x 1687.9000244140625, :y 43.0}}
|
||||
{:command :curve-to, :params {:x 1688.9000244140625, :y 44.0, :c1x 1688.5, :c1y 43.0, :c2x 1688.9000244140625, :c2y 43.400001525878906}}
|
||||
{:command :line-to, :params {:x 1688.9000244140625, :y 48.0}}
|
||||
{:command :curve-to, :params {:x 1687.9000244140625, :y 49.0, :c1x 1688.9000244140625, :c1y 48.599998474121094, :c2x 1688.5, :c2y 49.0}}
|
||||
{:command :line-to, :params {:x 1683.9000244140625, :y 49.0}}
|
||||
{:command :curve-to, :params {:x 1682.9000244140625, :y 48.0, :c1x 1683.300048828125, :c1y 49.0, :c2x 1682.9000244140625, :c2y 48.599998474121094}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :move-to, :params {:x 1684.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1684.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1686.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1686.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1684.9000244140625, :y 45.0}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :close-path, :params {}}]
|
||||
|
||||
[{:command :move-to, :params {:x 1672.9000244140625, :y 48.0}}
|
||||
{:command :line-to, :params {:x 1672.9000244140625, :y 44.0}}
|
||||
{:command :curve-to, :params {:x 1673.9000244140625, :y 43.0, :c1x 1672.9000244140625, :c1y 43.400001525878906, :c2x 1673.300048828125, :c2y 43.0}}
|
||||
{:command :line-to, :params {:x 1677.9000244140625, :y 43.0}}
|
||||
{:command :curve-to, :params {:x 1678.9000244140625, :y 44.0, :c1x 1678.5, :c1y 43.0, :c2x 1678.9000244140625, :c2y 43.400001525878906}}
|
||||
{:command :line-to, :params {:x 1678.9000244140625, :y 48.0}}
|
||||
{:command :curve-to, :params {:x 1677.9000244140625, :y 49.0, :c1x 1678.9000244140625, :c1y 48.599998474121094, :c2x 1678.5, :c2y 49.0}}
|
||||
{:command :line-to, :params {:x 1673.9000244140625, :y 49.0}}
|
||||
{:command :curve-to, :params {:x 1672.9000244140625, :y 48.0, :c1x 1673.300048828125, :c1y 49.0, :c2x 1672.9000244140625, :c2y 48.599998474121094}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :move-to, :params {:x 1674.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1674.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1676.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1676.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1674.9000244140625, :y 45.0}}
|
||||
{:command :close-path, :params {}}
|
||||
{:command :close-path, :params {}}]])
|
||||
|
||||
(def bool-result
|
||||
[{:command :move-to, :params {:x 1682.9000244140625, :y 48.0}}
|
||||
{:command :line-to, :params {:x 1682.9000244140625, :y 44.0}}
|
||||
{:command :curve-to,
|
||||
:params
|
||||
{:x 1683.9000244140625, :y 43.0, :c1x 1682.9000244140625, :c1y 43.400001525878906, :c2x 1683.300048828125, :c2y 43.0}}
|
||||
{:command :line-to, :params {:x 1687.9000244140625, :y 43.0}}
|
||||
{:command :curve-to,
|
||||
:params {:x 1688.9000244140625, :y 44.0, :c1x 1688.5, :c1y 43.0, :c2x 1688.9000244140625, :c2y 43.400001525878906}}
|
||||
{:command :line-to, :params {:x 1688.9000244140625, :y 48.0}}
|
||||
{:command :curve-to,
|
||||
:params {:x 1687.9000244140625, :y 49.0, :c1x 1688.9000244140625, :c1y 48.599998474121094, :c2x 1688.5, :c2y 49.0}}
|
||||
{:command :line-to, :params {:x 1683.9000244140625, :y 49.0}}
|
||||
{:command :curve-to,
|
||||
:params
|
||||
{:x 1682.9000244140625, :y 48.0, :c1x 1683.300048828125, :c1y 49.0, :c2x 1682.9000244140625, :c2y 48.599998474121094}}
|
||||
{:command :move-to, :params {:x 1684.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1684.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1686.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1686.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1684.9000244140625, :y 45.0}}
|
||||
{:command :move-to, :params {:x 1672.9000244140625, :y 48.0}}
|
||||
{:command :line-to, :params {:x 1672.9000244140625, :y 44.0}}
|
||||
{:command :curve-to,
|
||||
:params
|
||||
{:x 1673.9000244140625, :y 43.0, :c1x 1672.9000244140625, :c1y 43.400001525878906, :c2x 1673.300048828125, :c2y 43.0}}
|
||||
{:command :line-to, :params {:x 1677.9000244140625, :y 43.0}}
|
||||
{:command :curve-to,
|
||||
:params {:x 1678.9000244140625, :y 44.0, :c1x 1678.5, :c1y 43.0, :c2x 1678.9000244140625, :c2y 43.400001525878906}}
|
||||
{:command :line-to, :params {:x 1678.9000244140625, :y 48.0}}
|
||||
{:command :curve-to,
|
||||
:params {:x 1677.9000244140625, :y 49.0, :c1x 1678.9000244140625, :c1y 48.599998474121094, :c2x 1678.5, :c2y 49.0}}
|
||||
{:command :line-to, :params {:x 1673.9000244140625, :y 49.0}}
|
||||
{:command :curve-to,
|
||||
:params
|
||||
{:x 1672.9000244140625, :y 48.0, :c1x 1673.300048828125, :c1y 49.0, :c2x 1672.9000244140625, :c2y 48.599998474121094}}
|
||||
{:command :move-to, :params {:x 1674.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1674.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1676.9000244140625, :y 47.0}}
|
||||
{:command :line-to, :params {:x 1676.9000244140625, :y 45.0}}
|
||||
{:command :line-to, :params {:x 1674.9000244140625, :y 45.0}}])
|
||||
|
||||
(t/deftest calculate-bool-content
|
||||
(let [result (path.bool/calculate-content :union contents-for-bool)]
|
||||
(t/is (= result bool-result))))
|
||||
|
||||
@@ -113,10 +113,10 @@
|
||||
{:num 500})))
|
||||
|
||||
(t/deftest shape-path-content-json-roundtrip
|
||||
(let [encode (sm/encoder ::path/content (sm/json-transformer))
|
||||
decode (sm/decoder ::path/content (sm/json-transformer))]
|
||||
(let [encode (sm/encoder path/schema:content (sm/json-transformer))
|
||||
decode (sm/decoder path/schema:content (sm/json-transformer))]
|
||||
(smt/check!
|
||||
(smt/for [path-content (sg/generator ::path/content)]
|
||||
(smt/for [path-content (sg/generator path/schema:content)]
|
||||
(let [path-content-1 (encode path-content)
|
||||
path-content-2 (json-roundtrip path-content-1)
|
||||
path-content-3 (decode path-content-2)]
|
||||
|
||||
@@ -404,4 +404,4 @@ COPY files/entrypoint.sh /home/entrypoint.sh
|
||||
COPY files/init.sh /home/init.sh
|
||||
|
||||
ENTRYPOINT ["/home/entrypoint.sh"]
|
||||
CMD ["/home/init.sh"]
|
||||
CMD ["/home/init.sh"]
|
||||
@@ -15,6 +15,9 @@
|
||||
(def grid-x-axis 10)
|
||||
(def grid-y-axis 10)
|
||||
|
||||
(def sidebar-default-width 318)
|
||||
(def sidebar-default-max-width 768)
|
||||
|
||||
(def page-metadata
|
||||
"Default data for page metadata."
|
||||
{:grid-x-axis grid-x-axis
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.store :as st]
|
||||
@@ -144,8 +145,13 @@
|
||||
(watch [_ state _]
|
||||
(let [user-can-edit? (dm/get-in state [:permissions :can-edit])]
|
||||
(when-let [pid (::open-plugin state)]
|
||||
(open-plugin! (preg/get-plugin pid) user-can-edit?)
|
||||
(rx/of #(dissoc % ::open-plugin)))))))
|
||||
(let [plugin (preg/get-plugin pid)]
|
||||
(open-plugin! plugin user-can-edit?)
|
||||
(rx/of (ev/event {::ev/name "start-plugin"
|
||||
::ev/origin "workspace"
|
||||
:name (:name plugin)
|
||||
:host (:host plugin)})
|
||||
#(dissoc % ::open-plugin))))))))
|
||||
|
||||
(defn- update-plugin-permissions-peek
|
||||
[{:keys [plugin-id url]}]
|
||||
|
||||
@@ -172,7 +172,9 @@
|
||||
current)]
|
||||
(case current
|
||||
"dark" "light"
|
||||
"light" "dark")))))
|
||||
"light" "dark"
|
||||
; Failsafe for missing data
|
||||
"dark")))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
|
||||
@@ -673,41 +673,35 @@
|
||||
|
||||
(defn paste-shapes
|
||||
[{in-viewport? :in-viewport :as pdata}]
|
||||
(letfn [(translate-media [mdata media-idx attr-path]
|
||||
(let [id (-> (get-in mdata attr-path)
|
||||
(:id))
|
||||
(letfn [(translate-media [mdata media-idx attr]
|
||||
(let [id (-> (get mdata attr) :id)
|
||||
mobj (get media-idx id)]
|
||||
(if mobj
|
||||
(if (empty? attr-path)
|
||||
(assoc mdata :id (:id mobj))
|
||||
(update-in mdata attr-path assoc :id (:id mobj)))
|
||||
(update mdata attr assoc :id (:id mobj))
|
||||
mdata)))
|
||||
|
||||
(add-obj? [chg]
|
||||
(= (:type chg) :add-obj))
|
||||
|
||||
(process-rchange-shape [obj media-idx]
|
||||
(let [translate-fill-image #(translate-media % media-idx :fill-image)
|
||||
translate-stroke-image #(translate-media % media-idx :stroke-image)
|
||||
translate-fills #(mapv translate-fill-image %)
|
||||
translate-strokes #(mapv translate-stroke-image %)
|
||||
process-text-node #(d/update-when % :fills translate-fills)]
|
||||
|
||||
(-> obj
|
||||
(update :fills translate-fills)
|
||||
(update :strokes translate-strokes)
|
||||
(d/update-when :content #(txt/transform-nodes process-text-node %))
|
||||
(d/update-when :position-data #(mapv process-text-node %)))))
|
||||
|
||||
;; Analyze the rchange and replace staled media and
|
||||
;; references to the new uploaded media-objects.
|
||||
(process-rchange [media-idx change]
|
||||
(let [;; Texts can have different fills for pieces of the text
|
||||
tr-fill-xf (map #(translate-media % media-idx [:fill-image]))
|
||||
tr-stroke-xf (map #(translate-media % media-idx [:stroke-image]))]
|
||||
(if (add-obj? change)
|
||||
(update change :obj (fn [obj]
|
||||
(-> obj
|
||||
(update :fills #(into [] tr-fill-xf %))
|
||||
(update :strokes #(into [] tr-stroke-xf %))
|
||||
(d/update-when :metadata translate-media media-idx [])
|
||||
(d/update-when :fill-image translate-media media-idx [])
|
||||
(d/update-when :content
|
||||
(fn [content]
|
||||
(txt/xform-nodes tr-fill-xf content)))
|
||||
(d/update-when :position-data
|
||||
(fn [position-data]
|
||||
(mapv (fn [pos-data]
|
||||
(update pos-data :fills #(into [] tr-fill-xf %)))
|
||||
position-data))))))
|
||||
change)))
|
||||
(if (add-obj? change)
|
||||
(update change :obj process-rchange-shape media-idx)
|
||||
change))
|
||||
|
||||
(calculate-paste-position [state pobjects selected position]
|
||||
(let [page-objects (dsh/lookup-page-objects state)
|
||||
@@ -909,16 +903,17 @@
|
||||
(ev/event {::ev/name "use-library-component"
|
||||
::ev/origin origin
|
||||
:is-external-library external-lib?
|
||||
:parent-shape-type parent-type})
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})
|
||||
(if (cfh/has-layout? objects (:parent-id shape))
|
||||
(ev/event {::ev/name "layout-add-element"
|
||||
::ev/origin origin
|
||||
:element-type (get shape :type)
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})
|
||||
(ev/event {::ev/name "create-shape"
|
||||
::ev/origin origin
|
||||
:shape-type (get shape :type)
|
||||
:parent-shape-type parent-type})))))))
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})))))))
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
"Generates changes to update the new content of the shape"
|
||||
[it objects page-id shape old-content new-content]
|
||||
|
||||
(assert (path/check-path-content old-content))
|
||||
(assert (path/check-path-content new-content))
|
||||
(assert (path/content? old-content))
|
||||
(assert (path/content? new-content))
|
||||
|
||||
(let [shape-id (:id shape)
|
||||
|
||||
|
||||
@@ -305,7 +305,7 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [content (some-> (dm/get-in state [:workspace-drawing :object :content])
|
||||
(path/check-path-content))]
|
||||
(path/check-content))]
|
||||
(if (> (count content) 1)
|
||||
(assoc-in state [:workspace-drawing :object :initialized?] true)
|
||||
state)))
|
||||
|
||||
@@ -493,16 +493,17 @@
|
||||
(ev/event {::ev/name "use-library-component"
|
||||
::ev/origin origin
|
||||
:is-external-library external-lib?
|
||||
:parent-shape-type parent-type})
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})
|
||||
(if (cfh/has-layout? objects (:parent-id shape))
|
||||
(ev/event {::ev/name "layout-add-element"
|
||||
::ev/origin origin
|
||||
:element-type (get shape :type)
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})
|
||||
(ev/event {::ev/name "create-shape"
|
||||
::ev/origin origin
|
||||
:shape-type (get shape :type)
|
||||
:parent-shape-type parent-type})))))))
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})))))))
|
||||
|
||||
;; Warning: This order is important for the focus mode.
|
||||
(->> (rx/of
|
||||
|
||||
@@ -129,7 +129,10 @@
|
||||
(pcb/set-undo-group (:id shape)))
|
||||
|
||||
undo-id
|
||||
(js/Symbol)]
|
||||
(js/Symbol)
|
||||
|
||||
parent-type
|
||||
(cfh/get-shape-type objects (:parent-id shape))]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
@@ -146,12 +149,13 @@
|
||||
(rx/of (ev/event {::ev/name "create-shape"
|
||||
::ev/origin "workspace:add-shape"
|
||||
:type (get shape :type)
|
||||
:parent-type (cfh/get-shape-type objects (:parent-id shape))}))
|
||||
:parent-type parent-type}))
|
||||
|
||||
(when (cfh/has-layout? objects (:parent-id shape))
|
||||
(rx/of (ev/event {::ev/name "layout-add-element"
|
||||
::ev/origin "workspace:add-shape"
|
||||
:element-type (get shape :type)})))))))))
|
||||
:type (get shape :type)
|
||||
:parent-type parent-type})))))))))
|
||||
|
||||
(defn move-shapes-into-frame
|
||||
[frame-id shapes]
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
|
||||
(defn- hide-popover
|
||||
[node]
|
||||
(dom/unset-css-property! node "block-size")
|
||||
(dom/unset-css-property! node "display")
|
||||
(.hidePopover ^js node))
|
||||
|
||||
(defn- calculate-placement-bounding-rect
|
||||
@@ -159,11 +157,9 @@
|
||||
(let [tooltip-brect (dom/get-bounding-rect tooltip)
|
||||
window-size (dom/get-window-size)]
|
||||
(when-let [[placement placement-rect] (find-matching-placement placement tooltip-brect origin-brect window-size offset)]
|
||||
|
||||
(let [height (if (or (= placement "right") (= placement "left"))
|
||||
(- (:height placement-rect) arrow-height)
|
||||
(:height placement-rect))]
|
||||
(dom/set-css-property! tooltip "display" "grid")
|
||||
(dom/set-css-property! tooltip "block-size" (dm/str height "px"))
|
||||
(dom/set-css-property! tooltip "inset-block-start" (dm/str (:top placement-rect) "px"))
|
||||
(dom/set-css-property! tooltip "inset-inline-start" (dm/str (:left placement-rect) "px")))
|
||||
@@ -212,8 +208,9 @@
|
||||
|
||||
update-position
|
||||
(fn []
|
||||
(let [placement (update-tooltip-position tooltip placement origin-brect offset)]
|
||||
(reset! placement* placement)))]
|
||||
(let [new-placement (update-tooltip-position tooltip placement origin-brect offset)]
|
||||
(when (not= new-placement placement)
|
||||
(reset! placement* new-placement))))]
|
||||
|
||||
(add-schedule schedule-ref delay update-position)))))
|
||||
|
||||
@@ -234,7 +231,7 @@
|
||||
|
||||
tooltip-class
|
||||
(stl/css-case
|
||||
:tooltip true
|
||||
:tooltip-content-wrapper true
|
||||
:tooltip-top (identical? placement "top")
|
||||
:tooltip-bottom (identical? placement "bottom")
|
||||
:tooltip-left (identical? placement "left")
|
||||
@@ -260,10 +257,11 @@
|
||||
|
||||
[:> :div props
|
||||
children
|
||||
[:div {:class [class tooltip-class]
|
||||
[:div {:class [class (stl/css :tooltip)]
|
||||
:id id
|
||||
:popover "auto"
|
||||
:role "tooltip"}
|
||||
[:div {:class (stl/css :tooltip-content)} content]
|
||||
[:div {:class (stl/css :tooltip-arrow)
|
||||
:id "tooltip-arrow"}]]]))
|
||||
[:div {:class tooltip-class}
|
||||
[:div {:class (stl/css :tooltip-content)} content]
|
||||
[:div {:class (stl/css :tooltip-arrow)
|
||||
:id "tooltip-arrow"}]]]]))
|
||||
|
||||
@@ -19,6 +19,12 @@ $arrow-side: 12px;
|
||||
block-size: fit-content;
|
||||
}
|
||||
|
||||
.tooltip-content-wrapper {
|
||||
display: grid;
|
||||
inline-size: fit-content;
|
||||
block-size: fit-content;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
background-color: var(--color-background-primary);
|
||||
border-radius: var(--sp-xs);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.constants :refer [sidebar-default-width sidebar-default-max-width]]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
@@ -210,7 +211,7 @@
|
||||
(= current-section :code)))
|
||||
|
||||
{:keys [on-pointer-down on-lost-pointer-capture on-pointer-move set-size size]}
|
||||
(use-resize-hook :code 276 276 768 :x true :right)
|
||||
(use-resize-hook :code sidebar-default-width sidebar-default-width sidebar-default-max-width :x true :right)
|
||||
|
||||
on-change-section
|
||||
(mf/use-fn
|
||||
@@ -224,7 +225,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps size)
|
||||
(fn []
|
||||
(set-size (if (> size 276) 276 768))))
|
||||
(set-size (if (> size sidebar-default-width) sidebar-default-width sidebar-default-max-width))))
|
||||
|
||||
props
|
||||
(mf/spread-props props
|
||||
@@ -235,12 +236,12 @@
|
||||
[:aside
|
||||
{:class (stl/css-case :right-settings-bar true
|
||||
:not-expand (not can-be-expanded?)
|
||||
:expanded (> size 276))
|
||||
:expanded (> size sidebar-default-width))
|
||||
|
||||
:id "right-sidebar-aside"
|
||||
:data-testid "right-sidebar"
|
||||
:data-size (str size)
|
||||
:style {"--width" (if can-be-expanded? (dm/str size "px") "276px")}}
|
||||
:style {"--width" (if can-be-expanded? (dm/str size "px") (dm/str sidebar-default-width "px"))}}
|
||||
|
||||
(when can-be-expanded?
|
||||
[:div {:class (stl/css :resize-area)
|
||||
|
||||
@@ -286,7 +286,8 @@
|
||||
(fn [mod? ids]
|
||||
(let [sorted-ids
|
||||
(into (d/ordered-set)
|
||||
(comp (remove #(dm/get-in objects [% :blocked]))
|
||||
(comp (remove (partial cfh/hidden-parent? objects))
|
||||
(remove #(dm/get-in objects [% :blocked]))
|
||||
(remove (partial cfh/svg-raw-shape? objects)))
|
||||
(ctt/sort-z-index objects ids {:bottom-frames? mod?}))]
|
||||
(mf/set-ref-val! sorted-ids-cache (assoc cached-ids [mod? ids] sorted-ids))
|
||||
@@ -355,7 +356,6 @@
|
||||
hover-shape
|
||||
(->> ids
|
||||
(remove remove-hover?)
|
||||
(remove (partial cfh/hidden-parent? objects))
|
||||
(remove #(and mod? (no-fill-nested-frames? %)))
|
||||
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
|
||||
(first)
|
||||
@@ -366,7 +366,6 @@
|
||||
(when show-measures?
|
||||
(->> ids
|
||||
(remove remove-measure?)
|
||||
(remove (partial cfh/hidden-parent? objects))
|
||||
(remove #(and mod? (no-fill-nested-frames? %)))
|
||||
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
|
||||
(first)
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 1.0.7
|
||||
|
||||
- Add the ability to provide refereron creating build context
|
||||
|
||||
```js
|
||||
const context = penpot.createBuildContext({referer:"my-referer"});
|
||||
```
|
||||
|
||||
The referer will be added as an additional field on the manifest.json
|
||||
|
||||
|
||||
## 1.0.6
|
||||
|
||||
- Fix unexpected issue on library color decoding
|
||||
|
||||
@@ -21,11 +21,10 @@
|
||||
:dev
|
||||
{:extra-paths ["dev"]
|
||||
:extra-deps
|
||||
{thheller/shadow-cljs {:mvn/version "3.1.4"}
|
||||
{thheller/shadow-cljs {:mvn/version "3.1.7"}
|
||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||
org.clojure/tools.namespace {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "RELEASE"}
|
||||
cider/cider-nrepl {:mvn/version "0.48.0"}}}
|
||||
criterium/criterium {:mvn/version "RELEASE"}}}
|
||||
|
||||
:shadow-cljs
|
||||
{:main-opts ["-m" "shadow.cljs.devtools.cli"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@penpot/library",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
|
||||
@@ -40,8 +40,7 @@
|
||||
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",
|
||||
"concurrently": "^9.1.2",
|
||||
"luxon": "^3.6.1",
|
||||
"nodemon": "^3.1.9",
|
||||
"shadow-cljs": "3.1.4"
|
||||
"nodemon": "^3.1.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map-support": "^0.5.21"
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Writable } from "stream";
|
||||
// console.log(penpot);
|
||||
|
||||
(async function () {
|
||||
const context = penpot.createBuildContext();
|
||||
const context = penpot.createBuildContext({referer:"playground"});
|
||||
|
||||
{
|
||||
context.addFile({ name: "Test File 1" });
|
||||
|
||||
@@ -271,11 +271,19 @@
|
||||
(fn []
|
||||
(json/->js @state))))
|
||||
|
||||
(def ^:private schema:context-options
|
||||
[:map {:title "ContextOptions"}
|
||||
[:referer {:optional true} ::sm/text]])
|
||||
|
||||
(def ^:private decode-context-options
|
||||
(sm/decoder schema:context-options sm/json-transformer))
|
||||
|
||||
(defn create-build-context
|
||||
"Create an empty builder state context."
|
||||
[]
|
||||
(let [state (atom {})
|
||||
api (create-builder-api state)]
|
||||
[options]
|
||||
(let [options (some-> options decode-params decode-context-options)
|
||||
state (atom {:options options})
|
||||
api (create-builder-api state)]
|
||||
|
||||
(specify! api
|
||||
cljs.core/IDeref
|
||||
|
||||
@@ -183,17 +183,22 @@
|
||||
|
||||
(defn- generate-manifest-procs
|
||||
[state]
|
||||
(let [files (->> (get state ::fb/files)
|
||||
(mapv (fn [[file-id file]]
|
||||
{:id file-id
|
||||
:name (:name file)
|
||||
:features (:features file)})))
|
||||
(let [opts (get state :options)
|
||||
files (->> (get state ::fb/files)
|
||||
(mapv (fn [[file-id file]]
|
||||
{:id file-id
|
||||
:name (:name file)
|
||||
:features (:features file)})))
|
||||
params {:type "penpot/export-files"
|
||||
:version 1
|
||||
:generated-by "penpot-library/%version%"
|
||||
:referer (get opts :referer)
|
||||
:files files
|
||||
:relations []}]
|
||||
["manifest.json" (delay (json/encode params))]))
|
||||
:relations []}
|
||||
params (d/without-nils params)]
|
||||
|
||||
["manifest.json"
|
||||
(delay (json/encode params))]))
|
||||
|
||||
(defn- generate-procs
|
||||
[state]
|
||||
|
||||
@@ -7,6 +7,10 @@ export DEVENV_PNAME="penpotdev";
|
||||
export CURRENT_USER_ID=$(id -u);
|
||||
export CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD);
|
||||
|
||||
|
||||
# Safe directory to avoid ownership errors with Git
|
||||
git config --global --add safe.directory /home/penpot/penpot || true
|
||||
|
||||
# Set default java options
|
||||
export JAVA_OPTS=${JAVA_OPTS:-"-Xmx1000m -Xms50m"};
|
||||
|
||||
@@ -356,4 +360,4 @@ case $1 in
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
Reference in New Issue
Block a user