Compare commits

...

13 Commits

Author SHA1 Message Date
Andres Gonzalez
2e3df1dc73 📚 Add disclaimer about SH updates to the tech guide 2025-10-02 16:06:27 +02:00
Andrey Antukh
b90aba0f95 Merge tag '2.10.0' 2025-10-02 12:37:58 +02:00
Andrey Antukh
d4b7f231c7 🔧 Add missing config for on commit checker 2025-09-29 12:05:09 +02:00
andrés gonzález
d1607fbe54 💄 Update Help Center images (#7266) 2025-09-29 11:54:47 +02:00
andrés gonzález
cce1dd86a2 💄 Change variants video source to peertube (#7387) 2025-09-26 10:21:41 +02:00
Eva Marco
e184a9a8b9 🐛 Fix context menu on spacing tokens (#7382) 2025-09-25 17:28:46 +02:00
andrés gonzález
58c6c94cb8 📚 Update boards info at the user guide (#7383) 2025-09-25 16:36:35 +02:00
andrés gonzález
ecee7ecfc7 📚 Update workspace info at the user guide (#7376) 2025-09-25 12:24:59 +02:00
Eva Marco
a4ada6dc8a 🐛 Add default flags for tokens (#7367) 2025-09-25 08:47:04 +02:00
andrés gonzález
b770145436 💄 Update variants video at the user guide (#7363) 2025-09-24 13:41:20 +02:00
Andrey Antukh
11b75408fe 🐛 Fix regression on importing binfile-v1 files (#7359) 2025-09-23 11:38:33 +02:00
Andrey Antukh
59f7ede4ff 🐛 Add migration for properly decode all position data on text shapes 2025-09-23 11:34:24 +02:00
Andrés Moya
8954b05d76 🐛 Fix error exporting a file with deleted tokens (#7356) 2025-09-22 17:41:31 +02:00
53 changed files with 149 additions and 86 deletions

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check Commit Type
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(Merge|Revert|:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle):)\s[A-Z].*[^.]$'
pattern: '^(Merge|Revert|:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind):)\s[A-Z].*[^.]$'
flags: 'gm'
error: 'Commit should match CONTRIBUTING.md guideline'
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request

View File

@@ -62,6 +62,8 @@
- Fix moving elements up or down while pressing alt [Taiga Issue #11992](https://tree.taiga.io/project/penpot/issue/11992)
- Fix conflicting shortcuts (remove dec/inc line height and letter spacing) [Taiga #12102](https://tree.taiga.io/project/penpot/issue/12102)
- Fix conflicting shortcuts (remove text-align shortcuts) [Taiga #12047](https://tree.taiga.io/project/penpot/issue/12047)
- Fix export file with empty tokens library [Taiga #12137](https://tree.taiga.io/project/penpot/issue/12137)
- Fix context menu on spacing tokens [Taiga #12141](https://tree.taiga.io/project/penpot/issue/12141)
## 2.9.0

View File

@@ -6,22 +6,24 @@
(ns user
(:require
[app.binfile.common :as bfc]
[app.common.data :as d]
[app.common.debug :as debug]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.fressian :as fres]
[app.common.geom.matrix :as gmt]
[app.common.json :as json]
[app.common.logging :as l]
[app.common.perf :as perf]
[app.common.pprint :as pp]
[app.common.schema :as sm]
[app.common.schema.desc-js-like :as smdj]
[app.common.schema.desc-native :as smdn]
[app.common.schema.openapi :as oapi]
[app.common.schema.generators :as sg]
[app.common.schema.openapi :as oapi]
[app.common.spec :as us]
[app.common.json :as json]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
@@ -31,7 +33,6 @@
[app.srepl.helpers :as srepl.helpers]
[app.srepl.main :as srepl]
[app.util.blob :as blob]
[app.common.time :as ct]
[clj-async-profiler.core :as prof]
[clojure.contrib.humanize :as hum]
[clojure.java.io :as io]

View File

@@ -27,7 +27,7 @@
[app.common.types.page :as ctp]
[app.common.types.plugins :as ctpg]
[app.common.types.shape :as cts]
[app.common.types.tokens-lib :as cto]
[app.common.types.tokens-lib :as ctob]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -120,7 +120,7 @@
(sm/encoder cty/schema:typography sm/json-transformer))
(def encode-tokens-lib
(sm/encoder cto/schema:tokens-lib sm/json-transformer))
(sm/encoder ctob/schema:tokens-lib sm/json-transformer))
(def encode-plugin-data
(sm/encoder ctpg/schema:plugin-data sm/json-transformer))
@@ -158,7 +158,7 @@
(sm/decoder cty/schema:typography sm/json-transformer))
(def decode-tokens-lib
(sm/decoder cto/schema:tokens-lib sm/json-transformer))
(sm/decoder ctob/schema:tokens-lib sm/json-transformer))
(def decode-plugin-data
(sm/decoder ctpg/schema:plugin-data sm/json-transformer))
@@ -196,7 +196,7 @@
(sm/check-fn cty/schema:typography))
(def validate-tokens-lib
(sm/check-fn cto/schema:tokens-lib))
(sm/check-fn ctob/schema:tokens-lib))
(def validate-plugin-data
(sm/check-fn ctpg/schema:plugin-data))
@@ -349,7 +349,8 @@
typography (encode-typography object)]
(write-entry! output path typography)))
(when tokens-lib
(when (and tokens-lib
(not (ctob/empty-lib? tokens-lib)))
(let [path (str "files/" file-id "/tokens.json")
encoded-tokens (encode-tokens-lib tokens-lib)]
(write-entry! output path encoded-tokens)))))

View File

@@ -31,6 +31,7 @@
[app.common.types.shape :as cts]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.shadow :as ctss]
[app.common.types.shape.text :as ctst]
[app.common.types.text :as types.text]
[app.common.uuid :as uuid]
[clojure.set :as set]
@@ -1585,6 +1586,25 @@
(-> data
(update :pages-index d/update-vals update-page))))
(defmethod migrate-data "0012-fix-position-data"
[data _]
(let [decode-fn
(sm/decoder ctst/schema:position-data sm/json-transformer)
update-object
(fn [object]
(if (cfh/text-shape? object)
(d/update-when object :position-data decode-fn)
object))
update-container
(fn [container]
(d/update-when container :objects d/update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(def available-migrations
(into (d/ordered-set)
["legacy-2"
@@ -1652,4 +1672,5 @@
"0009-clean-library-colors"
"0009-add-partial-text-touched-flags"
"0010-fix-swap-slots-pointing-non-existent-shapes"
"0011-fix-invalid-text-touched-flags"]))
"0011-fix-invalid-text-touched-flags"
"0012-fix-position-data"]))

View File

@@ -156,7 +156,9 @@
:enable-dashboard-templates-section
:enable-google-fonts-provider
:enable-component-thumbnails
:enable-render-wasm-dpr])
:enable-render-wasm-dpr
:enable-token-units
:enable-token-typography-types])
(defn parse
[& flags]

View File

@@ -139,6 +139,13 @@
(def spacing-keys (schema-keys schema:spacing))
(def ^:private schema:spacing-gap-padding
(-> (reduce mu/union [schema:spacing-gap
schema:spacing-padding])
(mu/update-properties assoc :title "SpacingGapPaddingTokenAttrs")))
(def spacing-gap-padding-keys (schema-keys schema:spacing-gap-padding))
(def ^:private schema:dimensions
(-> (reduce mu/union [schema:sizing
schema:spacing
@@ -320,9 +327,9 @@
(set/union generic-attributes
border-radius-keys))
(def frame-attributes
(def frame-with-layout-attributes
(set/union rect-attributes
spacing-keys))
spacing-gap-padding-keys))
(def text-attributes
(set/union generic-attributes
@@ -330,12 +337,14 @@
number-keys))
(defn shape-type->attributes
[type]
[type is-layout]
(case type
:bool generic-attributes
:circle generic-attributes
:rect rect-attributes
:frame frame-attributes
:frame (if is-layout
frame-with-layout-attributes
rect-attributes)
:image rect-attributes
:path generic-attributes
:svg-raw generic-attributes
@@ -343,14 +352,14 @@
nil))
(defn appliable-attrs
"Returns intersection of shape `attributes` for `token-type`."
[attributes token-type]
(set/intersection attributes (shape-type->attributes token-type)))
"Returns intersection of shape `attributes` for `shape-type`."
[attributes shape-type is-layout]
(set/intersection attributes (shape-type->attributes shape-type is-layout)))
(defn any-appliable-attr?
"Checks if `token-type` supports given shape `attributes`."
[attributes token-type]
(seq (appliable-attrs attributes token-type)))
[attributes token-type is-layout]
(seq (appliable-attrs attributes token-type is-layout)))
;; Token attrs that are set inside content blocks of text shapes, instead
;; at the shape level.

View File

@@ -768,7 +768,8 @@
(theme-active? [_ group name] "predicate if token theme is active")
(activate-theme [_ group name] "adds theme from the active-themes")
(deactivate-theme [_ group name] "removes theme from the active-themes")
(toggle-theme-active? [_ group name] "toggles theme in the active-themes"))
(toggle-theme-active? [_ group name] "toggles theme in the active-themes")
(get-hidden-theme [_] "get the hidden temporary theme"))
(def schema:token-themes
[:and
@@ -906,6 +907,7 @@
(defprotocol ITokensLib
"A library of tokens, sets and themes."
(empty-lib? [_] "True if the lib does not contain any token, set or theme")
(set-path-exists? [_ path] "if a set at `path` exists")
(set-group-path-exists? [_ path] "if a set group at `path` exists")
(add-token-in-set [_ set-name token] "add token to a set")
@@ -1235,7 +1237,16 @@ Will return a value that matches this schema:
(filter #(theme-active? this (:group %) (:name %))))
(tree-seq d/ordered-map? vals themes)))
(get-hidden-theme [this]
(get-theme this hidden-theme-group hidden-theme-name))
ITokensLib
(empty-lib? [this]
(and (empty? sets)
(or (empty? themes)
(and (= (theme-count this) 1)
(get-hidden-theme this)))))
(set-path-exists? [_ set-path]
(some? (get-in sets (set-full-path->set-prefixed-full-path set-path))))
@@ -1338,10 +1349,6 @@ Will return a value that matches this schema:
cljs.core/IEncodeJS
(-clj->js [this] (clj->js (datafy this)))))
(defn get-hidden-theme
[tokens-lib]
(get-theme tokens-lib hidden-theme-group hidden-theme-name))
(defn valid-tokens-lib?
[o]
(and (instance? TokensLib o)
@@ -1752,11 +1759,12 @@ Will return a value that matches this schema:
active-set-names
(get-active-themes-set-names tokens-lib)]
(-> sets
(assoc "$themes" themes)
(assoc "$metadata" {"tokenSetOrder" ordered-set-names
"activeThemes" active-themes
"activeSets" active-set-names}))))
(when-not (empty-lib? tokens-lib)
(-> sets
(assoc "$themes" themes)
(assoc "$metadata" {"tokenSetOrder" ordered-set-names
"activeThemes" active-themes
"activeSets" active-set-names})))))
(defn get-tokens-of-unknown-type
"Search for all tokens in the decoded json file that have a type that is not currently

View File

@@ -16,7 +16,7 @@
title=title or metadata.title,
desc=desc or metadata.desc or description or metadata.description,
url="https://help.penpot.app" + page.url,
img="https://help.penpot.app/img/th-help-center.jpg",
img="https://help.penpot.app/img/thumb-help-center.jpg",
img_alt=alt,
twitterHandle=twitter or metadata.twitter,
name=name

View File

@@ -7,7 +7,7 @@ eleventyNavigation:
---
<div class="main-illus">
<img src="/img/home-contributing.png" alt="User guide" border="0">
<img src="/img/home-contribution.webp" alt="User guide" border="0">
</div>
<h1 id="contributing-guide">Contributing guide.</h1>

View File

@@ -832,22 +832,22 @@ a[href].post-tag:visited {
padding-left: 14.6rem;
}
.illus-userguide {
background-image: url(/img/home-userguide.png);
background-image: url(/img/home-user-guide.webp);
}
.illus-faq {
background-image: url(/img/home-faq.png);
background-image: url(/img/home-faq.webp);
}
.illus-techguide {
background-image: url(/img/home-techguide.png);
background-image: url(/img/home-technical-guide.webp);
}
.illus-plugins {
background-image: url(/img/home-plugins.png);
background-image: url(/img/home-plugins.webp);
}
.illus-contributing {
background-image: url(/img/home-contributing.png);
background-image: url(/img/home-contribution.webp);
}
.illus-contact {
background-image: url(/img/home-contact.png);
background-image: url(/img/home-contact.webp);
}
.illus-libraries {
padding-left: 13rem;
@@ -924,7 +924,7 @@ a[href].post-tag:visited {
background-size: auto 75%;
background-position: center top;
background-repeat: no-repeat;
background-image: url(/img/home-contact.png);
background-image: url(/img/home-contact.webp);
}
.contact-block p,
.contact-block h2,
@@ -992,22 +992,22 @@ a[href].post-tag:visited {
}
.plugins .illus-getting-started {
background-image: url(/img/plugins/getting_started.png);
background-image: url(/img/plugins/plugins-getting-started.webp);
}
.plugins .illus-create-plugin {
background-image: url(/img/plugins/create_plugin.png);
background-image: url(/img/plugins/plugins-create.webp);
}
.plugins .illus-deployment {
background-image: url(/img/plugins/deployment.png);
background-image: url(/img/plugins/plugins-deploy.webp);
}
.plugins .illus-api {
background-image: url(/img/plugins/api.png);
background-image: url(/img/plugins/plugins-api.webp);
}
.plugins .illus-examples {
background-image: url(/img/plugins/examples.png);
background-image: url(/img/plugins/plugins-examples-templates.webp);
}
.plugins .illus-faq {
background-image: url(/img/plugins/faqs.png);
background-image: url(/img/plugins/plugins-faqs.webp);
}
table, tr, th, td {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

BIN
docs/img/home-contact.webp Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/img/home-faq.webp Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

BIN
docs/img/home-plugins.webp Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 127 KiB

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -13,8 +13,13 @@ href="https://community.penpot.app/t/self-hosting-penpot-i/2336" target="_blank"
about self-hosting</a> in Penpot community.
**There is absolutely no difference between <a
href="https://design.penpot.app">our SaaS offer</a> for Penpot and your
href="https://design.penpot.app" target="_blank">our SaaS offer</a> for Penpot and your
self-hosted Penpot platform!**
<p class="advice">
Docker images are published shortly after the SaaS update:
<a
href="https://community.penpot.app/t/why-do-self-hosted-versions-lag-behind-new-releases/9897" target="_blank">Why do self hosted versions lag behind new releases?</a>
</p>
There are three main options for creating a Penpot instance:
@@ -22,7 +27,7 @@ There are three main options for creating a Penpot instance:
2. Using <a href="https://docker.com" target="_blank">Docker</a> tool.
3. Using <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>.
<p class="advice">
<p>
The recommended way is to use Elestio, since it's simpler, fully automatic and still greatly flexible.
Use Docker if you already know the tool, if need full control of the process or have extra requirements
and do not want to depend on any external provider, or need to do any special customization.

View File

@@ -7,7 +7,7 @@ eleventyNavigation:
---
<div class="main-illus">
<img src="/img/home-techguide.png" alt="Technical Guide" border="0">
<img src="/img/home-technical-guide.webp" alt="Technical Guide" border="0">
</div>
# Technical Guide

View File

@@ -195,13 +195,13 @@ desc: Streamline your design workflow with Penpot's Components guide! Learn to c
<iframe
width="672px"
height="378px"
src="https://www.youtube.com/embed/3iWc-dwjA30?start=6"
src="https://peertube.kaleidos.net/videos/embed/v9Yh79hom5otcBEnqondBY"
title="Penpot Variants Demo"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
<figcaption>Penpot Variants sneak peek</figcaption>
<figcaption>Penpot Variants release</figcaption>
</figure>
<h3 id="component-understanding-variants-properties-and-values">Understanding variants: properties and values</h3>

View File

@@ -7,7 +7,7 @@ eleventyNavigation:
---
<div class="main-illus">
<img src="/img/home-userguide.png" alt="User guide" border="0">
<img src="/img/home-user-guide.webp" alt="User guide" border="0">
</div>
<h1 id="user-guide">Penpot User Guide</h1>

View File

@@ -8,15 +8,23 @@ desc: "Work with Penpot's objects: boards, shapes, text, paths, and graphics. Le
available in Penpot, and how to get the most of them.</p>
<h2 id="Boards">Boards</h2>
<p>A Board is a layer typically used as a container for a design. Boards are useful if you want to design for a specific screen or print size. Boards can contain other boards. First level boards
<p>Boards are layers that serve as your high-level containers for content organization and layout. Boards are useful if you want to design for a specific screen or print size. Boards can contain other boards. First level boards
are shown by default at the <a href="/user-guide/view-mode">View mode</a>, acting as screens of a design or pages of a document. Also, objects inside boards can be clipped. Boards are a powerful element at Penpot, opening up a ton of possibilities when creating and organizing your designs.</p>
<h3>Create boards</h3>
<p>You can create a board using the board tool at the toolbar or the shortcut <kbd>B</kbd>. </p>
<p>Set a custom size or choose one of the provided presets with the most common resolution for devices and standard print sizes. </p>
<p>To create a board, use the board tool at the toolbar or the shortcut <kbd>B</kbd>.</p>
<figure>
<img src="/img/objects/board-tool.webp" alt="Board tool">
</figure>
<p>Then, with the board tool selected, you have two options:</p>
<ul>
<li><strong>Select a board size upfront</strong>. You can choose one of the provided presets with the most common resolution for devices and standard print sizes</li>
<li><strong>Click-and-drag</strong> to draw a frame of approximate size, then immediately edit its width/height values to be precise.</li>
</ul>
<figure>
<video title="Create board" muted="" playsinline="" controls="" width="100%" poster="/img/objects/board-create.webp" height="auto">
<source src="/img/objects/board-create.mp4" type="video/mp4">
<source src="/img/objects/board-creation.mp4" type="video/mp4">
</video>
</figure>

View File

@@ -28,33 +28,37 @@ desc: Discover Penpot's free user guide! Learn the interface, workspace basics,
<strong>10)</strong> Prototype mode
<strong>11)</strong> Inspect mode
<strong>12)</strong> View mode
<strong>13)</strong> History panel
<strong>14)</strong> Comments
<strong>15)</strong> Zoom
<strong>16)</strong> File status
<strong>13)</strong> Share / Invite
<strong>14)</strong> History
<strong>15)</strong> Comments
<strong>16)</strong> Zoom
<strong>17)</strong> Users
<strong>18)</strong> Assets panel
<strong>18)</strong> Assets
<strong>19)</strong> Design tokens
<strong>20)</strong> File status
</p>
<ol>
<li><strong>Viewport:</strong> An infinite canvas to design without limits.</li>
<li><strong>Toolbar: </strong> The toolbar is the place where you will find the tools to quickly and easily create the different types of objects: board, rectangle, ellipse, text, graphic, path and free drawing. <a href="/user-guide/objects/">More about objects.</a></li>
<li><strong>Main menu:</strong> At the file main menu you are able to tweak your workspace configuration. Manage visibility for grids, rulers, panels. Enable or disable snapping, dynamic alignment. Add or remove the file as Shared Library. You can also find here help resources.</li>
<li><strong>Pages:</strong> A file can have as many pages as you need. Each page has its own viewport (the almost infinite area where you actually create your designs) with its own layers. You can create, delete or reorder your pages at your needs.</li>
<li><strong>Layers:</strong> Layers are the different objects that you can place at the design viewport. <a href="/user-guide/layer-basics/">More about layers.</a></li>
<li><strong>Rulers:</strong> Coordinates to help you design. You can also drag guides from the rulers.</li>
<li><strong>Color palette:</strong> The color palette allows you to always have a color library at plain sight. Use the menu to easily switch between libraries. <a href="/user-guide/styling/#color-palette">More about the color palette</a>.</li>
<li><strong>Typography palette:</strong> The Typography palette allows you to always have the typography styles at plain sight. Use the menu to easily switch between libraries.</li>
<li><strong>Design properties:</strong> At the Design properties sidebar you can find and edit the properties of a selected layer. The list of properties shown varies depending on the type of element. There are some common properties always set (like size and position) and others not set but available to be added (stroke, shadow, blur...). <a href="/user-guide/styling/">More about styling.</a></li>
<li><strong>Prototype mode:</strong> Penpot allows you to prototype interactions connecting boards. Once selected the prototype mode at the right sidebar, select an element to drag a connection to another board. Youll be able to see the interactive prototype in action in the view mode (access by clicking the play button at the top right). <a href="/user-guide/prototyping/">More about prototyping.</a></li>
<li><strong>Inspect mode:</strong> Penpot allows you to get measures, properties and production-ready code from your designs. Also, inspect mode provides a safer view-only mode and other improvements. <a href="/user-guide/inspect/">More about inspecting designs.</a></li>
<li><strong>View mode:</strong> The view mode button launches a presentation with the boards. See more about what you can do at the <a href="/user-guide/view-mode/#viewmode-features">view mode.</a> </li>
<li><strong>History panel:</strong> The history panel keeps track of the latest changes on an opened file. You can undo/redo to walk the changes and expand each one of them to get detailed information. <a href="/user-guide/workspace-basics/#history">More about history panel.</a></li>
<li><strong>Comments:</strong> Activate comments mode to see all the comments at the file and add new ones.</li>
<li><strong>Zoom:</strong> Zooming options.</li>
<li><strong>File status:</strong> Information about the file saving state. Know if last changes are saved or if there is a problem.</li>
<li><strong>Users:</strong> Here you can see how many users have the file open at any moment.</li>
<li><strong>Assets panel:</strong> Each file has a default library (File Library) where you can store elements and styles that are likely to be reused within a project. That includes components, colors and typographies. To add an asset to a library just click the “+” button at the header of each asset group.</li>
<li><strong>Viewport:</strong> An infinite canvas where you can design without limits.</li>
<li><strong>Toolbar:</strong> This is where youll find all the tools to quickly and easily create different types of objects: board, rectangle, ellipse, text, graphic, path, and free drawing. <a href="/user-guide/objects/">Learn more about objects.</a></li>
<li><strong>Main menu:</strong> From the main menu, you can customize your workspace. Manage the visibility of grids, rulers, and panels. Enable or disable snapping and dynamic alignment. Add or remove the file as a Shared Library. Youll also find help resources here.</li>
<li><strong>Pages:</strong> A file can contain as many pages as you need. Each page has its own viewport (the almost infinite area where you design) and its own layers. You can create, delete, or reorder pages as needed.</li>
<li><strong>Layers:</strong> Layers are the different objects you can place in the design viewport. <a href="/user-guide/layer-basics/">Learn more about layers.</a></li>
<li><strong>Rulers:</strong> Rulers provide coordinates to help you design. You can also drag guides from them.</li>
<li><strong>Color palette:</strong> The color palette gives you quick access to a visible library of colors. Use the menu to easily switch between libraries. <a href="/user-guide/styling/#color-palette">Learn more about the color palette.</a></li>
<li><strong>Typography palette:</strong> The typography palette keeps text styles always at hand. Use the menu to switch between libraries.</li>
<li><strong>Design properties:</strong> The Design Properties sidebar lets you view and edit the attributes of a selected layer. The list changes depending on the element type: some properties are always present (size, position), while others are optional (stroke, shadow, blur). <a href="/user-guide/styling/">Learn more about styling.</a></li>
<li><strong>Prototype mode:</strong> Penpot lets you prototype interactions by connecting boards. Activate prototype mode from the right sidebar, then select an element and drag a connection to another board. You can preview the interactive prototype in View Mode (access it by clicking the play button in the top-right corner). <a href="/user-guide/prototyping/">Learn more about prototyping.</a></li>
<li><strong>Inspect mode:</strong> Use Inspect Mode to get measurements, properties, and production-ready code from your designs. It also provides a safer, view-only mode with additional improvements. <a href="/user-guide/inspect/">Learn more about inspecting designs.</a></li>
<li><strong>View mode:</strong> The View Mode button launches a presentation of your boards. Explore more features in the <a href="/user-guide/view-mode/#viewmode-features">View Mode guide.</a></li>
<li><strong>Share / Invite:</strong> Invite team members to give them access to this file and all team files.</li>
<li><strong>History:</strong> The History panel records recent changes in an open file. Use undo/redo to step through changes, and expand each entry for detailed information. <a href="/user-guide/workspace-basics/#history">Learn more about the history panel.</a></li>
<li><strong>Comments:</strong> Switch to Comments Mode to view all comments on the file and add new ones.</li>
<li><strong>Zoom:</strong> Options to zoom in, zoom out, or fit the canvas.</li>
<li><strong>Users:</strong> See how many users currently have the file open.</li>
<li><strong>Assets:</strong> Each file includes a default File Library where you can store reusable elements and styles, such as components, colors, and text styles. From this panel, you'll be also able to include and use shared libraries.</li>
<li><strong>Design tokens:</strong> Design tokens are the building blocks of UI elements, expressed in a format usable across design, tools, and code. They include colors, typography, spacing, shadows, and more.</li>
<li><strong>File status:</strong> Displays the files saving state, showing whether your latest changes are saved or if theres a problem.</li>
</ol>
<h2 id="interface-viewmode">View mode</h2>

