Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh
2025-06-25 14:24:26 +02:00
17 changed files with 665 additions and 548 deletions

View File

@@ -77,6 +77,7 @@ on-premises instances** that want to keep up to date.
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337}(https://tree.taiga.io/project/penpot/issue/11337)
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
## 2.7.2

View File

@@ -37,7 +37,6 @@
{:mvn/version "1.3.1002"}
metosin/reitit-core {:mvn/version "0.9.1"}
nrepl/nrepl {:mvn/version "1.3.1"}
cider/cider-nrepl {:mvn/version "0.56.0"}
org.postgresql/postgresql {:mvn/version "42.7.6"}
org.xerial/sqlite-jdbc {:mvn/version "3.49.1.0"}

View File

@@ -1,4 +1,7 @@
[{:id "wireframing-kit"
[{:id "tokens-starter-kit"
:name "Design tokens starter kit"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"},
{:id "wireframing-kit"
:name "Wireframe library"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}
{:id "prototype-examples"

View File

@@ -40,7 +40,6 @@
[app.svgo :as-alias svgo]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[cider.nrepl :refer [cider-nrepl-handler]]
[clojure.test :as test]
[clojure.tools.namespace.repl :as repl]
[cuerdas.core :as str]
@@ -605,7 +604,7 @@
(let [p (promise)]
(when (contains? cf/flags :nrepl-server)
(l/inf :hint "start nrepl server" :port 6064)
(nrepl/start-server :bind "0.0.0.0" :port 6064 :handler cider-nrepl-handler))
(nrepl/start-server :bind "0.0.0.0" :port 6064))
(start)
(deref p))

View File

@@ -595,7 +595,11 @@
(teams/check-read-permissions! conn profile-id team-id)
(->> (db/exec! conn [sql:team-shared-files team-id])
(into #{} (comp
(map decode-row)
;; NOTE: this decode operation is a workaround for a
;; fast fix, this should be approached with a more
;; efficient implementation, for now it loads all
;; the files in memory.
(map (partial bfc/decode-file cfg))
(map (fn [row]
(if-let [media-id (:media-id row)]
(-> row

View File

@@ -95,22 +95,33 @@
(defn migrate-file
[file libs]
(binding [cfeat/*new* (atom #{})]
(let [version (or (:version file)
(-> file :data :version))]
(-> file
(assoc :version cfd/version)
(update :migrations
(fn [migrations]
(if (nil? migrations)
(generate-migrations-from-version version)
migrations)))
;; NOTE: in some future we can consider to apply
;; a migration to the whole database and remove
;; this code from this function that executes on
;; each file migration operation
(update :features cfeat/migrate-legacy-features)
(migrate libs)
(update :features (fnil into #{}) (deref cfeat/*new*))))))
(let [version
(or (:version file) (-> file :data :version))
migrations
(not-empty (get file :migrations))
file
(-> file
(assoc :version cfd/version)
(assoc :migrations
(if migrations
migrations
(generate-migrations-from-version version)))
;; NOTE: in some future we can consider to apply a
;; migration to the whole database and remove this code
;; from this function that executes on each file
;; migration operation
(update :features cfeat/migrate-legacy-features)
(migrate libs)
(update :features (fnil into #{}) (deref cfeat/*new*)))]
;; NOTE: When we have no previous migrations, we report all
;; migrations as migrated in order to correctly persist them all
;; and not only the really applied migrations
(if (not migrations)
(vary-meta file assoc ::migrated (:migrations file))
file))))
(defn migrated?
[file]
@@ -1274,7 +1285,8 @@
;; rollback, we still need to perform an other migration
;; for properly delete the bool-content prop from shapes
;; once the know the migration was OK
(if (cfh/bool-shape? object)
(if (and (cfh/bool-shape? object)
(not (contains? object :content)))
(if-let [content (:bool-content object)]
(assoc object :content content)
object)
@@ -1469,14 +1481,22 @@
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(defmethod migrate-data "0008-fix-library-colors-opacity"
(defmethod migrate-data "0008-fix-library-colors-v2"
[data _]
(letfn [(update-color [color]
(letfn [(clear-color-opacity [color]
(if (and (contains? color :opacity)
(nil? (get color :opacity)))
(assoc color :opacity 1)
color))]
(d/update-when data :colors d/update-vals update-color)))
color))
(clear-color [color]
(-> color
(select-keys types.color/library-color-attrs)
(d/without-nils)
(d/without-qualified)
(clear-color-opacity)))]
(d/update-when data :colors d/update-vals clear-color)))
(defmethod migrate-data "0009-add-partial-text-touched-flags"
[data _]
@@ -1566,5 +1586,5 @@
"0005-deprecate-image-type"
"0006-fix-old-texts-fills"
"0007-clear-invalid-strokes-and-fills-v2"
"0008-fix-library-colors-opacity"
"0008-fix-library-colors-v2"
"0009-add-partial-text-touched-flags"]))

View File

@@ -150,6 +150,9 @@
(sm/optional-keys schema:image-color)]
[:fn has-valid-color-attrs?]])
(def library-color-attrs
(sm/keys schema:library-color-attrs))
(def valid-color?
(sm/lazy-validator schema:color))

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -367,12 +367,97 @@ title: 10· Design Tokens
<h2 id="design-tokens-import-export">Importing and Exporting Tokens</h2>
<p>You can export Tokens from Penpot and import them from your computer to a Penpot file. Tokens can be imported from the <strong>Tools</strong> option at the bottom of the <strong>Tokens</strong> tab.</p>
<p>The <strong>Import</strong> functionality allows you to upload and replace the global token set using a single file, while the <strong>Export</strong> functionality lets you download the current global token set using a single file to your system.</p>
<p>The <strong>Import</strong> functionality allows you to upload and replace the global token set using a single file or a folder with multiple files in it.</p>
<p>These features support JSON files formatted according to specific guidelines and preserve the ability to undo changes if needed.</p>
<figure>
<img src="/img/design-tokens/21-tokens-import-export.webp" alt="Tokens import export" />
</figure>
<ol>
<li><strong>Import:</strong> At the <strong>Tools</strong> option, select <strong>Import</strong>, then select your <code class="language-js">tokens.json</code> file. </li>
<li><strong>Export:</strong> At the <strong>Tools</strong> option, select <strong>Export</strong>. This will export all the tokens, including token sets and themes.</li>
<li><strong>Import:</strong> Click <strong>Tools</strong>, then select <strong>Import</strong> to view import options. </li>
<li><strong>Export:</strong> Click <strong>Tools</strong>, then select <strong>Export</strong> to view export options.</li>
</ol>
<h3 id="design-tokens-import-options">Import Options</h3>
<h4>Single file</h4>
<p>You can import a JSON file comprising all tokens, token sets and token themes.</p>
<p>When importing a single file, the first-level keys of the json file will be interpreted as the set name.</p>
<pre class="language-json">
<code class="language-json">
{
"Global": {
// first-level key will be interpreted as set name
"color": {
"300": {
"$value": "red",
"$type": "color",
"$description": "my token description"
}
}
},
"Brands/A": {
// first-level key will be interpreted as set name
"color": {
"accent": {
"$value": "{red}",
"$type": "color",
"$description": "my token description"
}
}
},
"Brands/B": {
// first-level key will be interpreted as set name
"color": {
"accent": {
"$value": "#fabada",
"$type": "color",
"$description": "my token description"
}
}
}
}
</code>
</pre>
<h4>Multifile</h4>
<p>Imports a folder containing multiple JSON files (one per Token Set) and additional files like <code class="language-json">$themes.json</code> or <code class="language-json">$metadata.json</code> configurations. When importing multiple files, the name and path of the individual json files inside the folder will be interpreted as set names. These files should only contain tokens.</p>
<p>Multifile folder structure example:</p>
<code>
<pre>
folder/
├── global/
│ ├── colors.json // can only contain tokens
│ └── dimension.json // can only contain tokens
├── mode/
│ ├── dark.json // can only contain tokens
│ └── light.json // can only contain tokens
├── $themes.json // themes config
└── $metadata.json // other metadata config
</code>
</pre>
<figcaption>The main folder name wont be used to build token set names, so in this example, <strong>folder</strong> will be ignored in the set names.</figcaption>
<h3 id="design-tokens-export-options">Export Options</h3>
<p>Just like with importing, you can export tokens, themes and sets either in a single JSON file or in multiple files. There is no difference in the content being exported; the choice depends on your team's preferences for file organization: a single file with all the tokens, sets and themes, or a folder structure with separated JSON files organized by sets.</p>
<p>In both cases you can preview the result of the export options:</p>
<figure>
<img src="/img/design-tokens/22-tokens-export-multiple.webp" alt="Tokens export with multiple files" />
<figcaption>Exporting tokens as multiple files.</figcaption>
</figure>
<figure>
<img src="/img/design-tokens/23-tokens-export-single.webp" alt="Tokens export with single file" />
<figcaption>Exporting tokens as a single file.</figcaption>
</figure>

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -679,14 +679,8 @@
mobj (get media-idx id)]
(if mobj
(if (empty? attr-path)
(-> mdata
(assoc :id (:id mobj))
(assoc :path (:path mobj)))
(update-in mdata attr-path (fn [value]
(-> value
(assoc :id (:id mobj))
(assoc :path (:path mobj))))))
(assoc mdata :id (:id mobj))
(update-in mdata attr-path assoc :id (:id mobj)))
mdata)))
(add-obj? [chg]

View File

@@ -148,13 +148,14 @@
"Given the current layout flags, and updates them with the data
stored in Storage."
[layout]
(reduce (fn [layout [flag key]]
(condp = (get storage/user key ::none)
::none layout
false (disj layout flag)
true (conj layout flag)))
layout
layout-flags-persistence-mapping))
(let [layout (set (or layout #{}))]
(reduce-kv (fn [layout flag key]
(condp = (get storage/user key ::none)
::none layout
false (disj layout flag)
true (conj layout flag)))
layout
layout-flags-persistence-mapping)))
(defn persist-layout-flags!
"Given a set of layout flags, and persist a subset of them to the Storage."

View File

@@ -304,11 +304,8 @@
(ptk/reify ::handle-drawing-end
ptk/UpdateEvent
(update [_ state]
(let [content (dm/get-in state [:workspace-drawing :object :content])]
(assert (path/check-path-content content)
"expected valid path content instance")
(let [content (some-> (dm/get-in state [:workspace-drawing :object :content])
(path/check-path-content))]
(if (> (count content) 1)
(assoc-in state [:workspace-drawing :object :initialized?] true)
state)))

View File

@@ -7,7 +7,6 @@
(ns app.main.data.workspace.path.tools
(:require
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.types.path :as path]
[app.common.types.path.segment :as path.segment]
[app.main.data.changes :as dch]
@@ -48,12 +47,10 @@
(changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
(rx/concat
(if (cfh/path-shape? shape)
(rx/empty)
(rx/of (dwsh/update-shapes [id] path/convert-to-path)))
(rx/of (dch/commit-changes changes)
(when (empty? new-content)
(dwe/clear-edition-mode)))))))))))
(rx/of (dwsh/update-shapes [id] path/convert-to-path)
(dch/commit-changes changes))
(when (empty? new-content)
(rx/of (dwe/clear-edition-mode)))))))))))
(defn make-corner
([]

View File

@@ -91,7 +91,8 @@
on-change
(fn [index]
(fn [color]
(st/emit! (dc/change-fill ids color index))))
(let [color (select-keys color ctc/color-attrs)]
(st/emit! (dc/change-fill ids color index)))))
on-reorder
(fn [new-index]