21909 Commits

Author SHA1 Message Date
Elena Torro
5c4d16fc2b Coalesce live drag preview state and reduce sidebar churn 2026-05-07 14:13:02 +02:00
BitCompass
55d085117b ♻️ Rename measurement and svg-defs components to defc* form (#9306)
Adopts the rumext * suffix convention for the following components,
invoking them with the [:> JS-style syntax to match the rest of the
codebase (see e.g. rea*, single-selection* in viewport/selection):

- measurements: size-display, distance-display-pill, selection-rect,
  distance-display, selection-guides, measurement
- shapes/svg-defs: svg-node, svg-defs (also drop the now-redundant
  {::mf/wrap-props false} annotations)

Updates all call sites in inspect/selection_feedback, shapes/shape,
workspace/viewport, and workspace/viewport_wasm. Pure rename — no
behavioral change.

Signed-off-by: bitcompass <devwiz.sh@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-07 14:13:02 +02:00
Dexterity
7e6e7baa71 🔥 Remove stray prn debug log in stroke-row* render (#9318) 2026-05-07 14:13:02 +02:00
Dexterity
2fc4f35cde 💄 Fix typos in comments and docstrings (#9362) 2026-05-07 14:13:02 +02:00
Dexterity
5fd758597e 🐛 Fix MCP "active in another tab" notification not clearing (#9321) 2026-05-07 14:13:02 +02:00
Dexterity
cc29334684 🐛 Fix swapped analytics event names on MCP tab-switch dialog (#9322) 2026-05-07 14:13:02 +02:00
Milos Milic
e61d512889 🐛 Fix missing labels.open i18n key surfacing raw key as aria-label (#9320) 2026-05-07 14:13:02 +02:00
Xaviju
defeeab054
📚 Update CONTRIBUTING (#9418) 2026-05-07 14:01:43 +02:00
Aitor Moreno
9fccee8689 ♻️ Refactor how viewport interest area works 2026-05-07 13:46:51 +02:00
Francis Santiago
4f172afce5 🐳 Reuse Nginx security headers config
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-07 13:42:02 +02:00
Madalena Melo
df9cef1bb8
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 13:29:25 +02:00
Renzo
691679d90b
🐛 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 13:10:48 +02:00
Andrey Antukh
798ee46b4a 🐛 Bind MCP ReplServer to localhost to prevent unauthenticated RCE
The ReplServer Express app was calling `app.listen(port)` with no host
argument, causing Node/Express to default to binding on all interfaces
(0.0.0.0). Combined with the unauthenticated /execute endpoint, any
network peer could POST arbitrary JS and get it run inside the MCP
process.

Fix: add a `host` parameter (default "localhost") to the ReplServer
constructor and pass it to `app.listen`. The call site in
PenpotMcpServer now forwards `this.host` (sourced from
PENPOT_MCP_SERVER_HOST env var, default "localhost"), so environment-
variable overrides continue to work.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-07 12:59:31 +02:00
Juanfran
bd91036b95 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 12:48:43 +02:00
Xaviju
7b1f0eaaf0
♻️ Revert ESC keypress closes plugins (#9267) 2026-05-07 12:34:37 +02:00
Marina López
b2e3dbe558 Refactor subscriptions page 2026-05-07 12:06:46 +02:00
Alejandro Alonso
03487f90e5 🐛 Fix double-clicking a text element selected via Ctrl+click in nested layouts jumps to parent instead of entering edit mode 2026-05-07 09:57:15 +02:00
wdeveloper16
70e1a16bb8
♻️ Migrate viewport debug and workspace shape debug components to modern syntax (#9395)
Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
2026-05-07 08:44:09 +02:00
Dexterity
61b791368a
🐛 Remove stray println debug logs from dashboard team invitations (#9365) 2026-05-07 01:43:15 +02:00
tmimmanuel
f173fafb62
♻️ Migrate components/code-block to modern component syntax (#9384)
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-07 01:41:10 +02:00
tmimmanuel
eca487afc5
♻️ Migrate frame-preview to modern component syntax (#9382)
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-07 01:40:38 +02:00
tmimmanuel
bffec015d7
♻️ Migrate debug icons-preview to modern component syntax (#9381)
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-07 01:40:20 +02:00
Andrey Antukh
697a825d76 📚 Update opencode planner agent 2026-05-07 01:04:38 +02:00
Francis Santiago
50df7cb5c4 🐳 Harden Nginx security headers
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-06 20:06:35 +02:00
Alejandro Alonso
0a0db15548 Merge remote-tracking branch 'origin/staging' into develop 2026-05-06 19:28:09 +02:00
Alejandro Alonso
db1e2a9cfc
Merge pull request #9391 from penpot/superalex-fix-drag-crop-cache-perf
🐛 Avoid opaque fill check in drag crop cache hot path
2026-05-06 19:27:58 +02:00
Alejandro Alonso
33396df2e2 🐛 Avoid opaque fill check in drag crop cache hot path 2026-05-06 19:15:00 +02:00
BitCompass
3433b41aa8
♻️ Rename measurement and svg-defs components to defc* form (#9306)
Adopts the rumext * suffix convention for the following components,
invoking them with the [:> JS-style syntax to match the rest of the
codebase (see e.g. rea*, single-selection* in viewport/selection):

- measurements: size-display, distance-display-pill, selection-rect,
  distance-display, selection-guides, measurement
- shapes/svg-defs: svg-node, svg-defs (also drop the now-redundant
  {::mf/wrap-props false} annotations)

Updates all call sites in inspect/selection_feedback, shapes/shape,
workspace/viewport, and workspace/viewport_wasm. Pure rename — no
behavioral change.

Signed-off-by: bitcompass <devwiz.sh@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-06 19:11:45 +02:00
Dexterity
3885c9ee74
🔥 Remove stray prn debug log in stroke-row* render (#9318) 2026-05-06 17:43:58 +02:00
Dexterity
3226660812
💄 Fix typos in comments and docstrings (#9362) 2026-05-06 17:43:09 +02:00
Dexterity
db77780227 🐛 Fix MCP "active in another tab" notification not clearing (#9321) 2026-05-06 17:42:34 +02:00
Dexterity
a5b7bd90c7
🐛 Fix MCP "active in another tab" notification not clearing (#9321) 2026-05-06 17:42:06 +02:00
Dexterity
ae7c7a7972 🐛 Fix swapped analytics event names on MCP tab-switch dialog (#9322) 2026-05-06 17:41:16 +02:00
Dexterity
f4317d00e5
🐛 Fix swapped analytics event names on MCP tab-switch dialog (#9322) 2026-05-06 17:40:39 +02:00
Milos Milic
aa8f2ab80d
🐛 Fix missing labels.open i18n key surfacing raw key as aria-label (#9320) 2026-05-06 17:39:26 +02:00
Alejandro Alonso
c36887e0bf
Merge pull request #9357 from penpot/elenatorro-14100-cljs-optimizations
 Event optimizations on drag
2026-05-06 16:23:09 +02:00
Elena Torro
97511ba6e5 Translation-aware modifier propagation and lazy parent walks 2026-05-06 15:10:26 +02:00
Elena Torro
9230091492 Coalesce live drag preview state and reduce sidebar churn 2026-05-06 15:10:26 +02:00
Andrey Antukh
e07ad9cb53 Merge remote-tracking branch 'origin/staging' into develop 2026-05-06 15:07:45 +02:00
Andrey Antukh
14a0660352 Merge remote-tracking branch 'origin/main' into staging 2026-05-06 15:06:59 +02:00
FairyPiggyDev
4892799cf6
♻️ Migrate fontfaces and viewer thumbnails components to modern syntax (#9293)
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).

Two unrelated namespaces, both clean Case-A migrations grouped in a
single PR for review efficiency.

frontend/src/app/main/ui/shapes/text/fontfaces.cljs
---------------------------------------------------

Three components, all previously using `::mf/wrap-props false` with a
custom memoizer (`#(mf/memo' % (mf/check-props ["fonts"]))`) and
reading `fonts` via `(obj/get props "fonts")`. The custom memoizer
existed because the legacy components received raw JS-object props,
where the default `mf/memo` Clojure-equality comparison would always
fail.

- `fontfaces-style-html`   → `fontfaces-style-html*`
- `fontfaces-style-render` → `fontfaces-style-render*`
- `fontfaces-style`        → `fontfaces-style*`

Migration:

- Standard destructuring `[{:keys [fonts]}]` replaces the
  `[props]` + `(obj/get props "fonts")` pattern.
- `::mf/wrap-props false` removed.
- Custom memoizer collapses to `::mf/wrap [mf/memo]`. With modern
  destructuring the props are Clojure data, so default `=`-based memo
  is structurally correct (and a stronger guarantee than the previous
  shallow JS-prop check).
- `app.util.object` require dropped from the namespace — it was only
  used for the `obj/get props "fonts"` reads that are now gone.
- Internal call site of `fontfaces-style-render*` (within
  `fontfaces-style*`) keeps its `[:>` form, just with the new name.

External call sites updated:

- `frontend/src/app/main/render.cljs` — three sites
  (`[:& ff/fontfaces-style {:fonts fonts}]` × 3) →
  `[:> ff/fontfaces-style* {:fonts fonts}]`.
- `frontend/src/app/main/ui/workspace/shapes.cljs` — one site,
  call signature unchanged. (Note: this caller passes `:shapes`
  rather than `:fonts`; the legacy component already ignored
  `:shapes` because it only read `(obj/get props "fonts")`, so the
  modern destructuring `{:keys [fonts]}` preserves the same
  behavior. Pre-existing bug, intentionally left out of scope.)

frontend/src/app/main/ui/viewer/thumbnails.cljs
-----------------------------------------------

Four components, all using standard destructuring already (no
`::mf/wrap-props false`, no `unchecked-get`). Migration is the
straight `*` rename plus `?`-prop renames per the prompt's mapping:

- `thumbnails-content`  → `thumbnails-content*`   (`expanded?` →
  `is-expanded`)
- `thumbnails-summary`  → `thumbnails-summary*`   (no `?`-props)
- `thumbnail-item`      → `thumbnail-item*`       (`selected?` →
  `is-selected`; `::mf/wrap [mf/memo #(mf/deferred …)]` preserved)
- `thumbnails-panel`    → `thumbnails-panel*`     (`show?` →
  `show` — no `is-` prefix needed, reads naturally as a verb)

Internal callsites of all three sub-components in `thumbnails-panel*`
updated to `[:> …*` with renamed kwargs (`:expanded?` →
`:is-expanded`, `:selected?` → `:is-selected`).

The `expanded?` symbol still appears in `thumbnails-panel*`'s body —
it's a local `let`-binding deref'd from the `expanded-state` atom,
not a component param, so the `?` suffix is preserved per the
prompt's "local bindings stay" rule.

External call sites updated:

- `frontend/src/app/main/ui/viewer.cljs` — one site, plus the
  `:refer [thumbnails-panel]` → `:refer [thumbnails-panel*]` require
  update.

Github #9260

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-06 14:55:55 +02:00
Andrey Antukh
54928e9ffb Merge branch 'backport-2.14' 2026-05-06 14:38:17 +02:00
Andrey Antukh
df01f76056 🐛 Fix incorrect invitation token handling on register process (#9380)
* 🐛 Fix incorrect invitation token handling on register process

- Reject prepare-register-profile when an active profile already
  exists for the requested email.
- Stop embedding an existing profile's :profile-id into the
  prepared-register JWE. Profile resolution in register-profile is
  now done exclusively by email lookup, never by a JWE claim.
- Add created? guard to the invitation-success branch in
  register-profile, so existing profiles (active or not) cannot
  reach session creation via anonymous registration.

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

* ♻️ Restructure invitation handling inside register-profile

Move the invitation-success branch into the created? sub-cond so it
sits alongside the other post-creation branches, making the control
flow consistent.

- Active new profile + matching invitation: mint session and return
  :invitation-token (frontend redirects to :auth-verify-token).
- Not-yet-active new profile + matching invitation: embed the
  invitation token inside the verify-email JWE and send the
  verification email. When the user clicks the link, they get
  logged in and the frontend completes the team-invitation flow.
- Extend send-email-verification! with an optional invitation-token
  parameter propagated into the verify-email JWE claims.
- Update the frontend verify-email handler to navigate to
  :auth-verify-token when the response carries :invitation-token.

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

* 🐛 Handle email-already-exists error on registration form

Add a specific handler for the [:validation :email-already-exists] error
code in the registration form's on-error callback. The backend raises
this error when an active profile already exists for the requested email,
but the frontend was falling through to the generic error message.

Now it shows the existing "Email already used" i18n message instead of
the generic "Something wrong has happened" toast.

* 🐛 Reset submitted state on registration form error

The on-error handler in the registration form was not resetting the
submitted? state, causing the submit button to remain disabled after
any error. The completion callback in rx/subs! only fires on success,
not on error.

Add (reset! submitted? false) at the beginning of the on-error handler
so the form becomes submittable again after any error, allowing the user
to fix their input and retry.

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2.14.5
2026-05-06 14:32:31 +02:00
Aitor Moreno
4cd44efa93
Merge pull request #9371 from penpot/superalex-fix-drag-and-drop-cache-eligibility-rules
🐛 Fix drag and drop cache eligibility rules
2026-05-06 14:28:22 +02:00
Andrey Antukh
1e1ca82ba5 📚 Add missing changelog entry and document changelog locations
Add changelog entry for the fix-incorrect-invitation-token-handling
change (PR #9380) under `## 2.15.0 (Unreleased)` > `🐛 Bugs fixed`.
Add a `## Changelogs` section to AGENTS.md documenting both changelog
locations (main project: `CHANGES.md`, plugins: `plugins/CHANGELOG.md`).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2.15.0-RC4
2026-05-06 12:24:55 +00:00
Andrey Antukh
9e681260cc
🐛 Fix incorrect invitation token handling on register process (#9380)
* 🐛 Fix incorrect invitation token handling on register process

- Reject prepare-register-profile when an active profile already
  exists for the requested email.
- Stop embedding an existing profile's :profile-id into the
  prepared-register JWE. Profile resolution in register-profile is
  now done exclusively by email lookup, never by a JWE claim.
- Add created? guard to the invitation-success branch in
  register-profile, so existing profiles (active or not) cannot
  reach session creation via anonymous registration.

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

* ♻️ Restructure invitation handling inside register-profile

Move the invitation-success branch into the created? sub-cond so it
sits alongside the other post-creation branches, making the control
flow consistent.

- Active new profile + matching invitation: mint session and return
  :invitation-token (frontend redirects to :auth-verify-token).
- Not-yet-active new profile + matching invitation: embed the
  invitation token inside the verify-email JWE and send the
  verification email. When the user clicks the link, they get
  logged in and the frontend completes the team-invitation flow.
- Extend send-email-verification! with an optional invitation-token
  parameter propagated into the verify-email JWE claims.
- Update the frontend verify-email handler to navigate to
  :auth-verify-token when the response carries :invitation-token.

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

* 🐛 Handle email-already-exists error on registration form

Add a specific handler for the [:validation :email-already-exists] error
code in the registration form's on-error callback. The backend raises
this error when an active profile already exists for the requested email,
but the frontend was falling through to the generic error message.

Now it shows the existing "Email already used" i18n message instead of
the generic "Something wrong has happened" toast.

* 🐛 Reset submitted state on registration form error

The on-error handler in the registration form was not resetting the
submitted? state, causing the submit button to remain disabled after
any error. The completion callback in rx/subs! only fires on success,
not on error.

Add (reset! submitted? false) at the beginning of the on-error handler
so the form becomes submittable again after any error, allowing the user
to fix their input and retry.

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-06 14:20:55 +02:00
Pablo Alba
e8ac5f26db 💄 Fix nitrate change org combo style 2026-05-06 13:37:37 +02:00
Alejandro Alonso
708c4065b3 🐛 Fix drag and drop cache eligibility rules 2026-05-06 13:20:57 +02:00
Alejandro Alonso
9dd7835815 Merge remote-tracking branch 'origin/staging' into develop 2026-05-06 12:43:13 +02:00
María Valderrama
7efeed1348 🐛 Fix nitrate activation modal not opening 2026-05-06 12:41:19 +02:00