21915 Commits

Author SHA1 Message Date
Jack Storment
9dc607902b
🐛 Only fall back to anonymous on :not-found in get-profile (#9254)
* 🐛 Only fall back to anonymous on :not-found in get-profile

::get-profile caught Throwable and silently returned the anonymous
user payload for every error - contradicting the in-code comment that
states in all other cases we need to reraise the exception. Under
transient DB conditions (pool checkout timeout, replica lag, statement
timeout, network blip) this masked real DB outages as ordinary
anonymous responses, returning HTTP 200 instead of 5xx and leaving
logged-in users on the login screen with a valid session cookie.

Narrow the catch so only :type :not-found falls through; everything
else propagates and reaches the standard error pipeline.

Closes #9235

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>

* 🐛 Only fall back to anonymous on :not-found in get-profile

::get-profile caught Throwable and silently returned the anonymous
user payload for every error - contradicting the in-code comment that
states in all other cases we need to reraise the exception. Under
transient DB conditions (pool checkout timeout, replica lag, statement
timeout, network blip) this masked real DB outages as ordinary
anonymous responses, returning HTTP 200 instead of 5xx and leaving
logged-in users on the login screen with a valid session cookie.

Narrow the catch so only :type :not-found falls through; everything
else propagates and reaches the standard error pipeline.

Closes #9253

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>

---------

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Signed-off-by: Jack Storment <88656337+jack-stormentswe@users.noreply.github.com>
2026-05-10 19:40:29 +02:00
TinyClaw
7df53a46f2
🔥 Remove stray debug log in exporter upload-resource (#9272)
Signed-off-by: iot2edge <tylerprice830@gmail.com>
Co-authored-by: iot2edge <tylerprice830@gmail.com>
2026-05-10 19:36:55 +02:00
Dexterity
e30e5906c8
♻️ Remove unreachable try/catch in hex->hsl (#9245) 2026-05-10 19:28:12 +02:00
Andrey Antukh
49759021bf Merge remote-tracking branch 'origin/staging' into develop 2026-05-10 14:27:53 +02:00
tmimmanuel
f06a2ae4e3
♻️ Migrate inspect fill/stroke deprecated blocks to modern syntax (#9392)
Signed-off-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>
Co-authored-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>
2026-05-10 14:16:43 +02:00
tmimmanuel
ef4f57c4a1
♻️ Migrate components/link to modern component syntax (#9383)
* ♻️ Migrate components/link to modern component syntax

Signed-off-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>

* 📎 Fix cljfmt indent after link* rename

Signed-off-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>

---------

Signed-off-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>
Co-authored-by: tmimmanuel <155203395+tmimmanuel@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-10 14:12:30 +02:00
Andrey Antukh
79937027eb Merge remote-tracking branch 'origin/main' into staging 2026-05-10 10:50:29 +02:00
Andrey Antukh
9b336e9a3d Add nrepl-eval script and skill 2026-05-10 10:49:53 +02:00
Andrey Antukh
60c718eba1 Merge remote-tracking branch 'origin/staging' into develop 2026-05-10 09:20:27 +02:00
Andrey Antukh
55406be084 Merge remote-tracking branch 'origin/main' into staging 2026-05-10 09:20:08 +02:00
Andrey Antukh
f414392f13 📎 Update changelog 2026-05-10 09:19:56 +02:00
Andrey Antukh
cf3455a487 📎 Add missing entry on CHANGES.md 2026-05-10 09:18:52 +02:00
Andrey Antukh
10a23a6869 Merge remote-tracking branch 'origin/main' into staging 2026-05-10 09:16:41 +02:00
Dexterity
a53237ce9f
🐛 Route Google fonts fetch warning through project logger (#9422) 2026-05-08 17:41:09 +02:00
Jeff
f5b38a5025
🐛 Skip add-recent-color when colorpicker has no completed color (#9251)
Closing the fill dialog while an image-fill upload is still in flight
(or while a gradient is mid-edit) leaves the colorpicker's
current-color with only :opacity — no :image, :gradient, or :color.
update-colorpicker-color's WatchEvent then constructed
`(add-recent-color partial)`, which runs the value through
`clr/check-color` and threw "expected valid color". The user saw an
Internal Assertion Error toast and lost the in-flight upload.

The existing `ignore-color?` guard reads `:type` from the *output* of
`get-color-from-colorpicker-state` — but that helper strips :type from
its result, so the guard never actually fires. Add a schema-based gate
(same validator add-recent-color itself uses) right before `rx/of`, so
a partial selection is silently dropped instead of crashing the
workspace. Behaviour for fully-valid colors is unchanged.

Tests cover three cases: (1) a partial image-tab state with only
:opacity returns nil from watch (was: throws); (2) the same partial
shape on the color tab also returns nil — pinning down that the prior
:type guard wouldn't have caught it; (3) a fully-populated plain color
still produces a watch observable so the guard isn't over-eager.

Closes #8443

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-08 17:37:12 +02:00
Renzo
ea24445c2c
🐛 Toggle display-guides via physical key code so the shortcut works on non-US layouts (#9209)
* 🐛 Toggle display-guides via physical key code so the shortcut works on non-US layouts

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>

* 🐛 Add tests for display-guides shortcut on non-US layout

---------

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
Signed-off-by: Renzo <170978465+RenzoMXD@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-08 17:36:52 +02:00
BitToby
6aeccb1208
🎉 Add selection size badge below bounding box (#9210)
* 🎉 Add selection size badge below bounding box

Signed-off-by: bittoby <218712309+bittoby@users.noreply.github.com>

* 💄 Address review comments

Signed-off-by: bittoby <218712309+bittoby@users.noreply.github.com>

* 💄 Move selection size badge text styles to SCSS class

---------

Signed-off-by: bittoby <218712309+bittoby@users.noreply.github.com>
Signed-off-by: BitToby <218712309+bittoby@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-08 14:20:24 +02:00
web-dev0521
bb93928099
🐛 Fix lost-update race on team.features during concurrent file cr… (#9198)
* 🐛 Fix lost-update race on team.features during concurrent file creation

* 📚 Add CHANGES.md entry for team.features race condition fix (#9197)
2026-05-08 14:12:20 +02:00
Francis Santiago
e9588f3939
🐳 Reuse shared Nginx security headers (#9473)
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2.15.0-RC5
2026-05-08 14:11:09 +02:00
Yamila Moreno
be92e37af3 🔧 Add notification when a new devenv is published 2026-05-08 14:09:21 +02:00
María Valderrama
5a3d5f86af
🐛 Fix nitrate lookups to use nested organization
* 🐛 Fix nitrate lookups to use nested organization

* 📎 Code review
2026-05-08 13:33:31 +02:00
Pablo Alba
639a457c69 💄 Change error message on nitrate subscriptions 2026-05-08 12:27:58 +02:00
Marina López
175fb67afc 💄 Change margin for current plan 2026-05-08 11:59:09 +02:00
Pablo Alba
f3c2c0bee2 Change team organization structure on state 2026-05-08 11:18:26 +02:00
Aitor Moreno
4e98dfb99f
♻️ Refactor GpuState and RenderState
* ♻️ Refactor GpuState

* ♻️ Refactor RenderState

* 🔧 Tweak some _build_env options
2026-05-08 11:10:14 +02:00
Eva Marco
cccd7bc6de
🐛 Fix pixel grid color row (#9360) 2026-05-08 11:06:56 +02:00
Belén Albeza
a52c4e099a 🐛 Fix round/square linecaps not being applied correctly in open paths 2026-05-08 10:31:18 +02:00
Andrey Antukh
18e289b15a
♻️ Migrate link-button component to rumext modern syntax (#9264)
Rename component from link-button to link-button* and remove the legacy
::mf/wrap-props false metadata. Update all callsites to use the modern
[:> lb/link-button* ...] syntax instead of [:& lb/link-button ...].

Part of the #9260 issue.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-08 09:53:12 +02:00
Andrey Antukh
a50785f105 📎 Update changelog 2026-05-08 09:29:28 +02:00
Andrey Antukh
279231240d
🐛 Harden outbound HTTP requests against SSRF and restrict assets handlers (#9390)
* ⬆️ Update root deps

* 🐛 Harden outbound HTTP requests against SSRF and restrict unauthenticated asset access

- Add app.util.ssrf URL/host validator that resolves hostnames and blocks
  loopback, link-local, site-local, cloud metadata, and operator-supplied CIDRs
- Add app.media.sanitize image EOF truncator that strips trailing data after
  PNG IEND, JPEG EOI, GIF trailer, and WebP RIFF markers
- Disable HTTP client auto-redirect; add req-with-redirects! helper that
  revalidates every redirect hop against the SSRF blocklist
- Wire SSRF validation and EOF sanitization into media/download-image
- Validate webhook URLs and OIDC profile picture URLs against SSRF
- Restrict /assets/by-id to require authentication for non-public buckets
  (profile) while keeping public access for file-media-object,
  file-object-thumbnail, team-font-variant, and file-data-fragment
- Add config knobs: ssrf-protection-enabled, ssrf-allowed-hosts,
  ssrf-extra-blocked-cidrs

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-08 09:18:22 +02:00
Andrés Moya
61cd757355
🐛 Detect duplicated token names in the whole library (#9034)
* 🐛 Detect duplicated token names in the whole library

* 🔧 Review comments

* 🐛 Prevent and repair token themes with inexistent sets

* 🔧 Convert tokens lib migration into file migration
2026-05-08 08:26:15 +02:00
Andrey Antukh
3496435e69 📚 Update changelog 2026-05-08 00:32:21 +02:00
Andrey Antukh
d103feebfa 📚 Update changelog 2026-05-07 23:57:49 +02:00
Dr. Dominik Jain
362440fead 🚑 Use base64 envelope for Uint8Array task results to avoid JSON expansion (#9431)
Resolves #9420 (critical memory usage issue in PROD deployment)

When the plugin's ExecuteCodeTaskHandler returns a Uint8Array (e.g. from penpotUtils.exportImage),
JSON.stringify previously serialized it as an object with numeric string keys,
causing ~10x payload expansion and large peak heap usage on the server side.

The plugin now wraps a top-level Uint8Array result in a tagged envelope
{ __type: "base64", data: <base64> }, and ImageContent.byteData decodes this envelope
on the server. The legacy numeric-keyed-object path is retained as a fallback for
compatibility with older plugin builds.
2026-05-07 23:51:50 +02:00
Dr. Dominik Jain
c3743930c2
🚑 Use base64 envelope for Uint8Array task results to avoid JSON expansion (#9431)
Resolves #9420 (critical memory usage issue in PROD deployment)

When the plugin's ExecuteCodeTaskHandler returns a Uint8Array (e.g. from penpotUtils.exportImage),
JSON.stringify previously serialized it as an object with numeric string keys,
causing ~10x payload expansion and large peak heap usage on the server side.

The plugin now wraps a top-level Uint8Array result in a tagged envelope
{ __type: "base64", data: <base64> }, and ImageContent.byteData decodes this envelope
on the server. The legacy numeric-keyed-object path is retained as a fallback for
compatibility with older plugin builds.
2026-05-07 23:50:20 +02:00
María Valderrama
7c5fa038c1
Add Nitrate advanced permissions delete (#9416)
*  Add Nitrate advanced permissions delete

* 📎 Code review
2026-05-07 21:14:30 +02:00
Dr. Dominik Jain
6a44b19311
🐛 Fix keep-alive interval leak in PluginBridge (#9435)
The ping interval was stored in a single variable shared across all
WebSocket connections, so each new connection overwrote the previous
handle and leaked the prior interval.

Move the interval onto ClientConnection as a per-connection field,
and centralize teardown in a new removeConnection(ws) method used
by the close, error and duplicate token rejection paths.

Resolves #9430
2026-05-07 20:37:22 +02:00
Aitor Moreno
0817f13340
♻️ Change how rendering spiral is generated 2026-05-07 17:25:50 +02:00
Alejandro Alonso
fc7748fc84
🐛 Fix(render-wasm): stabilize interactive drag backbuffer crops 2026-05-07 17:12:00 +02:00
Aitor Moreno
bc0f081371
♻️ Refactor text editor state (#9379) 2026-05-07 16:16:44 +02:00
Francis Santiago
d84685c0cb
Merge pull request #9426 from penpot/nginx-security-headers
🐳 Nginx security headers
2026-05-07 16:06:59 +02:00
Eva Marco
c5f2ffab69
🐛 Fix internal error when applying not valid value to margin input (#9311) 2026-05-07 15:24:25 +02:00
FairyPiggyDev
fa06efa84d
♻️ Migrate fo-text and html-text renderers to modern component syntax (#9385)
Step toward issue #9260 (incremental migration of legacy UI
components to the modern `*`-suffixed syntax, removing the per-render
JS-to-Clojure props conversion overhead).

Twin namespaces with parallel structure: each defines six components
that drive a recursive text rendering pass over the editor's content
tree (root -> paragraph-set -> paragraph -> node -> text). Both files
were uniformly legacy: every component carried `::mf/wrap-props
false` and read its props with `(obj/get props "key")`. None had
`::mf/register`, `unchecked-get` or `obj/merge!`, so they qualify as
clean Case-A migrations.

frontend/src/app/main/ui/shapes/text/fo_text.cljs (6 components)
----------------------------------------------------------------

- `render-text`           -> `render-text*`
- `render-root`           -> `render-root*`
- `render-paragraph-set`  -> `render-paragraph-set*`
- `render-paragraph`      -> `render-paragraph*`
- `render-node`           -> `render-node*`     (forward-props case,
                                                 see below)
- `text-shape`            -> `text-shape*`      (`::mf/forward-ref`
                                                 preserved)

The four leaf components switch from `[props]` + per-key
`(obj/get props "key")` to standard destructuring. `text-shape`
already used destructuring under `::mf/props :obj`; that legacy
metadata is dropped because the modern `*` form handles props
automatically. Its single `::mf/forward-ref true` is kept per the
prompt's "preserve forward-ref" rule.

`render-node` is the recursive driver. It needs to forward all of
its incoming props to the matched paragraph-* / text component and
then to a child `render-node*` after overriding `:node`, `:index`
and `:key`. The migrated form uses `::mf/props :obj` together with
`{:keys [node] :as props}` to keep the JS-object props symbol
available, and `(mf/spread-props props {…})` replaces the previous
`obj/clone` + `obj/set!` chain.

`app.util.object` is no longer required by this namespace and the
`(:require ... [app.util.object :as obj] ...)` line is removed.

frontend/src/app/main/ui/shapes/text/html_text.cljs (6 components)
-----------------------------------------------------------------

Identical six-component shape as `fo_text.cljs`, plus a `code?`
flag threaded through every component to switch the rendering path
between regular shapes and code-style shapes.

- `render-text`           -> `render-text*`
- `render-root`           -> `render-root*`
- `render-paragraph-set`  -> `render-paragraph-set*`
- `render-paragraph`      -> `render-paragraph*`
- `render-node`           -> `render-node*`     (same forward-props
                                                 treatment as above,
                                                 plus `is-code` in
                                                 the spread)
- `text-shape`            -> `text-shape*`      (`::mf/forward-ref`
                                                 preserved)

The `code?` boolean prop is renamed to `is-code` per the migration
prompt's "?-suffixed boolean -> `is-` prefix" rule. The rename is
applied at every read site (5 components) and at the `text-shape*`
internal call to `render-node*`, so the prop is consistent inside
the namespace.

`app.util.object` is no longer required by this namespace either
and the corresponding `:require` line is dropped.

External call sites (3 files, 4 sites)
--------------------------------------

- `frontend/src/app/main/ui/shapes/text.cljs` - the legacy
  text-shape wrapper (intentionally kept legacy in this PR because
  it dispatches to `svg/text-shape`, which is still being touched by
  the in-flight PR #9016) now calls `[:> fo/text-shape* props]`.
  The `props` symbol is the wrapper's incoming JS-object; modern
  destructured components accept JS-object props at the call site
  via `[:>` so this works unchanged.

- `frontend/src/app/util/code_gen/markup_html.cljs` -
  `(mf/element text/text-shape #js {:shape shape :code? true})`
  becomes
  `(mf/element text/text-shape* #js {:shape shape :is-code true})`
  (component renamed and the `code?` JS key updated to match the
  renamed prop).

- `frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs`
  - `[:& html/text-shape {…}]` -> `[:> html/text-shape* {…}]`.

Behavior preserved verbatim
---------------------------

Same render output, same forward-ref forwarding semantics, same
recursive children-by-index keying, same default `:dir "auto"` on
`render-paragraph*`. The visible-prop changes are only the `code?`
-> `is-code` rename, all driven from this namespace and its single
caller in `markup_html.cljs`.

Github #9260

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
2026-05-07 15:03:51 +02:00
Xaviju
ddad228849 📚 Update CONTRIBUTING (#9418) 2026-05-07 14:13:02 +02:00
Madalena Melo
3136b39404 Update issue templates to include the issue type (#9345)
*  Update issue templates to include the issue type

Added the type "bug" to the "New render bug report" and the "Bug report" templates and the type "feature" to the "Feature request template".

This will allow us to use the issue Type instead of labels to identify what kind of issue is being created.

*  Update bug_report.md to request screen recordings

Update the Screenshots section to also request screen recordings

Signed-off-by: Madalena Melo <madalena.melo@kaleidos.net>

---------

Signed-off-by: Madalena Melo <madalena.melo@kaleidos.net>
2026-05-07 14:13:02 +02:00
Renzo
dd1ceae667 🐛 Fix plugin API fills/strokes arrays read-only (#9161)
* 🐛 Fix plugin API fills/strokes arrays read-only

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>

* 🐛 Support mutable plugin fill and stroke gradients

---------

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-07 14:13:02 +02:00
Juanfran
f79cfafae5 Show nitrate checkout error on subscription page
When the Stripe checkout fails to start, the subscription page now
  shows an inline error in the Business Nitrate card under the CTA
  instead of a toast. When the post-payment activation fails, the toast
  message is updated to point users to support@penpot.app.

  The nitrate-form modal also passed a URI object to
  build-nitrate-callback-urls while the underlying append-query-param
  relied on lambdaisland's u/parse, which only accepts strings. Switched
  to the local u/uri helper so both strings and URI records work, so
  failures opened from the modal land on the subscription page.
2026-05-07 14:13:02 +02:00
Xaviju
10a0e9e78c ♻️ Revert ESC keypress closes plugins (#9267) 2026-05-07 14:13:02 +02:00
Marina López
bc13dfcf9e Refactor subscriptions page 2026-05-07 14:13:02 +02:00
wdeveloper16
6e186143d5 ♻️ Migrate viewport debug and workspace shape debug components to modern syntax (#9395)
Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
2026-05-07 14:13:02 +02:00