Compare commits

...

20 Commits

Author SHA1 Message Date
Alejandro Alonso
ed5875f29a Merge pull request #7154 from penpot/niwinz-staging-bug-1
🐛 Fix incorrect show request-access dialog on not-found on viewer
2025-08-22 09:19:47 +02:00
Andrey Antukh
ad38a21053 🐛 Fix incorrect show request-access dialog on not-found on viewer
When a user is not-authenticated
2025-08-20 13:35:20 +02:00
Andrey Antukh
adffac4eec Merge remote-tracking branch 'origin/main' into staging 2025-08-20 12:49:31 +02:00
Yamila Moreno
73dfe12ec9 📚 Update k8s documentation 2025-08-20 09:04:25 +02:00
Eva Marco
ff2e845f2c 🐛 Fix double click on set name input (#7096) 2025-08-13 09:23:53 +02:00
Alejandro Alonso
8e0a6e4123 🐛 Fix auto height is fixed in the HTML inspect tab for text elements (#7078) 2025-08-11 09:07:43 +02:00
Marina López
0131cd6f8b Display the total price of the subscription and the cap amount (#7088) 2025-08-11 09:07:24 +02:00
Andrey Antukh
288a7b21d6 Merge tag '2.9.0-RC8' 2025-08-08 09:47:42 +02:00
andrés gonzález
32bd08533d 💄 Remove slide about overrides in the release notes (#7086) 2025-08-08 09:46:40 +02:00
Yamila Moreno
c1aae12327 📎 Improve gh actions 2025-08-07 18:08:25 +02:00
Yamila Moreno
23a6f4b7c1 📎 Improve gh actions 2025-08-07 18:07:47 +02:00
Andrey Antukh
133e6e1e68 Merge tag '2.9.0-RC7' 2025-08-07 16:30:30 +02:00
Andrey Antukh
6abd045273 🐛 Add missing generator for token-set file change operation (#7080)
* 🐛 Add missing generator for token-set file change operation

* 🐛 Use ::sm/any instead of :any for on get-file-data-for-thumbnail rpc method

Mainly because :any will use a very generic generator that can generate
instances of Character that are not directly serializable to JSON
2025-08-07 12:36:14 +02:00
Marina López
778a608854 🐛 Fix tooltip for icon plans from team dropdown (#7075) 2025-08-07 07:43:49 +02:00
Marina López
a76a9fae41 🐛 Fix an unused translation (#7074) 2025-08-06 13:28:02 +02:00
Andrey Antukh
f7cfbdd229 🐛 Comment the problematic migration 2025-08-05 22:05:52 +02:00
Andrey Antukh
e28d2842f6 🐛 Revert the revert of orientation detection on media
This reverts commit 515cbf7bef.
2025-08-05 22:03:09 +02:00
Andrey Antukh
ccc3ca0948 Disable virtual threads on http server 2025-08-05 20:34:47 +02:00
Andrey Antukh
515cbf7bef 🐛 Revert orientation detection on media 2025-08-05 19:30:01 +02:00
Francis Santiago
4501d13961 📚 Clarify OpenShift requirements (#6937)
* 📚 Clarify OpenShift requirements

* 📚 Remove the click for expanding
2025-08-01 16:26:04 +02:00
20 changed files with 150 additions and 162 deletions

View File

@@ -1,28 +1,14 @@
name: Build and Upload Penpot Bundles non-prod
name: Build and Upload Penpot Bundles
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
# Create bundle from manual action
workflow_dispatch:
workflow_call:
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
gh_ref:
description: 'Name of the branch'
type: string
required: true
jobs:
build-bundles:
@@ -38,15 +24,15 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
- name: Extract somer useful variables
- name: Extract some 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
- name: Set up Docker Buildx for multi-arch build
uses: docker/setup-buildx-action@v3
- name: Run manage.sh build-bundle from host
@@ -57,73 +43,22 @@ jobs:
mkdir zips
mv bundles penpot
- name: Create zip bundles for zip_mode == 'all'
if: ${{ github.event.inputs.zip_mode == 'all' }}
- name: Create zip bundles
run: |
echo "📦 Packaging Penpot 'all' bundles..."
zip -r zips/penpot-all-bundles.zip penpot
echo "📦 Packaging Penpot bundles..."
zip -r zips/penpot.zip penpot
- name: Create zip bundles for zip_mode != 'all'
if: ${{ github.event.inputs.zip_mode != 'all' }}
- name: Upload Penpot bundle to S3
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
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_branch}}-latest.zip
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.commit_hash }}.zip
- 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'
- name: Notify Mattermost
if: failure()
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 }}\`
*[PENPOT] Error during the execution of the job*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_branch}}`
🔗 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 }}"

