12887 Commits

Author SHA1 Message Date
Eva Marco
003b54421d
🐛 Fix empty warning on login (#9056) 2026-04-20 12:23:47 +02:00
Pablo Alba
ae66317d6c Add nitrate api to remove user from org 2026-04-20 11:18:07 +02:00
Dream
42ebee88d6
Add paste to replace (Cmd+Shift+V) (#9033)
Paste clipboard contents in place of the currently selected shape,
inheriting its position, parent, and z-index. The replaced shape
is deleted in the same transaction for a single undo step.

Signed-off-by: eureka0928 <meobius123@gmail.com>
2026-04-20 11:03:50 +02:00
Dream
f0c68fb826
Add search bar to color palette (#8994)
*  Add search bar to color palette

Fixes #7653

Signed-off-by: eureka0928 <meobius123@gmail.com>

* ♻️ Use search icon toggle for color palette search

Address UX feedback: replace always-visible search input with a
search icon that toggles the input on click. Hide the search
functionality when no colors exist. Move CHANGES.md entry to
2.16.0 section.

Signed-off-by: eureka0928 <meobius123@gmail.com>

---------

Signed-off-by: eureka0928 <meobius123@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-20 11:02:23 +02:00
wdeveloper16
d772632b08
Allow customising the OIDC login button label (#9026)
*  Allow customising the OIDC login button label (#7027)

* 📚 Add CHANGES entry and docs for PENPOT_OIDC_NAME (#7027)

---------

Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
2026-04-17 16:56:29 +02:00
Pablo Alba
c5a2b592a2 Move team to another nitrate organization 2026-04-17 11:38:52 +02:00
Pablo Alba
a206d57443 Add team to a nitrate organization 2026-04-17 11:38:52 +02:00
alonso.torres
bfa1ae051f 🐛 Fix problem with dashboard thumbnails images 2026-04-17 10:43:41 +02:00
Elena Torró
b74d920d03
Merge pull request #8980 from penpot/superalex-atlas-2
🎉 Tiles atlas support
2026-04-17 09:38:27 +02:00
Alejandro Alonso
fb1f55c13e
Merge pull request #9039 from penpot/alotor-fix-position-data-ff
🐛 Fix problem with position data in Firefox
2026-04-17 09:36:48 +02:00
Alejandro Alonso
8775e234f3 🎉 Waiting for tiles complete in a non blocking way 2026-04-17 09:17:26 +02:00
Alejandro Alonso
c08c3bd160 🎉 Tiles atlas support 2026-04-17 09:05:52 +02:00
Andrey Antukh
6fa440cf92 🎉 Add chunked upload API for large media and binary files
Introduce a purpose-agnostic three-step session-based upload API that
allows uploading large binary blobs (media files and .penpot imports)
without hitting multipart size limits.

Backend:
- Migration 0147: new `upload_session` table (profile_id, total_chunks,
  created_at) with indexes on profile_id and created_at.
- Three new RPC commands in media.clj:
    * `create-upload-session`  – allocates a session row; enforces
      `upload-sessions-per-profile` and `upload-chunks-per-session`
      quota limits (configurable in config.clj, defaults 5 / 20).
    * `upload-chunk`           – stores each slice as a storage object;
      validates chunk index bounds and profile ownership.
    * `assemble-file-media-object` – reassembles chunks via the shared
      `assemble-chunks!` helper and creates the final media object.
- `assemble-chunks!` is a public helper in media.clj shared by both
  `assemble-file-media-object` and `import-binfile`.
- `import-binfile` (binfile.clj): accepts an optional `upload-id` param;
  when provided, materialises the temp file from chunks instead of
  expecting an inline multipart body, removing the 200 MiB body limit
  on .penpot imports.  Schema updated with an `:and` validator requiring
  either `:file` or `:upload-id`.
- quotes.clj: new `upload-sessions-per-profile` quota check.
- Background GC task (`tasks/upload_session_gc.clj`): deletes stalled
  (never-completed) sessions older than 1 hour; scheduled daily at
  midnight via the cron system in main.clj.
- backend/AGENTS.md: document the background-task wiring pattern.

Frontend:
- New `app.main.data.uploads` namespace: generic `upload-blob-chunked`
  helper drives steps 1–2 (create session + upload all chunks with a
  concurrency cap of 2) and emits `{:session-id uuid}` for callers.
- `config.cljs`: expose `upload-chunk-size` (default 25 MiB, overridable
  via `penpotUploadChunkSize` global).
- `workspace/media.cljs`: blobs ≥ chunk-size go through the chunked path
  (`upload-blob-chunked` → `assemble-file-media-object`); smaller blobs
  use the existing direct `upload-file-media-object` path.
  `handle-media-error` simplified; `on-error` callback removed.
- `worker/import.cljs`: new `import-blob-via-upload` helper replaces the
  inline multipart approach for both binfile-v1 and binfile-v3 imports.
- `repo.cljs`: `:upload-chunk` derived as a `::multipart-upload`;
  `form-data?` removed from `import-binfile` (JSON params only).

Tests:
- Backend (rpc_media_test.clj): happy path, idempotency, permission
  isolation, invalid media type, missing chunks, session-not-found,
  chunk-index out-of-range, and quota-limit scenarios.
- Frontend (uploads_test.cljs): session creation and chunk-count
  correctness for `upload-blob-chunked`.
- Frontend (workspace_media_test.cljs): direct-upload path for small
  blobs, chunked path for large blobs, and chunk-count correctness for
  `process-blobs`.
- `helpers/http.cljs`: shared fetch-mock helpers (`install-fetch-mock!`,
  `make-json-response`, `make-transit-response`, `url->cmd`).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-16 19:43:57 +02:00
alonso.torres
47abe09cfe 🐛 Fix problem with position data in Firefox 2026-04-16 18:08:34 +02:00
Elena Torró
b02e05e23d
Merge pull request #8919 from penpot/alotor-fix-change-font-grow-text
🐛 Fix problem when changing font and grow text
2026-04-16 16:38:32 +02:00
Eva Marco
7f409eadd4
♻️ Update copy to be more specific (#9028) 2026-04-16 13:49:48 +02:00
Pablo Alba
39f4c13493
Add nitrate remove team from org 2026-04-16 11:46:05 +02:00
Pablo Alba
ac472c615a
🐛 Fix nitrate invitations org ux review 2026-04-16 11:18:11 +02:00
Andrey Antukh
b5922d32ca Merge remote-tracking branch 'origin/main' into staging 2026-04-16 10:59:36 +02:00
Dream
78381873eb
Edit ruler guide position by double-clicking the guide pill (#8987)
Drag-and-drop is the only way to move a ruler guide today, which makes
hitting an exact pixel painful. Double-clicking the guide pill now
swaps the position label for a numeric input — Enter commits, Escape
cancels — so users can type a precise value relative to the guide's
frame (or canvas).

Closes #2311

Signed-off-by: eureka0928 <meobius123@gmail.com>
2026-04-16 10:03:28 +02:00
Andrey Antukh
de27ea904d
Add minor adjustments to the auth events (#9027) 2026-04-16 09:59:45 +02:00
Andrey Antukh
f5271dabee
🐛 Fix error handling issues (#8962)
* 🚑 Fix RangeError from re-entrant error handling in errors.cljs

Two complementary changes to prevent 'RangeError: Maximum call stack
size exceeded' when an error fires while the potok store error pipeline
is still on the call stack:

1. Re-entrancy guard on on-error: a volatile flag (handling-error?)
   is set true for the duration of each on-error invocation. Any
   nested call (e.g. from a notification emit that itself throws) is
   suppressed with a console.error instead of recursing indefinitely.

2. Async notification in flash: the st/emit!(ntf/show ...) call is
   now wrapped in ts/schedule (setTimeout 0) so the notification event
   is pushed to the store on the next event-loop tick, outside the
   error-handler call stack. This matches the pattern already used by
   the :worker-error, :svg-parser and :comment-error handlers.

* 🐛 Add unit tests for app.main.errors

Test coverage for the error-handling module:

- stale-asset-error?: 6 cases covering keyword-constant and
  protocol-dispatch mismatch signatures, plus negative cases
- exception->error-data: plain JS Error, ex-info with/without :hint
- on-error dispatch: map errors routed via ptk/handle-error, JS
  exceptions wrapped into error-data before dispatch
- Re-entrancy guard: verifies that a second on-error call issued
  from within a handle-error method is suppressed (exactly one
  handler invocation)

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:04 +02:00
Aitor Moreno
1477758656 🐛 Fix pointer selection 2026-04-15 16:44:24 +02:00
Juanfran
3829443046 🐛 Skip onboarding modal for nitrate entry users 2026-04-15 16:32:58 +02:00
Elena Torró
b442ca2209
Merge pull request #8988 from penpot/alotor-improve-token-set-change
🐛 Improve change token set performance
2026-04-15 16:23:55 +02:00
alonso.torres
4d2d559383 🐛 Fix problem with finish render callback 2026-04-15 16:09:38 +02:00
alonso.torres
e3bafab529 🐛 Fix problem with resizes in plugins 2026-04-15 16:09:38 +02:00
alonso.torres
3f5226485b 🐛 Fix problem when changing font and grow text 2026-04-15 16:09:38 +02:00
Aitor Moreno
424b689dca 🐛 Fix mixed fills issues 2026-04-15 14:32:32 +02:00
Aitor Moreno
77b4d07d1f 🐛 Fix v3 text styles not being applied when inc/dec value 2026-04-15 14:32:32 +02:00
Aitor Moreno
6fd264051a 🐛 Fix v2/v3 wrong styling 2026-04-15 14:32:32 +02:00
Marina López
c10f945473 Add nitrate subscription expected cancel date 2026-04-15 13:42:04 +02:00
Juanfran
f5591ed22e 🐛 Forward email when adding user to Nitrate organization 2026-04-15 13:28:39 +02:00
Andrey Antukh
8f30a95ca0 🐛 Guard against nil variant-data in typography-item
When d/seek finds no matching font variant (e.g. the variant-id stored
on the typography no longer exists in the font's variants list),
variant-data is nil and (:name nil) produces nil, resulting in a
malformed label like "14px | ". Fall back to "--" in that case.
2026-04-15 12:27:18 +02:00
Andrey Antukh
e8547ab6dd 🐛 Pass on-finish-drag to harmony-selector in colorpicker
Without this, dragging the value/opacity sliders in the harmony tab
never called on-finish-drag, so the undo transaction started by
on-start-drag was never committed.
2026-04-15 12:27:18 +02:00
Andrey Antukh
628ce604c5 ♻️ Convert colorpicker and its sub-components to modern rumext * format
slider-selector (slider_selector.cljs):
- Rename to slider-selector*
- Rename prop vertical? to is-vertical
- Remove prop reverse? entirely: it was never passed by any callsite,
  so the related reversal logic in calculate-pos and handler positioning
  is also removed as dead code

value-saturation-selector (ramp.cljs):
- Rename to value-saturation-selector*
- Update internal call site to [:> value-saturation-selector* ...]
- Update slider-selector call sites to [:> slider-selector* ...]

harmony-selector (harmony.cljs):
- Rename to harmony-selector*
- Update slider-selector call sites to [:> slider-selector* ...] with
  renamed is-vertical prop
- Remove stale duplicate :vertical true prop
- Fix spurious extra wrapping vector around the opacity slider in the
  when branch

hsva-selector (hsva.cljs):
- Rename to hsva-selector*
- Update all four slider-selector call sites to [:> slider-selector* ...]
- Remove no-op :reverse? false prop from the value slider

color-inputs (color_inputs.cljs):
- Rename to color-inputs*

colorpicker.cljs:
- Update :refer imports for color-inputs*, harmony-selector*,
  hsva-selector* and libraries*
- Update all corresponding call sites from [:& ...] to [:> ...]
2026-04-15 12:27:18 +02:00
Andrey Antukh
90d052464f ♻️ Convert text-palette components to modern * format
Convert typography-item, palette and text-palette to typography-item*,
palette* and text-palette* using {:keys [...]} destructuring. Rename
prop name-only? to is-name-only in typography-item*. Update internal
call sites to [:> ...] and update the :refer import in palette.cljs.
2026-04-15 12:27:18 +02:00
Andrey Antukh
fbee875d75 ♻️ Convert active-sessions to modern * component format
Convert active-sessions to active-sessions* (zero-prop component).
Update call site in right_header.cljs to use [:> ...] and update the
:refer import accordingly.
2026-04-15 12:27:18 +02:00
Andrey Antukh
bf7c12ae75 ♻️ Convert coordinates to modern * component format
Convert coordinates to coordinates* using {:keys [...]} destructuring
and rename prop colorpalette? to is-colorpalette. Update call site in
workspace.cljs to use [:> ...] with new prop name.
2026-04-15 12:27:18 +02:00
Andrey Antukh
175f122a0f ♻️ Convert viewport-scrollbars to modern * component format
Convert viewport-scrollbars to viewport-scrollbars* using {:keys [...]}
destructuring and update call sites in viewport.cljs and viewport_wasm.cljs
to use [:> ...].
2026-04-15 12:27:18 +02:00
Andrey Antukh
b2f4e90a79 ♻️ Convert shape-distance-segment to modern * component format
Convert shape-distance-segment to shape-distance-segment* using {:keys [...]}
destructuring and update its internal call site in shape-distance to use [:> ...].
2026-04-15 12:27:18 +02:00
Andrey Antukh
b4ec0a6d55 🐛 Add missing zoom and page-id dep on snap-feedback use-effect 2026-04-15 12:27:18 +02:00
Xaviju
431056404c
🎉 Save tokens tree state in local storage (#8922) 2026-04-15 11:24:01 +02:00
alonso.torres
988c277e37 🐛 Post-review enhancements 2026-04-15 09:53:36 +02:00
alonso.torres
1d8299a919 🐛 Fix problem with component thumbnails 2026-04-15 09:53:36 +02:00
Eva Marco
b0caa15516
🎉 Add test to bug (#8928) 2026-04-15 09:16:16 +02:00
Marina López
c63b9583a2 Add callback to nitrate billing url 2026-04-15 08:53:28 +02:00
Andrés Moya
a3ea9fbecb
🔧 Add more validations for components, to avoid some crashes (#7820)
* 🔧 Validate only after propagation in tests

* 💄 Enhance some component sync traces

* 🔧 Add fake uuid generator for debugging

* 🐛 Remove old feature of advancing references when reset changes

Since long time ago, we only allow to reset changes in the top copy
shape. In this case the near and the remote shapes are the same, so
the advance-ref has no effect.

* 🐛 Fix some bugs and add validations, repair and migrations

Also added several utilities to debug and to create scripts that
processes files

* 🐛 Fix misplaced parenthesis passing propagate-fn to wrong function

The :propagate-fn keyword argument was incorrectly placed inside the
ths/get-shape call instead of being passed to tho/reset-overrides.
This caused reset-overrides to never propagate component changes,
making the test not validate what it intended.

* 🐛 Accept and forward :include-deleted? in find-near-match

Callers were passing :include-deleted? true but the parameter was not
in the destructuring, so it was silently ignored and the function
always hardcoded true. This made the API misleading and would cause
incorrect behavior if called with :include-deleted? false.

* 💄 Use set/union alias instead of fully-qualified clojure.set/union

The namespace already requires [clojure.set :as set], so use the alias
for consistency.

* 🐛 Add tests for reset-overrides with and without propagate-fn

Add two focused tests to comp_reset_test to cover the propagate-fn
path in reset-overrides:

- test-reset-with-propagation-updates-copies: verifies that resetting
  an override on a nested copy inside a main and supplying propagate-fn
  causes the canonical color to appear in all downstream copies.

- test-reset-without-propagation-does-not-update-copies: regression
  guard for the misplaced-parenthesis bug; confirms that omitting
  propagate-fn leaves copies with the overridden value because the
  component sync never runs.

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 08:42:42 +02:00
Xaviju
909427d442
♻️ Improve import/export warning semantics (#8991) 2026-04-14 22:41:47 +02:00
Dream
dfec9004bf
Add delete and duplicate buttons to typography dialog (#8983)
*  Add delete and duplicate buttons to typography dialog

Add delete and duplicate action buttons to the expanded typography
editing panel, allowing users to quickly manage typographies without
needing to close the panel and use the context menu.

Fixes #5270

* ♻️ Use DS icon-button for typography dialog actions

Address review feedback: replace raw `:button`/`:div` elements and
deprecated-icon usage with the design system `icon-button*` and
non-deprecated icons (`i/add`, `i/delete`, `i/tick`).

* ♻️ Only show typography delete/duplicate buttons in assets sidebar

`typography-entry` is reused from the right sidebar text options
panel, where the delete and duplicate actions don't make sense.
Add an `is-asset?` opt-in prop and gate the `on-delete`/`on-duplicate`
handlers behind it, so the buttons only appear when the entry is
rendered from the assets sidebar.

* ♻️ Move typography delete/duplicate handlers next to their use site

Refine the previous opt-in: instead of plumbing on-delete/on-duplicate
function props through typography-entry, build them directly inside
typography-advanced-options where they're actually rendered. The
component now takes :file-id and :is-asset? and gates the action
buttons on a single `show-actions?` flag.

---------

Signed-off-by: eureka0928 <meobius123@gmail.com>
2026-04-14 21:32:56 +02:00