View File

@@ -487,7 +487,7 @@
(or
(and (ctsl/any-layout-immediate-child? objects shape)
(some ctt/spacing-margin-keys attributes))
(ctt/any-appliable-attr? attributes (:type shape))))))
(ctt/any-appliable-attr? attributes (:type shape) (:layout shape))))))
shape-ids (d/nilv (keys shapes) [])
any-variant? (->> shapes vals (some ctk/is-variant?) boolean)

View File

@@ -341,7 +341,7 @@
(:id token)))}]))
(defn- allowed-shape-attributes [shapes]
(reduce into #{} (map #(ctt/shape-type->attributes (:type %)) shapes)))
(reduce into #{} (map #(ctt/shape-type->attributes (:type %) (:layout %)) shapes)))
(defn menu-actions [{:keys [type token selected-shapes] :as context-data}]
(let [context-data (assoc context-data :allowed-shape-attributes (allowed-shape-attributes selected-shapes))
@@ -445,7 +445,8 @@
(if (some? type)
(submenu-actions-selection-actions context-data)
(selection-actions context-data))
(default-actions context-data))]
(default-actions context-data))
entries (clean-separators entries)]
(for [[index {:keys [title action selected? hint submenu no-selectable] :as entry}] (d/enumerate entries)]
[:* {:key (dm/str title " " index)}
(cond

View File

@@ -191,7 +191,7 @@
;; Edge-case for allowing margin attribute on shapes inside layout parent
(and selected-inside-layout? (set/subset? ctt/spacing-margin-keys attrs))
(some (fn [shape]
(ctt/any-appliable-attr? attrs (:type shape)))
(ctt/any-appliable-attr? attrs (:type shape) (:layout shape)))
selected-shapes)))
(def token-types-with-status-icon

View File

@@ -148,6 +148,7 @@
(->> (rp/cmd! ::sse/import-binfile
{:name (str/replace (:name data) #".penpot$" "")
:file file
:version 1
:project-id project-id})
(rx/tap (fn [event]
(let [payload (sse/get-payload event)

View File

@@ -296,10 +296,10 @@
frame-1' (cths/get-shape file' :frame-1)
frame-2' (cths/get-shape file' :frame-2)]
(t/testing "shape `:applied-tokens` got updated"
(t/is (= (:p1 (:applied-tokens frame-1')) (:name token-target')))
(t/is (= (:p2 (:applied-tokens frame-1')) (:name token-target')))
(t/is (= (:p3 (:applied-tokens frame-1')) (:name token-target')))
(t/is (= (:p4 (:applied-tokens frame-1')) (:name token-target')))
(t/is (= (:p1 (:applied-tokens frame-1')) nil))
(t/is (= (:p2 (:applied-tokens frame-1')) nil))
(t/is (= (:p3 (:applied-tokens frame-1')) nil))
(t/is (= (:p4 (:applied-tokens frame-1')) nil))
(t/is (= (:p1 (:applied-tokens frame-2')) (:name token-target')))
(t/is (= (:p2 (:applied-tokens frame-2')) (:name token-target')))