12
.github/workflows/build-develop.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: Build and Upload Penpot DEVELOP Bundles
on:
schedule:
- cron: '16 5-20 * * 1-5'
jobs:
build-develop-bundle:
uses: ./.github/workflows/build-bundles.yml
secrets: inherit
with:
gh_ref: "develop"

12
.github/workflows/build-staging.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
name: Build and Upload Penpot STAGING Bundles
on:
schedule:
- cron: '0 5 * * 1-5'
jobs:
build-staging-bundle:
uses: ./.github/workflows/build-bundles.yml
secrets: inherit
with:
gh_ref: "staging"

View File

@@ -28,6 +28,8 @@
- Add the ability to show login dialog on profile settings [Github #6871](https://github.com/penpot/penpot/pull/6871)
- Improve the application of tokens with object specific tokens [Taiga #10209](https://tree.taiga.io/project/penpot/us/10209)
- Add info to apply-token event [Taiga #11710](https://tree.taiga.io/project/penpot/task/11710)
- Fix double click on set name input [Taiga #11747](https://tree.taiga.io/project/penpot/issue/11747)
### :bug: Bugs fixed
@@ -58,6 +60,7 @@
- Fix export button width on inspect tab [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
- Fix stroke width token application [Taiga #11724](https://tree.taiga.io/project/penpot/issue/11724)
- Fix number token application on shape [Taiga #11331](https://tree.taiga.io/project/penpot/task/11331)
- Fix auto height is fixed in the HTML inspect tab for text elements [Taiga #11680](https://tree.taiga.io/project/penpot/task/11680)
## 2.8.1

View File

@@ -25,6 +25,7 @@
[app.rpc :as-alias rpc]
[app.rpc.doc :as-alias rpc.doc]
[app.setup :as-alias setup]
[app.worker :as wrk]
[integrant.core :as ig]
[promesa.exec :as px]
[reitit.core :as r]
@@ -63,7 +64,7 @@
(assert (sm/check schema:server-params params)))
(defmethod ig/init-key ::server
[_ {:keys [::handler ::router ::host ::port] :as cfg}]
[_ {:keys [::handler ::router ::host ::port ::wrk/executor] :as cfg}]
(l/info :hint "starting http server" :port port :host host)
(let [options {:http/port port
:http/host host
@@ -72,7 +73,7 @@
:xnio/direct-buffers false
:xnio/io-threads (or (::io-threads cfg)
(max 3 (px/get-available-processors)))
:xnio/dispatch :virtual
:xnio/dispatch executor
:ring/compat :ring2
:socket/backlog 4069}

View File

@@ -231,7 +231,8 @@
::http/router (ig/ref ::http/router)
::http/io-threads (cf/get :http-server-io-threads)
::http/max-body-size (cf/get :http-server-max-body-size)
::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)}
::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)
::wrk/executor (ig/ref ::wrk/executor)}
::ldap/provider
{:host (cf/get :ldap-host)

View File

@@ -185,7 +185,7 @@
[:map {:title "PartialFile"}
[:id ::sm/uuid]
[:revn {:min 0} ::sm/int]
[:page :any]])
[:page [:map-of :keyword ::sm/any]]])
(sv/defmethod ::get-file-data-for-thumbnail
"Retrieves the data for generate the thumbnail of the file. Used

View File

@@ -418,7 +418,14 @@
[:type [:= :set-token-set]]
[:set-name :string]
[:group? :boolean]
[:token-set [:maybe [:fn ctob/token-set?]]]]]
;; FIXME: we should not pass private types as part of changes
;; protocol, the changes protocol should reflect a
;; method/protocol for perform surgical operations on file data,
;; this has nothing todo with internal types of a file data
;; structure.
[:token-set {:gen/gen (sg/generator ctob/schema:token-set)}
[:maybe [:fn ctob/token-set?]]]]]
[:set-token
[:map {:title "SetTokenChange"}

View File

@@ -1618,4 +1618,4 @@
"0007-clear-invalid-strokes-and-fills-v2"
"0008-fix-library-colors-v4"
"0009-clean-library-colors"
"0009-add-partial-text-touched-flags"]))
#_"0009-add-partial-text-touched-flags"]))

View File

