6536 Commits

Author SHA1 Message Date
Elena Torro
374c64da74 🐛 Fix text focus on empty text 2026-05-14 07:12:30 +02:00
Alonso Torres
c56f5cc01b
🐛 Fix problem with colorpicker in multiselect color with texts (#9549) 2026-05-13 15:57:30 +02:00
Alonso Torres
c65b24495b
🐛 Fix several issues with color picker (#9558) 2026-05-13 15:54:05 +02:00
Luis de Dios
b125c2cabb
🐛 Fix remove resize cursor CSS for inputs (#9590)
* 🐛 Remove cursor CSS for all inputs

- Restore the default cursor for the dashboard inputs.
- Make the numeric-input component from DS to work as expected.

* 🐛 Fix remove drag-to-change behaviour from old numeric-input
2026-05-13 15:51:24 +02:00
Andrey Antukh
947f6d392d
🎉 Add chunked upload support for font variants (#9551)
*  Add additional logging and validation for image upload

* 🎉 Add chunked upload support for font variants

Extend the font variant upload flow across frontend, backend, and common
to support the standardized chunked upload protocol.

**Backend:**
- Add \`:font-max-file-size\` config default (30 MiB) and schema entry
- Add \`validate-font-size!\` in \`media.clj\` (mirrors
  \`validate-media-size!\`, raises \`:font-max-file-size-reached\`)
- Extend \`schema:create-font-variant\` to accept either \`:data\`
  (legacy bytes or chunk-vector) or \`:uploads\` (new chunked session
  map), with a validator requiring exactly one
- Add \`prepare-font-data-from-uploads\`: assembles each chunked
  session via \`cmedia/assemble-chunks\`, validates type+size
- Add \`prepare-font-data-from-legacy\`: normalises legacy byte/chunk
  entries, writing to a tempfile (joining via SequenceInputStream),
  validates type+size
- Add structured logging ("init"/"end") with \`:size\`, \`:mtypes\`,
  and \`:elapsed\` in \`create-font-variant\`

**Frontend:**
- \`upload-blob-chunked\` accepts a per-caller \`:chunk-size\` option
- Add \`font-upload-chunk-size\` (10 MiB) and \`upload-font-variant\`
  fn that uploads each mtype as a separate chunked session
- \`on-upload*\` in dashboard fonts now calls \`upload-font-variant\`
  instead of issuing \`create-font-variant\` RPC directly
- \`process-upload\` stores raw ArrayBuffer instead of chunking
  client-side

**Common:**
- Replace \`"font/opentype"\` with \`"font/woff2"\` in \`font-types\`

**Tests:**
- 25 tests / 224 assertions covering all three upload paths (direct
  bytes, legacy chunk-vector, new chunked sessions), size validation,
  and media type validation

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

* 📎 Add a script for check the commit format locally

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-12 18:30:19 +02:00
Alejandro Alonso
9ebf7875ea 🐛 Fix WASM viewport interaction lifecycle for pan/zoom 2026-05-12 14:13:18 +02:00
Elena Torro
847d55bfb4 🐛 Update component thumbnails to match current shape 2026-05-12 12:21:06 +02:00
Luis de Dios
f62a89dcc2
🐛 Fix font selection position is hiding fonts (#9499) 2026-05-12 10:25:16 +02:00
Elena Torro
463017b5c9 🐛 Fix blur on page transition 2026-05-12 09:47:00 +02:00
Jack Storment
c394a281c8
🐛 Revert blend-mode hover preview when dismissing dropdown (#9237)
* 🐛 Revert blend-mode hover preview when dismissing dropdown

When the blend-mode dropdown was dismissed by clicking outside instead
of selecting an option, the canvas kept rendering the last hovered
blend mode even though the inspector and data state had reverted. The
visible state of the shape no longer matched its stored state. Reset
the canvas render back to the shape's saved blend mode on dropdown
close so the preview never outlives the dropdown.

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

* Fix blend-mode dropdown and various bugs

Fix blend-mode dropdown behavior and revert WASM render on pointer leave. Also, address multiple bugs related to user interactions and data handling.

Signed-off-by: Alejandro Alonso <alejandro.alonso@kaleidos.net>

---------

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Signed-off-by: Jack Storment <88656337+jack-stormentswe@users.noreply.github.com>
Co-authored-by: Alejandro Alonso <alejandro.alonso@kaleidos.net>
2026-05-12 08:17:07 +02:00
FairyPiggyDev
09bd7f96f6
Add author, relative timestamp and short identifier to history entries (#9132)
The workspace Actions history panel previously showed the operation
icon and a one-line message for each undo entry with no indication
of when the action happened, any stable way to refer to it, or who
made the change. The reporter of issue #7660 (and @Takhoffman's
follow-up comment) asked for a git-like display: `<hash> · <time> by
<name>`.

This change stamps each undo entry with its creation timestamp and
author at the moment it lands on the undo stack and surfaces three
extra pieces of information in the history sidebar:

- A short 7-character identifier derived from the entry's existing
  `:undo-group` UUID. Hovering shows the full UUID.
- A relative timestamp (e.g. `just now`, `5 minutes ago`, `2 hours
  ago`, `3 days ago`) rendered via `app.common.time/timeago` so it
  matches the formatting already used for comments and the dashboard.
- The display name of the profile that created the entry, rendered
  as `by <Name>` in the same metadata row.

The undo stack is client-side per profile, so every entry is always
the current user; the author is stored on the entry anyway so the UI
does not need to reach into profile state while rendering and so the
data stays correct if the stack shape ever changes.

Changes at a glance:

- `data/workspace/undo.cljs`: extend `schema:undo-entry` with an
  optional `:timestamp` and `:by`; new `profile-display-name` helper
  that falls back from full name to email to nil; `stamp-entry` now
  takes state and fills in both fields on entries that do not
  already carry them. Pre-stamped entries (e.g. coming out of an
  accumulated transaction) keep their original values.
- `ui/workspace/sidebar/history.cljs`: propagate `:timestamp`,
  `:undo-group`, and `:by` through `parse-entries`; add `short-id`
  helper; render the metadata row in `history-entry` using
  `app.common.time/timeago` against `:timestamp`; skip the author
  span entirely when `:by` is nil.
- `ui/workspace/sidebar/history.scss`: styling for the new metadata
  row (monospace hash, muted separator, truncated time/author).
- `translations/en.po`: 1 new string for `by %s`.

Existing undo entries created before this change have neither
timestamp nor author; the UI is defensive about both, so old entries
simply render with whatever data they have (and often the plain
title on its own).

Github #7660

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Signed-off-by: FairyPiggyDev <luislee3108@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 14:48:32 +02:00
jony376
f7fbd3007e
🐛 Prevent viewers from overwriting file thumbnails (#9285)
* 🐛 Prevent viewers from overwriting file thumbnails

* 🐛 Fix message

---------

Co-authored-by: jony376 <jony376@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 14:38:40 +02:00
Andrey Antukh
06986e25a3 Merge remote-tracking branch 'origin/staging' into develop 2026-05-11 14:06:31 +02:00
Belén Albeza
0639ca53de
Implement WebGL context restoring (#9317)
*  Implement asset re-uploading to wasm

*  Show toast instead of error screen when webgl context is lost

* 🎉 Recover context after webgl context restored event

* 🎉 Set Read-only mode when the context has been lost

*  Disable scroll & zoom when context loss

*  Fix stale reload payload

*  Use existing debounce util to take screenshots

*  Implement design / ux specs

*  Fix playwright test by looking for toast, not error page
2026-05-11 13:15:45 +02:00
Andrey Antukh
a228b257e9 Merge remote-tracking branch 'origin/staging' into develop 2026-05-11 09:37:32 +02:00
Andrey Antukh
f2c631b8b7 Merge remote-tracking branch 'origin/main-staging' into staging 2026-05-11 09:30:10 +02:00
Andrey Antukh
1a212a2769 Merge remote-tracking branch 'origin/main-staging' 2026-05-11 08:46:25 +02:00
Alonso Torres
9f05ba2fdf
Add plugins and mcp event data (#9228)
*  Add plugins and mcp event data

* ♻️ Changed data-event ::ev/event to ev/event
2026-05-11 08:36:53 +02:00
andrés gonzález
9c771ae6b9
🐛 Fix MCP integrations copy button to match displayed URL (#9239) 2026-05-10 19:23:03 +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
60c718eba1 Merge remote-tracking branch 'origin/staging' into develop 2026-05-10 09:20:27 +02:00
Andrey Antukh
10a23a6869 Merge remote-tracking branch 'origin/main' into staging 2026-05-10 09:16:41 +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
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
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
Eva Marco
cccd7bc6de
🐛 Fix pixel grid color row (#9360) 2026-05-08 11:06:56 +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
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
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
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
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
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
Alejandro Alonso
0a0db15548 Merge remote-tracking branch 'origin/staging' into develop 2026-05-06 19:28:09 +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
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