mirror of
https://github.com/penpot/penpot.git
synced 2026-01-18 19:30:27 -05:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3007aa19a2 | ||
|
|
e20adda766 | ||
|
|
3d9fda7a21 | ||
|
|
7a5dea5cfe | ||
|
|
b47df2c230 | ||
|
|
b8b3cc641a | ||
|
|
09ff7372da | ||
|
|
f45fa95935 | ||
|
|
ce02cbc3f1 | ||
|
|
b386403fa8 | ||
|
|
0a6e884584 | ||
|
|
06f6a49bce | ||
|
|
afd309c62b | ||
|
|
214a89e20d | ||
|
|
e64cf9f283 | ||
|
|
3a34c51e43 | ||
|
|
0ff9c44246 | ||
|
|
5bfab454f5 | ||
|
|
5ebde405ea | ||
|
|
531b002a5c | ||
|
|
3eae3178a2 | ||
|
|
2cf3e37b7a | ||
|
|
e0b9751b16 | ||
|
|
ccea9b1564 | ||
|
|
5fcf889d3c | ||
|
|
7247db14b2 | ||
|
|
658e5dce22 | ||
|
|
f27cbfa0ec | ||
|
|
5754c393b9 | ||
|
|
c618efc29e | ||
|
|
3685f7b32b | ||
|
|
06b5304926 | ||
|
|
91efcd17a2 |
34
CHANGES.md
34
CHANGES.md
@@ -1,6 +1,38 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.5.0 (Unreleased)
|
||||
## 2.5.2
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
- When the workspace is empty, set default the board creation tool [Taiga #9425](https://tree.taiga.io/project/penpot/us/9425)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix scroll on storybook docs [taiga #9962](https://tree.taiga.io/project/penpot/issue/9962)
|
||||
- Navigate tracking event firing multiple times [Taiga #10415](https://tree.taiga.io/project/penpot/issue/10415)
|
||||
- Fix problem with selection colors [Taiga #10376](https://tree.taiga.io/project/penpot/issue/10376)
|
||||
- Fix scroll on storybook icons list [taiga #9962](https://tree.taiga.io/project/penpot/issue/9962)
|
||||
|
||||
## 2.5.1
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- Improve Nginx entryponit to get the resolvers dinamically by default
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
(let [data (::file-data (meta changes))]
|
||||
(dm/get-in data [:pages-index uuid/zero :objects])))
|
||||
|
||||
(defn- apply-changes-local
|
||||
(defn apply-changes-local
|
||||
[changes]
|
||||
(dm/assert!
|
||||
"expected valid changes"
|
||||
|
||||
@@ -747,42 +747,35 @@
|
||||
(let [omit-touched? (not reset?)
|
||||
clear-remote-synced? (and initial-root? reset?)
|
||||
set-remote-synced? (and (not initial-root?) reset?)
|
||||
changes (cond-> changes
|
||||
:always
|
||||
(update-attrs shape-inst
|
||||
shape-main
|
||||
root-inst
|
||||
root-main
|
||||
container
|
||||
omit-touched?)
|
||||
changes
|
||||
(cond-> changes
|
||||
:always
|
||||
(update-attrs shape-inst
|
||||
shape-main
|
||||
root-inst
|
||||
root-main
|
||||
container
|
||||
omit-touched?)
|
||||
|
||||
(ctl/flex-layout? shape-main)
|
||||
(update-flex-child-copy-attrs shape-main
|
||||
shape-inst
|
||||
library
|
||||
component
|
||||
container
|
||||
omit-touched?)
|
||||
(ctl/flex-layout? shape-main)
|
||||
(update-flex-child-copy-attrs shape-main
|
||||
shape-inst
|
||||
library
|
||||
component
|
||||
container
|
||||
omit-touched?)
|
||||
|
||||
(ctl/grid-layout? shape-main)
|
||||
(update-grid-copy-attrs shape-main
|
||||
shape-inst
|
||||
library
|
||||
component
|
||||
container
|
||||
omit-touched?)
|
||||
reset?
|
||||
(change-touched shape-inst
|
||||
shape-main
|
||||
container
|
||||
{:reset-touched? true})
|
||||
|
||||
reset?
|
||||
(change-touched shape-inst
|
||||
shape-main
|
||||
container
|
||||
{:reset-touched? true})
|
||||
clear-remote-synced?
|
||||
(change-remote-synced shape-inst container nil)
|
||||
|
||||
clear-remote-synced?
|
||||
(change-remote-synced shape-inst container nil)
|
||||
|
||||
set-remote-synced?
|
||||
(change-remote-synced shape-inst container true))
|
||||
set-remote-synced?
|
||||
(change-remote-synced shape-inst container true))
|
||||
|
||||
component-container (find-main-container container shape-inst shape-main library component)
|
||||
|
||||
@@ -859,23 +852,36 @@
|
||||
(d/index-of children-inst child-inst)
|
||||
(d/index-of children-main child-main)
|
||||
container
|
||||
omit-touched?))]
|
||||
omit-touched?))
|
||||
|
||||
(compare-children changes
|
||||
children-inst
|
||||
children-main
|
||||
container
|
||||
component-container
|
||||
file
|
||||
libraries
|
||||
only-inst
|
||||
only-main
|
||||
both
|
||||
swapped
|
||||
moved
|
||||
false
|
||||
reset?
|
||||
components-v2))))
|
||||
changes
|
||||
(compare-children changes
|
||||
children-inst
|
||||
children-main
|
||||
container
|
||||
component-container
|
||||
file
|
||||
libraries
|
||||
only-inst
|
||||
only-main
|
||||
both
|
||||
swapped
|
||||
moved
|
||||
false
|
||||
reset?
|
||||
components-v2)
|
||||
|
||||
changes
|
||||
(cond-> changes
|
||||
(ctl/grid-layout? shape-inst)
|
||||
(update-grid-copy-attrs
|
||||
(:id shape-inst)
|
||||
shape-main
|
||||
library
|
||||
component
|
||||
omit-touched?))]
|
||||
|
||||
changes)))
|
||||
|
||||
(defn generate-rename-component
|
||||
"Generate the changes for rename the component with the given id, in the current file library."
|
||||
@@ -1710,30 +1716,36 @@
|
||||
|
||||
(defn- update-grid-copy-attrs
|
||||
"Synchronizes the `layout-grid-cells` property from the main shape to the copies"
|
||||
[changes shape-main shape-copy main-container main-component copy-container omit-touched?]
|
||||
(let [ids-map
|
||||
(into {}
|
||||
(comp
|
||||
(map #(dm/get-in copy-container [:objects %]))
|
||||
(keep
|
||||
(fn [copy-shape]
|
||||
(let [main-shape (ctf/get-ref-shape main-container main-component copy-shape)]
|
||||
[(:id main-shape) (:id copy-shape)]))))
|
||||
(:shapes shape-copy))
|
||||
[changes shape-copy-id shape-main main-container main-component omit-touched?]
|
||||
(-> changes
|
||||
(pcb/apply-changes-local)
|
||||
(pcb/update-shapes
|
||||
[shape-copy-id]
|
||||
(fn [shape-copy objects]
|
||||
(let [ids-map
|
||||
(into {}
|
||||
(comp
|
||||
(map #(get objects %))
|
||||
(keep
|
||||
(fn [copy-shape]
|
||||
(let [main-shape (ctf/get-ref-shape main-container main-component copy-shape)]
|
||||
[(:id main-shape) (:id copy-shape)]))))
|
||||
(:shapes shape-copy))
|
||||
|
||||
new-changes
|
||||
(-> (pcb/empty-changes)
|
||||
(pcb/with-container copy-container)
|
||||
(pcb/with-objects (:objects copy-container))
|
||||
(pcb/update-shapes
|
||||
[(:id shape-copy)]
|
||||
(fn [shape-copy]
|
||||
remove-orphan-cells
|
||||
(fn [cells {:keys [shapes]}]
|
||||
(let [child? (set shapes)]
|
||||
(-> cells
|
||||
(update-vals
|
||||
(fn [cell]
|
||||
(update cell :shapes #(filterv child? %)))))))
|
||||
;; Take cells from main and remap the shapes to assign it to the copy
|
||||
(let [copy-cells (:layout-grid-cells shape-copy)
|
||||
main-cells (-> (ctl/remap-grid-cells shape-main ids-map) :layout-grid-cells)]
|
||||
(assoc shape-copy :layout-grid-cells (ctl/merge-cells copy-cells main-cells omit-touched?))))
|
||||
{:ignore-touched true}))]
|
||||
(pcb/concat-changes changes new-changes)))
|
||||
copy-cells (-> shape-copy :layout-grid-cells (remove-orphan-cells shape-copy))
|
||||
main-cells (-> shape-main (ctl/remap-grid-cells ids-map) :layout-grid-cells)]
|
||||
(-> shape-copy
|
||||
(assoc :layout-grid-cells
|
||||
(ctl/merge-cells copy-cells main-cells omit-touched?)))))
|
||||
{:ignore-touched true :with-objects? true})))
|
||||
|
||||
(defn- update-grid-main-attrs
|
||||
"Synchronizes the `layout-grid-cells` property from the copy to the main shape"
|
||||
|
||||
@@ -1642,11 +1642,16 @@
|
||||
"Given target cells update with source cells while trying to keep target as
|
||||
untouched as possible"
|
||||
[target-cells source-cells omit-touched?]
|
||||
(if (not omit-touched?)
|
||||
source-cells
|
||||
|
||||
(if omit-touched?
|
||||
(letfn [(get-data [cells id]
|
||||
(dissoc (get cells id) :shapes :row :column :row-span :column-span))]
|
||||
(dissoc (get cells id) :row :column :row-span :column-span))
|
||||
|
||||
(merge-cells [source-cell target-cell]
|
||||
(-> source-cell
|
||||
(d/patch-object
|
||||
(dissoc target-cell :shapes :row :column :row-span :column-span))
|
||||
(cond-> (d/not-empty? (:shapes target-cell))
|
||||
(assoc :shapes (:shapes target-cell)))))]
|
||||
(let [deleted-cells
|
||||
(into #{}
|
||||
(filter #(not (contains? source-cells %)))
|
||||
@@ -1664,5 +1669,6 @@
|
||||
(reduce
|
||||
(fn [cells id]
|
||||
(-> cells
|
||||
(d/update-when id d/patch-object (get-data target-cells id))))
|
||||
source-cells))))))
|
||||
(d/update-when id merge-cells (get target-cells id))))
|
||||
source-cells))))
|
||||
source-cells))
|
||||
|
||||
@@ -11,6 +11,7 @@ RUN set -ex; \
|
||||
ADD ./bundle-frontend/ /var/www/app/
|
||||
ADD ./files/config.js /var/www/app/js/config.js
|
||||
ADD ./files/nginx.conf /etc/nginx/nginx.conf.template
|
||||
ADD ./files/resolvers.conf /etc/nginx/overrides.d/resolvers.conf.template
|
||||
ADD ./files/nginx-mime.types /etc/nginx/mime.types
|
||||
ADD ./files/nginx-entrypoint.sh /entrypoint.sh
|
||||
|
||||
|
||||
@@ -21,10 +21,14 @@ update_flags /var/www/app/js/config.js
|
||||
|
||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060};
|
||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061};
|
||||
export PENPOT_INTERNAL_RESOLVER=${PENPOT_INTERNAL_RESOLVER:-127.0.0.11};
|
||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)";
|
||||
export PENPOT_INTERNAL_RESOLVER=${PENPOT_INTERNAL_RESOLVER:-$PENPOT_DEFAULT_INTERNAL_RESOLVER};
|
||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600}; # Default to 350MiB
|
||||
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_INTERNAL_RESOLVER,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
< /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
< /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf;
|
||||
|
||||
envsubst "\$PENPOT_INTERNAL_RESOLVER" \
|
||||
< /etc/nginx/overrides.d/resolvers.conf.template > /etc/nginx/overrides.d/resolvers.conf;
|
||||
|
||||
exec "$@";
|
||||
|
||||
@@ -46,7 +46,6 @@ http {
|
||||
proxy_buffer_size 16k;
|
||||
proxy_busy_buffers_size 24k; # essentially, proxy_buffer_size + 2 small buffers of 4k
|
||||
proxy_buffers 32 4k;
|
||||
resolver $PENPOT_INTERNAL_RESOLVER ipv6=off;
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
|
||||
1
docker/images/files/resolvers.conf
Normal file
1
docker/images/files/resolvers.conf
Normal file
@@ -0,0 +1 @@
|
||||
resolver $PENPOT_INTERNAL_RESOLVER ipv6=off valid=10s;
|
||||
BIN
docs/img/interface/youraccount-notifications.webp
Normal file
BIN
docs/img/interface/youraccount-notifications.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/interface/youraccount-password.webp
Normal file
BIN
docs/img/interface/youraccount-password.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/interface/youraccount-profile.webp
Normal file
BIN
docs/img/interface/youraccount-profile.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/interface/youraccount-settings.webp
Normal file
BIN
docs/img/interface/youraccount-settings.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -280,6 +280,67 @@ Postgres database and another one for the assets uploaded by your users (images
|
||||
clips). There may be more volumes if you enable other features, as explained in the file
|
||||
itself.
|
||||
|
||||
### Configure the proxy
|
||||
|
||||
Your host configuration needs to make a proxy to http://localhost:9001.
|
||||
|
||||
#### Example with NGINX
|
||||
|
||||
```bash
|
||||
server {
|
||||
listen 80;
|
||||
server_name penpot.mycompany.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name penpot.mycompany.com;
|
||||
|
||||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
client_max_body_size 31457280;
|
||||
|
||||
# Logs: Configure your logs following the best practices inside your company
|
||||
access_log /path/to/penpot.access.log;
|
||||
error_log /path/to/penpot.error.log;
|
||||
|
||||
# TLS: Configure your TLS following the best practices inside your company
|
||||
ssl_certificate /path/to/fullchain;
|
||||
ssl_certificate_key /path/to/privkey;
|
||||
|
||||
# Websockets
|
||||
location /ws/notifications {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass http://localhost:9001/ws/notifications;
|
||||
}
|
||||
|
||||
# Proxy pass
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://localhost:9001/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example with CADDY SERVER
|
||||
|
||||
```bash
|
||||
penpot.mycompany.com {
|
||||
reverse_proxy :9001
|
||||
tls /path/to/fullchain.pem /path/to/privkey.pem
|
||||
log {
|
||||
output file /path/to/penpot.log
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Knowing how to do Penpot troubleshooting can be very useful; on the one hand, it helps to create issues easier to resolve, since they include relevant information from the beginning which also makes them get solved faster; on the other hand, many times troubleshooting gives the necessary information to resolve a problem autonomously, without even creating an issue.
|
||||
@@ -350,7 +411,6 @@ you need.
|
||||
Therefore, your prerequisite will be to have a Kubernetes cluster on which we can install
|
||||
Helm.
|
||||
|
||||
|
||||
### What is Helm
|
||||
|
||||
*Helm* is the package manager for Kubernetes. A *Chart* is a Helm package. It contains
|
||||
|
||||
@@ -5,52 +5,6 @@ title: 02· The interface
|
||||
<h1 id="the-interface">The interface</h1>
|
||||
<p class="main-paragraph">The Penpot interface has three main areas: Dashboard, Workspace and View mode. Lets take a look at their composition and main features.</p>
|
||||
|
||||
|
||||
<h2 id="interface-dashboard">Dashboard</h2>
|
||||
<p>The Dashboard is the place where you will be able to organize your files, libraries, projects and teams.</p>
|
||||
<figure>
|
||||
<a href="/img/interface/dashboard-dark.webp" target="_blank">
|
||||
<img src="/img/interface/dashboard-dark.webp" alt="Penpot's dashboard" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<p class="hint">
|
||||
<strong>1)</strong> Teams
|
||||
<strong>2)</strong> Search files
|
||||
<strong>3)</strong> Projects
|
||||
<strong>4)</strong> Drafts
|
||||
<strong>5)</strong> Shared Libraries
|
||||
<strong>6)</strong> Custom fonts
|
||||
<strong>7)</strong> Pinned projects
|
||||
<strong>8)</strong> User area
|
||||
<strong>9)</strong> Comment notifications
|
||||
<strong>10)</strong> Create project
|
||||
<strong>11)</strong> File card
|
||||
<strong>12)</strong> Libraries & Templates module
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li><strong>Teams:</strong> A team allows you to collaborate with other Penpot users. Team members are allowed to work with any project or file within the team depending on their permissions. Members with admin permissions can also invite other members. Create or join as many teams as you need with different groups of people.</li>
|
||||
<li><strong>Search:</strong> If you are looking for a specific file just type its title at the search box.</li>
|
||||
<li><strong>Projects:</strong> A project allows you to group design files. It works pretty much like a folder in a file system. You can create as many projects as you need. If you are going to work with more people in a project, you should create it inside a team.</li>
|
||||
<li><strong>Drafts:</strong> The drafts section is where you can find the design files that are not inside any project.</li>
|
||||
<li><strong>Shared Libraries:</strong> In this section you will find all the design files that have been added as shared libraries. That way you will be able to better control the files that are sharing their assets. </li>
|
||||
<li><strong>Custom fonts:</strong> If you have purchased or own personal fonts that are not included in the catalog provided by Penpot, you can upload them from your computer and use them across the files of a team.</li>
|
||||
<li><strong>Pinned projects:</strong> If you want to keep some projects handy (for instance because you’re currently working on them) you can pin them to make them quickly available at the sidebar.</li>
|
||||
<li><strong>User area:</strong> This must be you! Access your profile settings, Penpot tutorials, the Penpot Community and more. You can also find here a way to leave us feedback. We’d love to read your thoughts :)</li>
|
||||
<li><strong>Comments notifications:</strong> Here you will be able to see if you have unread comments inside the files of the team.</li>
|
||||
<li><strong>Create project:</strong> Create as many projects as you need to organize your designs.</li>
|
||||
<li><strong>File card:</strong> Basic information about a file at plain sight. A preview, update info or if it’s added as a Shared Library. From there you can perform several actions over the file (rename, duplicate, move, download, delete).</li>
|
||||
<li><strong>Libraries & Templates module:</strong> A curated selection of Libraries & Templates files ready to import.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<h3 id="your-account">Your account</h3>
|
||||
<p>Your account settings can be changed at the user area, in <b>Your account</b>. Here you can make changes to your profile, password or account language, as well as generate personal access tokens and access release notes.</p>
|
||||
<p>If you want to change the email address associated to your account or remove your account entirely, this can be done in the <b>Profile</b> section.</p>
|
||||
|
||||
|
||||
|
||||
<h2 id="interface-workspace">Workspace</h2>
|
||||
<p>The Workspace is where you actually create your designs. You have an infinite canvas where you can work directly but you also have the ability to create and work inside boards that will help you to create pages and exportation units.</p>
|
||||
|
||||
@@ -102,8 +56,6 @@ title: 02· The interface
|
||||
<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>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h2 id="interface-viewmode">View mode</h2>
|
||||
<p>Launch the view mode to present and share your designs, comment on them and play with the interactions set at the workspace. You also have an Inspect mode where you can get properties specifications and code snippets. <a href="/user-guide/view-mode/">More about the View mode.</a></p>
|
||||
|
||||
@@ -145,6 +97,83 @@ title: 02· The interface
|
||||
<li><strong>Navigation buttons:</strong> Forward and backwards buttons.</li>
|
||||
</ol>
|
||||
|
||||
<h2 id="interface-dashboard">Dashboard</h2>
|
||||
<p>The Dashboard is the place where you will be able to organize your files, libraries, projects and teams.</p>
|
||||
<figure>
|
||||
<a href="/img/interface/dashboard-dark.webp" target="_blank">
|
||||
<img src="/img/interface/dashboard-dark.webp" alt="Penpot's dashboard" />
|
||||
</a>
|
||||
</figure>
|
||||
|
||||
<p class="hint">
|
||||
<strong>1)</strong> Teams
|
||||
<strong>2)</strong> Search files
|
||||
<strong>3)</strong> Projects
|
||||
<strong>4)</strong> Drafts
|
||||
<strong>5)</strong> Shared Libraries
|
||||
<strong>6)</strong> Custom fonts
|
||||
<strong>7)</strong> Pinned projects
|
||||
<strong>8)</strong> User area
|
||||
<strong>9)</strong> Comment notifications
|
||||
<strong>10)</strong> Create project
|
||||
<strong>11)</strong> File card
|
||||
<strong>12)</strong> Libraries & Templates module
|
||||
</p>
|
||||
|
||||
<ol>
|
||||
<li><strong>Teams:</strong> A team allows you to collaborate with other Penpot users. Team members are allowed to work with any project or file within the team depending on their permissions. Members with admin permissions can also invite other members. Create or join as many teams as you need with different groups of people.</li>
|
||||
<li><strong>Search:</strong> If you are looking for a specific file just type its title at the search box.</li>
|
||||
<li><strong>Projects:</strong> A project allows you to group design files. It works pretty much like a folder in a file system. You can create as many projects as you need. If you are going to work with more people in a project, you should create it inside a team.</li>
|
||||
<li><strong>Drafts:</strong> The drafts section is where you can find the design files that are not inside any project.</li>
|
||||
<li><strong>Shared Libraries:</strong> In this section you will find all the design files that have been added as shared libraries. That way you will be able to better control the files that are sharing their assets. </li>
|
||||
<li><strong>Custom fonts:</strong> If you have purchased or own personal fonts that are not included in the catalog provided by Penpot, you can upload them from your computer and use them across the files of a team.</li>
|
||||
<li><strong>Pinned projects:</strong> If you want to keep some projects handy (for instance because you’re currently working on them) you can pin them to make them quickly available at the sidebar.</li>
|
||||
<li><strong>User area:</strong> This must be you! Access your profile settings, Penpot tutorials, the Penpot Community and more. You can also find here a way to leave us feedback. We’d love to read your thoughts :)</li>
|
||||
<li><strong>Comments notifications:</strong> Here you will be able to see if you have unread comments inside the files of the team.</li>
|
||||
<li><strong>Create project:</strong> Create as many projects as you need to organize your designs.</li>
|
||||
<li><strong>File card:</strong> Basic information about a file at plain sight. A preview, update info or if it’s added as a Shared Library. From there you can perform several actions over the file (rename, duplicate, move, download, delete).</li>
|
||||
<li><strong>Libraries & Templates module:</strong> A curated selection of Libraries & Templates files ready to import.</li>
|
||||
</ol>
|
||||
|
||||
<h3 id="your-account">Your account</h3>
|
||||
<p>Your account settings can be changed at the user area, in <b>Your account</b>. Here you can make changes to your profile, password or account language, as well as generate personal access tokens and access release notes.</p>
|
||||
|
||||
<h4 id="your-account-profile">Profile
|
||||
<a class="direct-link" href="#your-account-profile">#</a>
|
||||
</h3>
|
||||
<p>If you want to change the email address associated to your account or remove your account entirely, this can be done in the <b>Profile</b> section.</p>
|
||||
<figure>
|
||||
<img src="/img/interface/youraccount-profile.webp" alt="Penpot's profile" />
|
||||
</figure>
|
||||
|
||||
<h4 id="your-account-password">Password
|
||||
<a class="direct-link" href="#your-account-password">#</a>
|
||||
</h3>
|
||||
<p>If you want to change your password to a new one, this can be done in the <b>Password</b> section.</p>
|
||||
<figure>
|
||||
<img src="/img/interface/youraccount-password.webp" alt="Penpot's password" />
|
||||
</figure>
|
||||
|
||||
<h4 id="your-account-notifications">Notifications
|
||||
<a class="direct-link" href="#your-account-notifications">#</a>
|
||||
</h3>
|
||||
<p>At the <strong>Notifications</strong> section you can configure the email and dashboard notifications.</p>
|
||||
<figure>
|
||||
<img src="/img/interface/youraccount-notifications.webp" alt="Penpot's notifications" />
|
||||
</figure>
|
||||
|
||||
<h4 id="your-account-settings">Settings
|
||||
<a class="direct-link" href="#your-account-settings">#</a>
|
||||
</h3>
|
||||
<p>At the <strong>Settings</strong> section you can change the language and the UI color theme.</p>
|
||||
<figure>
|
||||
<img src="/img/interface/youraccount-settings.webp" alt="Penpot's settings" />
|
||||
</figure>
|
||||
|
||||
<h4 id="your-account-accesstokens">Access tokens
|
||||
<a class="direct-link" href="#your-account-accesstokens">#</a>
|
||||
</h3>
|
||||
<p>At the <strong>Asset tokens</strong> section you can manage your access tokens. <a href="https://help.penpot.app/technical-guide/integration/#access-tokens" target="_blank">Read more about access tokens here</a>.</p>
|
||||
|
||||
<h2 id="interface-ui-theme">UI Theme</h2>
|
||||
<p>Penpot's default interface is dark but you can switch anytime to a light option. You have 2 ways to change the theme:</p>
|
||||
@@ -170,4 +199,4 @@ title: 02· The interface
|
||||
<img src="/img/interface/viewmode-light.webp" alt="Penpot's view mode" />
|
||||
</a>
|
||||
<figcaption>Penpot's view mode in light mode</figcaption>
|
||||
</figure>
|
||||
</figure>
|
||||
@@ -70,6 +70,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
);
|
||||
this.toolbarOptions = page.getByTestId("toolbar-options");
|
||||
this.rectShapeButton = page.getByRole("button", { name: "Rectangle (R)" });
|
||||
this.moveButton = page.getByRole("button", { name: "Move (V)" });
|
||||
this.boardButton = page.getByRole("button", { name: "Board (B)" });
|
||||
this.toggleToolbarButton = page.getByRole("button", {
|
||||
name: "Toggle toolbar",
|
||||
|
||||
@@ -13,6 +13,7 @@ test("Bug 7549 - User clicks on color swatch to display the color picker next to
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.moveButton.click();
|
||||
const swatch = workspacePage.page.getByRole("button", { name: "E8E9EA" });
|
||||
const swatchBox = await swatch.boundingBox();
|
||||
await swatch.click();
|
||||
@@ -171,6 +172,7 @@ test("Bug 9900 - Color picker has no inputs for HSV values", async ({
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.moveButton.click();
|
||||
const swatch = workspacePage.page.getByRole("button", { name: "E8E9EA" });
|
||||
await swatch.click();
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette",
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.moveButton.click();
|
||||
|
||||
await workspacePage.page.keyboard.press("Alt+p");
|
||||
|
||||
|
||||
10
frontend/resources/styles/common/dependencies/storybook.scss
Normal file
10
frontend/resources/styles/common/dependencies/storybook.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
.sb-show-main.sb-main-fullscreen,
|
||||
.sb-show-main.sb-main-padded {
|
||||
overflow-y: auto;
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
@import "common/dependencies/fonts";
|
||||
@import "common/dependencies/animations";
|
||||
@import "common/dependencies/highlight.scss";
|
||||
@import "common/dependencies/storybook.scss";
|
||||
|
||||
@import "common/refactor/themes.scss";
|
||||
@import "common/refactor/design-tokens.scss";
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
(update :comments-local assoc :open id))
|
||||
(update :comments-local assoc :options nil)
|
||||
(update :comments-local dissoc :draft)
|
||||
(update :workspace-drawing dissoc :comment)
|
||||
(update-in [:comments id] assoc (:id comment) comment))))
|
||||
|
||||
ptk/WatchEvent
|
||||
@@ -146,7 +145,6 @@
|
||||
(update :comments-local assoc :open id)
|
||||
(update :comments-local assoc :options nil)
|
||||
(update :comments-local dissoc :draft)
|
||||
(update :workspace-drawing dissoc :comment)
|
||||
(update-in [:comments id] assoc (:id comment) comment))))
|
||||
|
||||
ptk/WatchEvent
|
||||
@@ -415,8 +413,8 @@
|
||||
(->> (rp/cmd! :get-comment-threads {:file-id file-id :share-id share-id})
|
||||
(rx/map comment-threads-fetched))
|
||||
|
||||
;; Refresh team members
|
||||
(rx/of (dtm/fetch-members)))))))
|
||||
(when (:workspace-local state)
|
||||
(rx/of (dtm/fetch-members))))))))
|
||||
|
||||
(defn retrieve-comments
|
||||
[thread-id]
|
||||
@@ -474,7 +472,7 @@
|
||||
(-> state
|
||||
(update :comments-local assoc :open id)
|
||||
(update :comments-local assoc :options nil)
|
||||
(update :workspace-drawing dissoc :comment)))))
|
||||
(update :comments-local dissoc :draft)))))
|
||||
|
||||
(defn close-thread
|
||||
[]
|
||||
@@ -482,8 +480,7 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :comments-local dissoc :open :draft :options)
|
||||
(update :workspace-drawing dissoc :comment)))))
|
||||
(update :comments-local dissoc :open :draft :options)))))
|
||||
|
||||
(defn update-filters
|
||||
[{:keys [mode show list] :as params}]
|
||||
@@ -524,7 +521,6 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-drawing assoc :comment params)
|
||||
(update :comments-local assoc :draft params)))))
|
||||
|
||||
(defn update-draft-thread
|
||||
@@ -533,7 +529,6 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(d/update-in-when [:workspace-drawing :comment] merge data)
|
||||
(d/update-in-when [:comments-local :draft] merge data)))))
|
||||
|
||||
(defn toggle-comment-options
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
(declare go-to-frame-auto)
|
||||
|
||||
(defn bundle-fetched
|
||||
[{:keys [project file share-links libraries users permissions thumbnails] :as bundle}]
|
||||
[{:keys [project file team share-links libraries users permissions thumbnails] :as bundle}]
|
||||
(let [pages (->> (dm/get-in file [:data :pages])
|
||||
(map (fn [page-id]
|
||||
(let [data (get-in file [:data :pages-index page-id])]
|
||||
@@ -183,15 +183,19 @@
|
||||
(ptk/reify ::bundle-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :share-links share-links)
|
||||
(assoc :viewer {:libraries (d/index-by :id libraries)
|
||||
:users (d/index-by :id users)
|
||||
:permissions permissions
|
||||
:project project
|
||||
:pages pages
|
||||
:thumbnails thumbnails
|
||||
:file file})))
|
||||
(let [team-id (:id team)
|
||||
team {:members users}]
|
||||
(-> state
|
||||
(assoc :share-links share-links)
|
||||
(assoc :current-team-id team-id)
|
||||
(assoc :teams {team-id team})
|
||||
(assoc :viewer {:libraries (d/index-by :id libraries)
|
||||
:users (d/index-by :id users)
|
||||
:permissions permissions
|
||||
:project project
|
||||
:pages pages
|
||||
:thumbnails thumbnails
|
||||
:file file}))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
|
||||
@@ -478,8 +478,7 @@
|
||||
(rx/of (initialize-page* file-id page-id page)
|
||||
(dwth/watch-state-changes file-id page-id)
|
||||
(dwl/watch-component-changes)
|
||||
(when (cf/external-feature-flag "boards-02" "test")
|
||||
(select-frame-tool file-id page-id)))
|
||||
(select-frame-tool file-id page-id))
|
||||
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
|
||||
|
||||
(defn finalize-page
|
||||
|
||||
@@ -62,18 +62,21 @@
|
||||
;; --- Navigate (Event)
|
||||
|
||||
(defn navigated
|
||||
[match]
|
||||
[match send-event-info?]
|
||||
(ptk/reify ::navigated
|
||||
IDeref
|
||||
(-deref [_] match)
|
||||
|
||||
ev/Event
|
||||
(-data [_]
|
||||
(let [route (dm/get-in match [:data :name])
|
||||
params (get match :path-params)]
|
||||
(assoc params
|
||||
::ev/name "navigate"
|
||||
:route (name route))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(when send-event-info?
|
||||
(let [route (dm/get-in match [:data :name])
|
||||
params (get match :query-params)]
|
||||
(rx/of (ptk/event
|
||||
::ev/event
|
||||
(assoc params
|
||||
::ev/name "navigate"
|
||||
:route (name route)))))))
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
@@ -186,6 +189,21 @@
|
||||
|
||||
;; --- History API
|
||||
|
||||
;; Check the urls to see if we need to send the navigated event.
|
||||
;; If two paths are the same we only send the event when there is a
|
||||
;; change in the parameters `file-id`, `page-id` or `team-id`
|
||||
(defn- send-event-info?
|
||||
[old-url new-url]
|
||||
(let [params [:file-id :page-id :team-id]
|
||||
new-uri (u/uri new-url)
|
||||
new-path (:path new-uri)
|
||||
new-params (-> new-uri :query u/query-string->map (select-keys params))
|
||||
old-uri (u/uri old-url)
|
||||
old-path (:path old-uri)
|
||||
old-params (-> old-uri :query u/query-string->map (select-keys params))]
|
||||
(or (not= old-path new-path)
|
||||
(not= new-params old-params))))
|
||||
|
||||
(defn initialize-history
|
||||
[on-change]
|
||||
(ptk/reify ::initialize-history
|
||||
@@ -200,11 +218,19 @@
|
||||
(let [stopper (rx/filter (ptk/type? ::initialize-history) stream)
|
||||
history (:history state)
|
||||
router (:router state)]
|
||||
(ts/schedule #(on-change router (.getToken ^js history)))
|
||||
(->> (rx/create (fn [subs]
|
||||
(let [key (e/listen history "navigate" (fn [o] (rx/push! subs (.-token ^js o))))]
|
||||
(fn []
|
||||
(bhistory/disable! history)
|
||||
(e/unlistenByKey key)))))
|
||||
(ts/schedule #(on-change router (.getToken ^js history) true))
|
||||
(->> (rx/concat
|
||||
(rx/of nil nil)
|
||||
(rx/create
|
||||
(fn [subs]
|
||||
(let [key (e/listen history "navigate" (fn [o] (rx/push! subs (.-token ^js o))))]
|
||||
(fn []
|
||||
(bhistory/disable! history)
|
||||
(e/unlistenByKey key))))))
|
||||
(rx/buffer 2 1)
|
||||
(rx/take-until stopper)
|
||||
(rx/subs! #(on-change router %)))))))
|
||||
(rx/subs!
|
||||
(fn [[old-url new-url]]
|
||||
(when (some? new-url)
|
||||
(let [send? (or (nil? old-url) (send-event-info? old-url new-url))]
|
||||
(on-change router new-url send?))))))))))
|
||||
|
||||
@@ -132,10 +132,11 @@
|
||||
|
||||
(defn- blank-content?
|
||||
[content]
|
||||
(or (str/blank? content)
|
||||
(str/empty? content)
|
||||
;; If only one char and it's the zero-width whitespace
|
||||
(and (= 1 (count content)) (= (first content) zero-width-space))))
|
||||
(let [content (str/trim content)]
|
||||
(or (str/blank? content)
|
||||
(str/empty? content)
|
||||
(and (= (count content) 1)
|
||||
(= (first content) zero-width-space)))))
|
||||
|
||||
;; Component that renders the component content
|
||||
(mf/defc comment-content*
|
||||
|
||||
@@ -959,6 +959,7 @@
|
||||
on-power-up-click
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(st/emit! (ptk/event ::ev/event {::ev/name "explore-pricing-click" ::ev/origin "dashboard" :section "sidebar"}))
|
||||
(dom/open-new-window "https://penpot.app/pricing")))]
|
||||
|
||||
[:*
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
:plugin-url plugin))))
|
||||
|
||||
(defn on-navigate
|
||||
[router path]
|
||||
[router path send-event-info?]
|
||||
(let [location (.-location js/document)
|
||||
[base-path qs] (str/split path "?")
|
||||
location-path (dm/str (.-origin location) (.-pathname location))
|
||||
@@ -102,7 +102,7 @@
|
||||
(st/emit! (rt/assign-exception {:type :not-found}))
|
||||
|
||||
(some? match)
|
||||
(st/emit! (rt/navigated match))
|
||||
(st/emit! (rt/navigated match send-event-info?))
|
||||
|
||||
:else
|
||||
;; We just recheck with an additional profile request; this
|
||||
|
||||
@@ -35,5 +35,8 @@
|
||||
|
||||
;; Old components can have texts without position data that must be rendered via foreign key
|
||||
(cond
|
||||
(some? position-data) [:> svg/text-shape props]
|
||||
is-component? [:> fo/text-shape props])))
|
||||
(some? position-data)
|
||||
[:> svg/text-shape props]
|
||||
|
||||
(or (nil? position-data) is-component?)
|
||||
[:> fo/text-shape props])))
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc options*
|
||||
[{:keys [shape file-id shapes-with-children shared-libs] :as props}]
|
||||
[{:keys [shape file-id shapes-with-children libraries] :as props}]
|
||||
(let [shape-id (dm/get-prop shape :id)
|
||||
shape-type (dm/get-prop shape :type)
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
[:> color-selection-menu* {:type shape-type
|
||||
:shapes shapes-with-children
|
||||
:file-id file-id
|
||||
:libraries shared-libs}]
|
||||
:libraries libraries}]
|
||||
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
|
||||
[:& blur-menu {:ids ids
|
||||
:values (select-keys shape [:blur])}]
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
|
||||
page-id (unchecked-get props "page-id")
|
||||
file-id (unchecked-get props "file-id")
|
||||
shared-libs (unchecked-get props "shared-libs")
|
||||
shared-libs (unchecked-get props "libraries")
|
||||
|
||||
show-caps (some #(and (= :path (:type %)) (gsh/open-path? %)) shapes)
|
||||
|
||||
|
||||
@@ -304,8 +304,7 @@
|
||||
:page-id page-id
|
||||
:file-id file-id
|
||||
:vport vport
|
||||
:zoom zoom
|
||||
:drawing drawing}])
|
||||
:zoom zoom}])
|
||||
|
||||
(when picking-color?
|
||||
[:& pixel-overlay/pixel-overlay {:vport vport
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc comments-layer*
|
||||
[{:keys [vbox vport zoom drawing file-id page-id]}]
|
||||
[{:keys [vbox vport zoom file-id page-id]}]
|
||||
(let [vbox-x (dm/get-prop vbox :x)
|
||||
vbox-y (dm/get-prop vbox :y)
|
||||
vport-w (dm/get-prop vport :width)
|
||||
@@ -73,7 +73,7 @@
|
||||
:viewport viewport
|
||||
:zoom zoom}])))
|
||||
|
||||
(when-let [draft (:comment drawing)]
|
||||
(when-let [draft (:draft local)]
|
||||
[:> cmt/comment-floating-thread-draft*
|
||||
{:draft draft
|
||||
:on-cancel on-draft-cancel
|
||||
|
||||
@@ -350,8 +350,7 @@
|
||||
[:> comments/comments-layer* {:vbox vbox
|
||||
:page-id page-id
|
||||
:vport vport
|
||||
:zoom zoom
|
||||
:drawing drawing}])
|
||||
:zoom zoom}])
|
||||
|
||||
(when picking-color?
|
||||
[:& pixel-overlay/pixel-overlay {:vport vport
|
||||
|
||||
@@ -4754,7 +4754,7 @@ msgstr "Or add some of these to try:"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:368
|
||||
msgid "workspace.libraries.empty.no-libraries"
|
||||
msgstr "There are no Shared Libraries at you team, you can look for"
|
||||
msgstr "There are no Shared Libraries at your team, you can look for"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:372
|
||||
msgid "workspace.libraries.empty.some-templates"
|
||||
|
||||
@@ -4772,7 +4772,7 @@ msgstr "O añadir algunas de éstas para probar:"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:368
|
||||
msgid "workspace.libraries.empty.no-libraries"
|
||||
msgstr "No hay Biblioteacas Compartidas en tu equipo, puedes buscar"
|
||||
msgstr "No hay Bibliotecas Compartidas en tu equipo, puedes buscar"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:372
|
||||
msgid "workspace.libraries.empty.some-templates"
|
||||
|
||||
Reference in New Issue
Block a user