@@ -186,6 +186,10 @@
:modified-at modified-at
:tokens tokens})])
#?@(:clj
[json/JSONWriter
(-write [this writter options] (json/-write (deref this) writter options))])
#?@(:cljs [cljs.core/IEncodeJS
(-clj->js [_] (js-obj "id" (clj->js id)
"name" (clj->js name)
@@ -292,7 +296,9 @@
(declare make-token-set)
(def schema:token-set
(sm/required-keys schema:token-set-attrs))
[:schema {:gen/gen (->> (sg/generator schema:token-set-attrs)
(sg/fmap #(make-token-set %)))}
(sm/required-keys schema:token-set-attrs)])
(sm/register! ::token-set schema:token-set) ;; need to register for the recursive schema of token-sets

View File

@@ -103,6 +103,11 @@ If you are deploying Penpot on OpenShift, we recommend following the specific gu
Make sure to review the section **OpenShift Requirements** for important security and compatibility considerations.
### Using Rancher?
If you are deploying Penpot on Rancher, we recommend following the specific guidelines provided in the official documentation:
<a href="https://docs.apps.rancher.io/reference-guides/penpot/" target="_blank">Reference guides / Penpot</a>.
## Upgrade Penpot
When a new version of Penpot's chart is released, or when you want to change the

View File

@@ -125,7 +125,11 @@
(mf/defc menu-team-icon*
[{:keys [subscription-type]}]
[:span {:class (stl/css :subscription-icon) :data-testid "subscription-icon"}
[:span {:class (stl/css :subscription-icon)
:title (if (= subscription-type "unlimited")
(tr "subscription.dashboard.power-up.unlimited-plan")
(tr "subscription.dashboard.power-up.enterprise-plan"))
:data-testid "subscription-icon"}
(case subscription-type
"unlimited" i/character-u
"enterprise" i/character-e)])

View File

@@ -74,42 +74,12 @@
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
:total 2}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
1
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
[:img {:src "images/features/2.9-overrides.gif"
:class (stl/css :start-image)
:border "0"
:alt "Component text overrides"}]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-header)}
[:h1 {:class (stl/css :modal-title)}
"Component text overrides"]]
[:div {:class (stl/css :feature)}
[:p {:class (stl/css :feature-content)}
"You can now edit the text content independently from its properties—such as color, effects, font size, or weight—allowing you to update the wording while preserving the styling."]
[:p {:class (stl/css :feature-content)}
"This change (inspired by community feedback) greatly improves consistency, predictability, and control when working with texts in components."]]
[:div {:class (stl/css :navigation)}
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
[:button {:on-click next
:class (stl/css :next-btn)} "Continue"]]]]]]
2
[:div {:class (stl/css-case :modal-overlay true)}
[:div.animated {:class klass}
[:div {:class (stl/css :modal-container)}
@@ -135,7 +105,7 @@
[:& c/navigation-bullets
{:slide slide
:navigate navigate
:total 3}]
:total 2}]
[:button {:on-click finish
:class (stl/css :next-btn)} "Let's go"]]]]]])))

View File

@@ -150,7 +150,16 @@
:class (stl/css :input-field)}]]
[:div {:class (stl/css :editors-cost)}
[:span {:class (stl/css :modal-text-small)}
(tr "subscription.settings.management.dialog.price-month" (or (get-in @form [:clean-data :min-members]) 0))]
(when (> (get-in @form [:clean-data :min-members]) 25)
[:> i18n/tr-html*
{:class (stl/css :modal-text-cap)
:tag-name "span"
:content (tr "subscription.settings.management.dialog.price-month" "175")}])
[:> i18n/tr-html*
{:class (stl/css-case :text-strikethrough (> (get-in @form [:clean-data :min-members]) 25))
:tag-name "span"
:content (tr "subscription.settings.management.dialog.price-month"
(* 7 (or (get-in @form [:clean-data :min-members]) 0)))}]]
[:span {:class (stl/css :modal-text-small)}
(tr "subscription.settings.management.dialog.payment-explanation")]]]

View File

@@ -199,6 +199,20 @@
@include t.use-typography("body-small");
}
.modal-text-cap {
margin-inline-end: var(--sp-s);
}
.text-strikethrough {
text-decoration: line-through;
}
.modal-text-small strong,
.text-strikethrough strong,
.modal-text-cap strong {
font-weight: $fw700;
}
.modal-content,
.modal-end {
color: var(--color-foreground-secondary);

View File

@@ -501,16 +501,16 @@
profile (mf/deref refs/profile)
auth-error? (= type :authentication)
not-found? (= type :not-found)
authenticated?
(is-authenticated? profile)
request-access?
(and
(or (= type :not-found) auth-error?)
(or workspace? dashboard? view?)
(or (:file-id info)
(:team-id info)))]
(or (some? (:file-id info))
(some? (:team-id info))))]
(mf/with-effect [params info]
(when-not (:loaded info)
@@ -518,25 +518,26 @@
(rx/subs! (partial reset! info*)
(partial reset! info* {:loaded true})))))
(if (and auth-error? (not authenticated?))
[:> context-wrapper*
{:is-workspace workspace?
:is-dashboard dashboard?
:is-viewer view?
:profile profile}
[:> login-dialog* {}]]
(when (get info :loaded false)
(if request-access?
[:> context-wrapper* {:is-workspace workspace?
:is-dashboard dashboard?
:is-viewer view?
:profile profile}
[:> request-access* {:file-id (:file-id info)
:team-id (:team-id info)
:is-default (:team-default info)
:profile profile
:is-workspace workspace?}]]
[:> exception-section* props])))))
(if (or auth-error? not-found?)
(if (not authenticated?)
[:> context-wrapper*
{:is-workspace workspace?
:is-dashboard dashboard?
:is-viewer view?
:profile profile}
[:> login-dialog* {}]]
(when (get info :loaded false)
(if request-access?
[:> context-wrapper* {:is-workspace workspace?
:is-dashboard dashboard?
:is-viewer view?
:profile profile}
[:> request-access* {:file-id (:file-id info)
:team-id (:team-id info)
:is-default (:team-default info)
:profile profile
:is-workspace workspace?}]]
[:> exception-section* props])))
[:> exception-section* props])))

View File

@@ -59,7 +59,7 @@
:type "text"
:on-blur on-submit
:on-key-down on-key-down
:maxlength "256"
:max-length "256"
:auto-focus true
:placeholder (tr "workspace.tokens.set-edit-placeholder")
:default-value default-value}]))
@@ -210,7 +210,7 @@
(mf/defc sets-tree-set*
[{:keys [id set label tree-depth tree-path tree-index is-selected is-active is-draggable is-editing
on-select on-drop on-toggle on-start-edition on-reset-edition on-edit-submit]}]
on-select on-drop on-toggle on-start-edition on-reset-edition on-edit-submit is-new]}]
(let [set-name (ctob/get-name set)
can-edit? (mf/use-ctx ctx/can-edit?)
@@ -239,7 +239,11 @@
:path tree-path})))))
on-double-click
(mf/use-fn (mf/deps id) #(on-start-edition id))
(mf/use-fn
(mf/deps id is-new)
(fn []
(when-not is-new
(on-start-edition id))))
on-checkbox-click
(mf/use-fn
@@ -392,7 +396,7 @@
:is-editing true
:is-active true
:is-selected true
:is-new true
:tree-path path
:tree-depth depth
:tree-index index
@@ -416,6 +420,7 @@
:tree-path path
:tree-depth depth
:tree-index index
:is-new false
:tree-parent-path parent-path
:on-toggle on-toggle-set
:edition-id edition-id

View File

@@ -116,13 +116,17 @@
(let [root? (contains? (:root-shapes options) (:id shape))]
(if (and root? (ctl/any-layout? shape))
:fill
(get-shape-size shape objects :width))))
;; Don't set fixed width for auto-width text shapes
(when-not (and (cfh/text-shape? shape) (= (:grow-type shape) :auto-width))
(get-shape-size shape objects :width)))))
(defmethod get-value :height
[_ shape objects options]
(let [root? (contains? (:root-shapes options) (:id shape))]
(when-not (and root? (ctl/any-layout? shape))
(get-shape-size shape objects :height))))
;; Don't set fixed height for auto-height text shapes
(when-not (and (cfh/text-shape? shape) (= (:grow-type shape) :auto-height))
(get-shape-size shape objects :height)))))
(defmethod get-value :flex-grow
[_ shape _ options]

View File

@@ -4387,11 +4387,6 @@ msgstr "Subscription"
msgid "subscription.settings.add-payment-to-continue"
msgstr "Add a payment method to continue after your trial"
#: src/app/main/ui/settings/subscription.cljs:82, src/app/main/ui/settings/subscription.cljs:115, src/app/main/ui/settings/subscription.cljs:127
#, fuzzy, unused
msgid "subscription.settings.benefits.all-professiona-benefits"
msgstr ""
#: src/app/main/ui/settings/subscription.cljs:247, src/app/main/ui/settings/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:303, src/app/main/ui/settings/subscription.cljs:317
msgid "subscription.settings.benefits.all-professional-benefits"
msgstr "All Professional plan benefits and:"
@@ -4446,8 +4441,10 @@ msgid "subscription.settings.management.dialog.payment-explanation"
msgstr "(No payment will be made now)"
#: src/app/main/ui/settings/subscription.cljs:124
#, markdown
msgid "subscription.settings.management.dialog.price-month"
msgstr "$7 per editor/month x %s"
msgstr ""
"**$%s** per month"
#: src/app/main/ui/settings/subscription.cljs:112
msgid "subscription.settings.management.dialog.select-editors"

View File

@@ -4474,8 +4474,10 @@ msgid "subscription.settings.management.dialog.payment-explanation"
msgstr "(Ahora no se efectuará ningún pago)"
#: src/app/main/ui/settings/subscription.cljs:124
#, markdown
msgid "subscription.settings.management.dialog.price-month"
msgstr "$7 por editor/mes x %s"
msgstr ""
"**$%s** por mes"
#: src/app/main/ui/settings/subscription.cljs:112
msgid "subscription.settings.management.dialog.select-editors"