Compare commits

...
3646 Commits
Author SHA1 Message Date
Andrés Moya 5042a34e3c 🔧 Normalize text nodes comparison, to be used in tokens detach
🔧 Add more tests for all cases and fix text token application in tests
2026-06-23 12:24:30 +02:00
Andrey Antukh d8434cbffb 🐛 Add missing migrations (#10363) 2026-06-22 13:26:21 +02:00
Alejandro Alonso 3a9be0b1d8 Merge pull request #10358 from penpot/niwinz-bugfix-1
🐛 Fix incorrect events handling on webgl render toggle
2026-06-22 12:54:13 +02:00
Andrey Antukh 3b9a895f62 🐛 Fix incorrect events handling on webgl render toggle
From the workspace main menu
2026-06-22 11:29:57 +02:00
Andrey Antukh 960b90253f ⬆️ Update root deps 2026-06-22 09:04:13 +02:00
Andrey Antukh d8894abcbc 📎 Update changelog 2026-06-22 09:04:01 +02:00
Andrey Antukh d3bec95860 🐛 Allow pasting comma-separated emails in multi-input (#10186)
The multi-input component did not handle paste events for
comma-separated values. When users pasted emails like
'qa@example.com, test@example.com', the entire string was
inserted as-is, triggering validation errors.

The on-key-down handler already split text on commas/spaces
when typing, but paste events bypassed this logic.

Added an on-paste handler that:
- Detects if pasted text contains commas or whitespace
- Splits the text by commas and/or whitespace
- Validates each part individually
- Adds valid items to the items list
- Prevents default paste behavior
- Resets input state after processing

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-19 09:38:00 +02:00
196e47fa93 Backport MCP related changes from develop (#10315)
* ♻️ Add mcp integration state management refactor (#10226)

* ♻️ Add mcp integration state management refactor

* 🐛 Fix access tokens do not appear

* ♻️ Refactor some names

* ♻️ Refactor token deletion

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>

* 🐛 Fix stale MCP token data after create/regenerate (#10280)

Fix the root cause in profile.cljs: remove the optimistic conj from
access-token-created and instead chain a fetch-access-tokens after the
create-access-token API call succeeds. This ensures all callers get a
fresh, server-consistent token list automatically.

Suggested-by: niwinz

Signed-off-by: kapilvus <kapil69265@gmail.com>
Co-authored-by: kapilvus <kapilvus@gmail.com>

*  Remove non-recoverable mcp key warning from regenerated modal (#10298)

---------

Signed-off-by: kapilvus <kapil69265@gmail.com>
Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
Co-authored-by: kapil971390 <kapil69265@gmail.com>
Co-authored-by: kapilvus <kapilvus@gmail.com>
2026-06-18 18:08:40 +02:00
Andrey Antukhanddeepseek-v4-flash 71f5c11a11 Qualify MCP Redis channel names with tenant prefix
Read PENPOT_TENANT env var (defaulting to "default") and embed it in
Redis Pub/Sub channel names as penpot.mcp.<tenant>.task.{req,res}.<id>.

This prevents cross-tenant interference when multiple environments share
a Redis instance, matching the backend convention
(e.g. penpot.rlimit.<tenant>.window.<name> in app.rpc.rlimit).

Co-authored-by: deepseek-v4-flash <deepseek-v4-flash@penpot.app>
2026-06-18 09:22:29 +02:00
Eva Marco 895c9cb8da 🐛 Fix tokens fonts combobox to show resolved value 2026-06-17 21:18:08 +02:00
Andrey Antukh a2e69db265 ⬆️ Update deps 2026-06-17 21:18:08 +02:00
Andrey Antukh fe9598f96c Merge remote-tracking branch 'origin/main' into staging 2026-06-17 19:13:16 +02:00
Andrey Antukh 594bbf9dd6 📎 Update pr and commits worflow on serena 2026-06-17 19:12:26 +02:00
Andrey Antukh cc4cdff729 🔥 Remove duplicated github workflow 2026-06-17 00:05:07 +02:00
Andrey Antukh b645f51486 Backport .github directory from develop 2026-06-17 00:03:25 +02:00
Andrey Antukh 0338655cd0 📎 Add frontend pnpm-lock.yaml dedup 2026-06-17 00:02:07 +02:00
Andrey Antukh c9f9bd5029 📎 Use same playwright version on all frontend subpackages 2026-06-16 23:22:19 +02:00
andrés gonzález 565e173918 📚 Update troubleshooting WebGL doc (#10233) 2026-06-16 16:03:14 +02:00
Andrey Antukh 3dff9b8f28 Merge remote-tracking branch 'origin/main' into staging 2026-06-16 13:07:40 +02:00
Alejandro Alonso 92c9517322 Merge pull request #10184 from penpot/azazeln28-fix-frame-stroke
🐛 Board stroke disappears when a layer is placed inside
2026-06-16 09:32:14 +02:00
Aitor Moreno c54ab3bd34 🐛 Fix frame stroke 2026-06-15 16:09:23 +02:00
Andrey Antukh 61c52a665d ⬆️ Update dependencies (#10166)
* ⬆️ Update exporter dependencies

* ⬆️ Update frontend dependencies

* ⬆️ Update nodejs version devenv and docker images
2026-06-15 12:03:59 +02:00
Andrey Antukh 79227c4de8 Batch multiple thumbnail deletions into a single RPC call (#9943)
*  Batch multiple thumbnail deletions into a single RPC call

Replace the old per-object immediate thumbnail deletion with a
debounced batched approach. The frontend queues object-ids in state
and waits 200ms before sending a single RPC request with up to 200
object-ids. The backend deletes all matching thumbnails in one SQL
statement with a single RETURNING clause, then touches the affected
media objects.

This reduces RPC overhead when rapidly clearing thumbnails (e.g.
navigating pages) and makes deletions more efficient.

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

* 📎 Fix missing issues

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-15 12:01:32 +02:00
Juan de la CruzandLuis de Dios ddeaf3ce2a Add MCP status button to workspace toolbar with single-tab connection control (#9930)
*  Add MCP connection badge to the workspace toolbar

*  Add MCP status button with single-tab connection control

* ♻️ Extract component for MCP indicator in the toolbar

* ♻️ Some improvements

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-06-15 11:29:25 +02:00
David Barragán Merino 6a1c3348f3 🐳 Clean apt package cache in all Dockerfiles 2026-06-15 11:13:12 +02:00
David Barragán Merino fe86359dc8 🐳 Migrate storybook image to alpine 2026-06-15 11:13:12 +02:00
David Barragán Merino 572b094bab 🐳 Migrate frontend image to alpine 2026-06-15 11:13:12 +02:00
Luis de Dios 739a2d4958 💄 Update translations for company size options in in-app onboarding (#10164) 2026-06-15 10:57:08 +02:00
Andrey Antukh a8d0c18c1b 🐛 Fix race condition between MCP initialization and plugin runtime (#10137)
* 🐛 Fix race condition between MCP init and plugin runtime

Add promise-based synchronization to ensure MCP initialization waits
for plugin runtime to be ready before calling global.ɵloadPlugin.

- Add runtime-ready-promise in app.plugins that resolves when
  init-plugins-runtime completes
- Add wait-for-runtime function for other modules to await readiness
- MCP init now waits for runtime via rx/from before starting plugin
- Add defensive guards in start-plugin!, load-plugin!, close-plugin!
  to check if plugin APIs exist before calling
- Rename init-plugins-runtime! to init-plugins-runtime

Fixes: global.ɵloadPlugin is not a function error when MCP plugin
starts before async plugin runtime initialization completes.

* 📎 Add 'create-pr' opencode skill
2026-06-12 11:40:02 +02:00
David Barragán Merino 7cb7f7adb2 🐳 Add command to upgrade packages during the creation 2026-06-12 09:34:26 +02:00
Justin Lin 9c7af3aeb5 📚 Add WebSocket proxy configuration for MCP in Nginx example (#10152)
The current Nginx example doesn't include the required Websocket settings to correctly proxy /mcp/ws, and leads to WebSocket connection error when trying to connect in a design. Adding this lines fixed the issue.

Signed-off-by: Justin Lin <30039756+lancatlin@users.noreply.github.com>
2026-06-12 08:55:23 +02:00
Andrey Antukh 2e1839f898 🐛 Fix NotReadableError in rasterizer during thumbnail generation
The rasterizer's create-image function was clearing image.src in its
Rx teardown cleanup. This caused the decoded pixel data to be discarded
before downstream operators (drawImage / createImageBitmap) could read
it, resulting in a browser NotReadableError.

Changes:
- Remove image.src = "" from cleanup; the image element will be
  garbage collected naturally. Event handler nulling is kept to break
  circular references.
- Add dimension validation in svg-get-adjusted-size to return nil for
  zero/NaN dimensions instead of producing invalid sizes.
- Add fallback in svg-set-intrinsic-size! to use [max max] when SVG
  dimensions can't be determined.

Error occurred in production (2.16.0-RC10) during thumbnail generation
in the workspace.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-11 17:04:24 +02:00
Andrey Antukh 5f21ebd08d 🐛 Filter ignorable React removeChild errors at error boundary and fix HTML anti-pattern (#10145)
* 🐛 Filter ignorable exceptions in error-boundary onError callback

The global uncaught-error-handler already skips NotFoundError/removeChild
and other harmless errors, but react-error-boundary's onError callback fires
independently of the window.onerror pipeline. This means the error boundary
was still logging these errors and setting last-exception, causing them to
continue appearing in error reports despite being non-actionable.

Add the is-ignorable-exception? check to the error-boundary* onError so
harmless errors are silently ignored, matching the behavior of the global
handler.

* 🐛 Fix dangerouslySetInnerHTML anti-pattern in context-notification

The previous code used dangerouslySetInnerHTML on the same element that
could also contain React children. This is a React anti-pattern that can
cause reconciliation mismatches and lead to removeChild DOMExceptions.

Refactor to use two separate element branches: one for raw HTML injection
and one for normal React children with links.
2026-06-11 16:59:00 +02:00
David Barragán Merino b2c7854f5e 🐳 Pin docker images to 2.16 2026-06-11 16:48:24 +02:00
David Barragán Merino ab614d2ae9 🐳 Pin docker images to 2.16 2026-06-11 16:47:35 +02:00
David Barragán Merino 11096f1829 🔧 Remove the confirmation step for publishing docker images 2026-06-11 16:26:36 +02:00
David Barragán Merino b1ec03a5d2 🔧 Remove the confirmation step for publishing docker images 2026-06-11 16:26:18 +02:00
Andrey Antukh 715bd1c09e 📎 Update mcp server api_types.yml file 2026-06-11 14:51:02 +02:00
Andrey Antukh 6827c4554b 📎 Update version on mcp package.json 2026-06-11 14:49:48 +02:00
Andrey Antukh 8b16326334 📎 Update changelog 2026-06-11 14:49:09 +02:00
Andrey Antukh 86d508eaf1 Merge remote-tracking branch 'origin/main' into staging 2026-06-11 13:24:57 +02:00
Luis de Dios 331e66c1c6 Update company size options in in-app onboarding (#10110) 2026-06-11 13:23:41 +02:00
Alejandro Alonso 38d12b3363 Add hard reload path for renderer menu A/B test (#10133) 2026-06-11 13:16:09 +02:00
Andrey Antukh 935fd85e9c 🐛 Fix exporter deps install script 2026-06-11 12:42:43 +02:00
Andrey Antukh 3a386c0ee6 Merge remote-tracking branch 'origin/main' into staging 2026-06-11 11:56:05 +02:00
andrés gonzález a8c1a673a8 📚 Add MCP server block and setup videos to docs (#10121) 2026-06-11 11:46:40 +02:00
Andrey Antukh d65e8317e3 📎 Update changelog 2026-06-11 10:41:05 +02:00
Andrey Antukh 99bf493030 📎 Update the update-changelog skill 2026-06-11 10:40:03 +02:00
Andrey Antukh 3377473f05 Merge remote-tracking branch 'origin/main' into staging 2026-06-11 10:12:12 +02:00
Andrey Antukh ba9b03268a ⬆️ Update backend and common dependencies (#10108)
* 🐛 Fix incorrect valkey uri on backend tests

* ⬆️ Update backend dependencies

* ⬆️ Update pnpm dependencies

* 📎 Fix minor linter issues
2026-06-11 10:11:23 +02:00
Aitor Moreno 17b38e3e6b Merge pull request #10123 from penpot/superalex-fix-wasm-selected-text-hover-double-click
🐛 Keep hover on selected text layers in WASM renderer
2026-06-11 09:47:50 +02:00
Belén Albeza 8eedd951a8 🐛 Fix mistmatch in mapping custom font variants (wasm) (#10122) 2026-06-11 09:36:29 +02:00
Alejandro Alonso 435cdcb1d4 🐛 Keep hover on selected text layers in WASM renderer 2026-06-11 09:23:03 +02:00
Andrey Antukh d1fb1d34a1 🐛 Fix email validation to reject consecutive dots in domain (#10096)
* 🐛 Tighten email validation regex to reject consecutive dots in domain

* 📎 Add minor adjustments to gh-issue-from-pr skill
2026-06-10 19:21:51 +02:00
Andrey Antukh fa0531cd28 Merge remote-tracking branch 'origin/main' into staging 2026-06-10 11:26:14 +02:00
Andrey Antukh 3419f7e60a ⬆️ Update root repo deps 2026-06-10 11:25:59 +02:00
Andrey Antukh 0ac092d177 🐛 Make set-file-shared idempotent to fix race condition with optimistic updates (#10093) 2026-06-10 10:58:50 +02:00
Alejandro Alonso becba0a82b 🐛 Fix frame inner strokes ignoring focus mode in wasm renderer 2026-06-10 09:36:28 +02:00
Andrey Antukh f7c5ce7ac9 Merge remote-tracking branch 'origin/main' into staging 2026-06-09 14:38:48 +02:00
Andrey Antukh e0a44eede0 Backport serena memory and other minor config fixes from develop 2026-06-09 14:37:59 +02:00
David Barragán Merino 82acec1191 📎 Set pnpm version on docs/package.json 2026-06-09 13:50:00 +02:00
David Barragán Merino 11a8d08f95 📎 Set pnpm version on docs/package.json 2026-06-09 13:28:54 +02:00
Andrey Antukh 27ba1ffbe0 📎 Update version on mcp/package.json 2026-06-08 14:38:47 +02:00
Andrey Antukh c7fae1f353 Merge remote-tracking branch 'origin/main' into staging 2026-06-08 14:36:24 +02:00
Andrey Antukh 51a9eed02e Backport from develop AGENTS.md changes 2026-06-08 14:35:19 +02:00
Andrey Antukh 0e16db66b8 Backport from develop AGENTS.md changes 2026-06-08 14:34:31 +02:00
Andrey Antukh eff533374d 🐛 Ignore Safari browser extension errors in error handler
Add detection for Safari's webkit-masked-url:// extension URLs and filter
the "Attempting to change value of a readonly property" TypeError to prevent
Safari browser extension errors from being surfaced to users.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-08 14:32:01 +02:00
Andrey Antukh 82cfbedc26 Merge remote-tracking branch 'origin/main' into staging 2026-06-08 14:28:30 +02:00
Andrey Antukh c2f2e0e34b 📎 Add opencode issue-title skill 2026-06-08 14:27:07 +02:00
Andrey Antukh a326cc416e Backport github issue templates from develop 2026-06-08 14:26:45 +02:00
Alonso Torres 6808390827 🐛 Fix problem with color picker error (#10056) 2026-06-08 13:25:45 +02:00
Andrey Antukh 5426092d68 📚 Remove the requirement of changelog update 2026-06-08 12:02:28 +02:00
Andrey Antukh 4d0a3efc5c 🐛 Fix plugin API crash when setting text fills (#10051)
The `update-text-range` event's `watch` method was returning a bare
potok event object (`dwwt/resize-wasm-text-debounce id`) directly
inside `rx/concat`, instead of wrapping it in `rx/of`. This caused
RxJS to throw "You provided an invalid object where a stream was
expected" when a plugin set text fills via the Plugin API.

The fix wraps the event in `rx/of` so it becomes a valid Observable,
matching the pattern used elsewhere in the codebase (e.g.,
`clipboard.cljs` lines 1050/1082 and `texts.cljs` line 1232).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-08 11:33:36 +02:00
Andrey Antukh 4f852e33bf Backport mcp package changes from develop 2026-06-08 09:59:33 +02:00
Andrey Antukh bae4d23c67 Merge remote-tracking branch 'origin/staging' 2026-06-08 09:40:28 +02:00
Andrey Antukh c5bd583b1f 📎 Update root deps 2026-06-08 09:39:59 +02:00
Andrey Antukh 4755ebbedf Merge remote-tracking branch 'origin/main' into staging 2026-06-05 11:49:18 +02:00
Andrey Antukh 2ad63d8887 📎 Backport .opencode directory fron develop 2026-06-05 11:49:06 +02:00
Andrey Antukh 7736104daa Merge remote-tracking branch 'origin/main' into staging 2026-06-05 11:44:36 +02:00
Andrey Antukh f457c68355 📎 Backport devenv improvements 2026-06-05 11:44:20 +02:00
Elena Torró 47ce68eed0 🐛 Fix masked group applied blur and bounds (#10028) 2026-06-05 11:01:47 +02:00
Andrey Antukh 6d77ca3fc1 Merge remote-tracking branch 'origin/main' into staging 2026-06-04 19:01:25 +02:00
David Barragán Merino 97c3a9facf 🐳 Add improvements related to Docker and Podman compatibility (#10012)
* 📎 Add tests for boolean parser coverage

* 🐳 Normalize boolean handling in nginx entrypoint

* 🐳 Quote boolean env vars in compose example (add Podman compatibility)

* 🔥 Remove deprecated and duplicated nginx.conf file for Storybook
2026-06-04 10:11:58 +02:00
Elena Torró dfa88a28fd 🐛 Fix text editor swap when WebGL render is enabled/disabled 2026-06-04 08:39:33 +02:00
Andrey Antukh 7e66929010 🐛 Fix crash when typography token value is an array (#9992)
Add guard in parse-composite-typography-value to check if the
converted value is a map before attempting map operations. When
a typography token has an array value like ["Roboto"], return
an invalid-token-value-typography error instead of crashing with
IMap.-dissoc protocol error.

Add regression test to verify the fix.
2026-06-03 16:54:40 +02:00
Andrey Antukh 2808268e52 📎 Update changelog 2026-06-03 14:36:05 +02:00
Elena Torró 0fe4337359 🐛 Fix webgl thumbnail label (#10009) 2026-06-03 14:06:05 +02:00
Andrey Antukh 3cedf11e1c 🔧 Update tests github workflow config 2026-06-02 17:24:35 +02:00
Andrey Antukh 6bf7c33c43 🐛 Fix del-page change constructed with nil id (#9990)
Guard against nil id and missing page in delete-page to prevent
broken changes from being sent to the server. This can happen due
to a race condition where the page is no longer present in the
pages-index. Also add assertion in changes-builder/del-page as
defense-in-depth.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-02 17:23:13 +02:00
Andrey Antukh a57833f3cd 🐛 Fix get-comment-threads called with empty params due to race condition (#9988)
Prevent navigate-to-comment-id from making an RPC call with nil
file-id when current-file-id has been cleared by finalize-workspace
during rapid workspace navigation.  The deferred stream observer
(rx/observe-on :async) could fire after the workspace state was
already cleaned up, causing {:file-id nil} to become {} after
query-string nil-filtering in map->query-string.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-06-02 17:22:39 +02:00
Belén Albeza e2545915b8 🔧 Fix log level of migration exceptions (#9986) 2026-06-02 16:17:22 +02:00
Belén Albeza d5fe5f82f3 🐛 Fix wasm info label positioning (#9981) 2026-06-02 15:18:37 +02:00
Andrey Antukh 3744186510 🔧 Update default nginx limit configuration 2026-06-02 14:05:21 +02:00
Belén Albeza 7fdd2ceb5c 🐛 Fix crash when dismissing the restore version modal (#9969) 2026-06-02 11:33:06 +02:00
Andrey Antukh 17fb1c49f8 Redunce the render throttling to 50ms of the layers-tree* component 2026-06-02 10:30:08 +02:00
Andrey Antukh fe9e47f947 Merge remote-tracking branch 'origin/main' into staging 2026-06-02 10:15:24 +02:00
Andrey Antukh cd18a2bcb2 📎 Update version on mcp/package.json 2026-06-02 10:13:01 +02:00
Andrey Antukh d49fa51fef Update changelog 2026-06-02 10:10:20 +02:00
Andrey Antukh 0d2e0f8367 📎 Update the update-changelog skill and gh.py tool 2026-06-02 10:09:53 +02:00
Andrey Antukh 0044e76cb4 🐛 Revert throttle timeout increase on layers pannel 2026-06-01 14:40:55 +02:00
Andrey Antukh c5de4c27b0 Merge remote-tracking branch 'origin/main' into staging 2026-06-01 12:57:39 +02:00
Alejandro Alonso 88f2366c6f 🎉 Enable render switch and wasm info by default and simplify feature helpers to use pre-computed features set (#9942)
The setup-wasm-features function is the single source of truth for
    resolving the renderer choice (URL param > profile preference > team
    flags), storing the result in state[:features]. Several helpers were
    re-deriving the same priority chain independently, duplicating logic:

    - wasm-enabled?, wasm-url-override, wasm-url-override-ref
    - enabled-by-flags?, enabled-without-migration?

    This change removes all duplicated helpers and simplifies the
    remaining functions to rely exclusively on the pre-computed
    :features set:

    - active-feature? — now just checks (contains? (:features state)
      feature) without special-casing render-wasm/v1
    - use-feature — uses the reactive features-ref for all features
    - initialize/recompute-features effects — use the local features
      binding directly

    Since :features is rebuilt by setup-wasm-features on every
    initialization and recompute, this preserves correctness while
    eliminating ~50 lines of duplicated code.
2026-06-01 12:52:34 +02:00
Aitor Moreno 2410bcb0df Merge pull request #9941 from penpot/alotor-layout-fixes
🐛 Fix layout render-wasm issues
2026-06-01 12:38:01 +02:00
alonso.torres dddb4cf0b6 🐛 Fix layout render-wasm issues 2026-06-01 10:27:59 +02:00
Luis de Dios d3148e1a10 🐛 Fix add modal confirmation when clicking restore from saved version preview (#9804) 2026-06-01 10:08:40 +02:00
Juan de la Cruz af81818b97 Add new release 2.16 slides (#9940)
*  Add new slides for the 2.16 release

* 🎉 Add new slide gif images
2026-06-01 09:59:10 +02:00
Andrey Antukh 3a4e3aaeac 🐛 Fix consistency issues on mcp readme 2026-06-01 09:36:44 +02:00
Andrey Antukh d66e8702e7 📎 Update root repo deps 2026-06-01 09:27:58 +02:00
Luis de Dios 0b56fd2f77 🐛 Fix locked flex and grid elements cannot be selected in viewer role (#9865)
* 🐛 Fix locked flex and grid elements cannot be selected in viewer role

*  Add playwright test
2026-06-01 08:30:27 +02:00
Belén Albeza a5c8bcaf9e 🐛 Fix text editor crash when switching from svg to wasm renderer (#9926)
* 🐛 Fix crash when switching renderers with text editor open

* ♻️ Use new initialized? helper in wasm api
2026-05-29 14:00:49 +02:00
Alejandro Alonso b5108ca1ad 🎉 Update wasm label (#9938) 2026-05-29 13:40:35 +02:00
Andrés Moya 7e6884e330 🐛 Fix error when copy & paste a swapped copy (#9934) 2026-05-29 13:36:17 +02:00
Eva Marco 6e8d2b3708 🎉 Add clear error messages (#9886) 2026-05-29 13:27:43 +02:00
Eva Marco 237fa568e8 🐛 Fix comments 2026-05-29 12:28:10 +02:00
Eva Marco ba39600192 🐛 Fix show error on name-input 2026-05-29 12:28:10 +02:00
Andrés Moya 05cceab768 🔧 Add small adjustments and spanish translation 2026-05-29 12:28:10 +02:00
rene0422 0efebc5e0e 🐛 Keep colliding tokens visible as broken pills 2026-05-29 12:28:10 +02:00
Andrés Moya 429103d076 🐛 Fix errors when token name conflicts with group name 2026-05-29 12:28:10 +02:00
Eva Marco 8dbbd49c0e 🐛 Fix foreground color on numeric input (#9920) 2026-05-29 11:30:19 +02:00
Yamila Moreno ddba2ffa75 📎 Update Kaleidos Copyright (#9929) 2026-05-29 11:24:58 +02:00
Pablo Alba aa996c5118 🐛 Fix nested components becomes flat after reset (#9841) 2026-05-28 15:57:35 +02:00
Andrey Antukh 3cce47094e ♻️ Refactor font upload to process variants sequentially (#9921)
* ♻️ Refactor font upload to process variants sequentially

Change the batch upload handler so fonts are uploaded one at a time
instead of all concurrently, preventing excessive simultaneous
upload requests.

Previously `on-upload-all` used `run!` which fired all font variant
uploads simultaneously. Now it uses `rx/from` combined with
`rx/mapcat` to process each font sequentially.

As part of this change, extract the upload logic into a standalone
`handle-font-upload` helper for reuse between single and batch
upload paths, and remove the separately memoized `on-upload*` hook.

Also fix error logging to use `js/console.error` instead of
`js/console.log` for consistency with project conventions.

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

* 📎 Add code comment

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-28 15:51:46 +02:00
Eva Marco 7843bb1208 ♻️ Fix regression and add test 2026-05-28 15:06:29 +02:00
Aitor Moreno 92a3030f05 Merge pull request #9904 from penpot/superalex-fix-tile-spiral-missing-column
🐛 Fix inclusive TileRect width/height for tile spiral scheduling
2026-05-28 13:34:18 +02:00
alonso.torres 111d7daa94 🐛 Fix problem with absolute elements in flex layout 2026-05-28 13:09:06 +02:00
Alejandro Alonso 6663105f4b 🐛 Avoid restoring empty saved-font-size to prevent invalid text shapes on save (#9917) 2026-05-28 12:39:43 +02:00
Alejandro Alonso 03e10fa871 🐛 Fix inclusive TileRect width/height for tile spiral scheduling 2026-05-28 10:22:22 +02:00
Alejandro Alonso 921c5a4294 🐛 Fix drag shapes out of frames (#9905) 2026-05-28 09:13:04 +02:00
Belén Albeza a278820230 Adjust viewport interest area (#9897) 2026-05-27 19:21:42 +02:00
Eva Marco 16d0ee99c1 ♻️ Update copy on context menu (#9888) 2026-05-27 13:43:31 +02:00
Andrey Antukh 95e7edc9d2 📚 Update changelog 2026-05-27 12:51:26 +02:00
Andrey Antukh 243798f458 Add improvements to 'update-changelog' skill 2026-05-27 12:51:26 +02:00
David Barragán Merino a0f869b917 🐳 Remove the configuration of the mcp from Nginx if it is not enabled 2026-05-27 12:36:26 +02:00
Andrey Antukhandalonso.torres 40ce360c99 Improve performance and fix orphan detection in validate-file (#9789)
*  Improve performance and fix orphan detection in validate-file

- Add `*ref-shape-cache*` dynamic var to memoize `find-ref-shape`
  lookups per page, avoiding repeated O(depth) ancestor walks.
- Add `*children-sets*` pre-computed maps for O(1) parent-child
  containment checks, replacing linear `some` scans.
- Short-circuit `inside-component-main?` when the shape context
  already implies a main component.
- Use single-pass reduce with early exit for duplicate detection
  (children, swap slots) instead of count/distinct or frequencies.
- Guard `check-missing-slot` to skip expensive `find-near-match`
  when the shape already has a swap slot.
- Refactor variant-set validation to use `run!` with direct `get`.
- Refactor `check-ref-cycles` to use a single `reduce-kv` pass.
- Fix `get-orphan-shapes`: the original `map` pipeline produced
  nils so orphan shapes were never validated; rewrite with
  `reduce-kv` for correct results.
- Add `validate-file-affected!` for change-scoped validation,
  replacing full file validation in `process-changes-and-validate`
  to only validate pages and components touched by the changes.

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

*  Improved validation

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-05-27 12:36:21 +02:00
Alejandro Alonso 30bba7cd38 🎉 Increase viewport interest area threshold (#9885) 2026-05-27 11:47:40 +02:00
Andrey Antukh 57397bc32a Merge remote-tracking branch 'origin/main' into staging 2026-05-27 11:35:47 +02:00
Andrey Antukh 676e7f8646 🔧 Update devenv postgresql config 2026-05-27 11:35:21 +02:00
Andrey Antukh f6c76711f4 Merge remote-tracking branch 'origin/main' into staging 2026-05-27 11:34:59 +02:00
Andrey Antukh e43703c368 📎 Update root dependencies 2026-05-27 10:57:06 +02:00
Alejandro Alonso 1025d2e756 🐛 Fix WASM context-restore background reapplication (#9880) 2026-05-27 09:16:02 +02:00
Pablo AlbaandAndrey Antukh c9d43006c8 🐛 Fix restore saved version keeps view-only (#9514)
* 🐛 Fix restore saved verrsion keeps view-only

* 📎 Remove outdated note from CHANGES.md

Remove note about restoring saved version from Preview mode.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-27 09:09:32 +02:00
Andrey Antukh 110db4380e 🐛 Fix invitation token propagation in login flow
Pass invitation-token through login-from-token event so it reaches
the logged-in state. Fix component render syntax (:& -> :>) for the
verify-token route. Remove redundant navigation that re-visited
verify-token after login. Fix missing dependency in effect hook
to re-run when token changes.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-26 19:03:47 +02:00
Andrés Moya 7a8fa7a9cb 🐛 Token remap preserves child component sync after renaming a token group (#9566) (#9878) 2026-05-26 17:12:03 +02:00
Eva Marco 310f224ab6 🐛 Fix keep copy untranslated to preserve token name validation (#9877) 2026-05-26 16:26:13 +02:00
Eva Marco 0c568b0b2d 🐛 Fix highlight on token pill (#9829) 2026-05-26 16:25:18 +02:00
Alonso Torres b609a964be 🐛 Fix boolean issues on the wasm render
* 🐛 Fix sharp angles in text-to-path due to wrong quad/conic degree elevation

* 🐛 Preserve even-odd fill type through Skia path conversions

* 🐛 Fix wrong quadratic-to-cubic degree elevation in push_bezier

* 🐛 Skip zero-length degenerate close segments in path_to_beziers

* 🐛 Replace BTreeMap for Vec for bool calculation

* 🐛 Fix even_odd missing when creating Path
2026-05-26 15:11:52 +02:00
David Barragán Merino f1c78945c4 🐳 Start penpot-frontend always after penpot-mcp 2026-05-26 13:55:27 +02:00
Elena Torró 75c61f9211 🐛 Fix stroke rendering on drag (#9871) 2026-05-26 13:14:38 +02:00
Belén Albeza 34f30e38aa 🐛 Fix migrations throwing exception on corrupted file (#9868) 2026-05-26 12:07:22 +02:00
Aitor Moreno eb095169b8 Merge pull request #9814 from penpot/hiru-fix-detach-token-typ
🐛 Fix changed font size when editing a text with no changes
2026-05-26 11:42:30 +02:00
Alonso Torres 5a3a855b24 🐛 Fix problem with position-data not present
* 🐛 Fix problem with position-data not present

* 🐛 Async set-objects wait before calculate-position-data
2026-05-26 09:50:23 +02:00
alonso.torres eafd261ca6 🐛 Add a e2e test for undo after renaming 2026-05-26 09:46:18 +02:00
alonso.torres 63886d097b 🐛 Fix problem with undo rename tokens 2026-05-26 09:46:18 +02:00
Eva Marco 9439d63682 🐛 Fix rename on non empty page (#9850) 2026-05-26 08:58:09 +02:00
Eva Marco 017f1d9994 🐛 Fix filter tokens to be case-sensitive (#9800)
* 🐛 Fix filter tokens to be case-sensitive

* ♻️ Add test
2026-05-25 07:47:58 +02:00
Elena Torro 2fdd3aab98 🐛 Fix nested inherited transformations 2026-05-22 14:11:50 +02:00
andrés gonzález 649efd124e 📚 Update User Guide with 2.6 features (#9768) 2026-05-22 10:49:39 +02:00
Belén Albeza 3fd114550f 🐛 Fix library update tab UX (#9812)
* 🐛 Fix update library modal UI not working properly when updating a lib

*  Add playwright test for bug 14214
2026-05-21 19:50:12 +02:00
David Barragán Merino d574ec4ed2 🔧 Change the path to the cache directories in the custom runner 2026-05-21 19:04:41 +02:00
David Barragán Merino 0e399b7ad8 🔧 Change the path to the cache directories in the custom runner 2026-05-21 19:03:58 +02:00
Eva Marco 8119ed132e 🐛 Fix sorting of standalone and grouped tokens (#9736) 2026-05-21 17:15:07 +02:00
Luis de Dios bfa338bdee 🐛 Fix resize line between sitemaps and layers 2026-05-21 16:54:31 +02:00
Andrés Moya 65c6b7be7c 🐛 Fix changed font size when editing a text with no changes 2026-05-21 16:46:52 +02:00
Alonso Torres a7b17f54f1 🐛 Fix problem of path position on variant change (#9801) 2026-05-21 16:39:26 +02:00
Belén Albeza 4a73a97a32 🐛 Fix shapes not being rendered after render switch 2026-05-21 16:31:56 +02:00
andrés gonzálezandAndrey Antukh 6d6f624d09 Track WebGL rendering toggle events (#9683) (#9772)
*  Track WebGL rendering toggle events (#9683)

* 📎 Normalize workspace menu events origin

* 📎 Normalize event origin for profile settings page

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-21 13:17:14 +02:00
e2ed6a488d Polish workspace find and replace UX (#9687)
*  Polish workspace find and replace UX

Co-authored-by: Cursor <cursoragent@cursor.com>

*  Add toggle mode button

This button toggles between search and search and replace modes

* ♻️ Refactor and CSS cleanup

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-05-21 12:10:36 +02:00
Alejandro Alonso f3053fc844 🐛 Fix 2.15 release notes crash on invalid slide index (#9805) 2026-05-21 12:09:43 +02:00
Alonso Torres 3cfd1e1a48 🐛 Fix problem with request file access 2026-05-21 11:34:45 +02:00
Belén Albeza 3512a57df7 🐛 Fix referential integrity data in old files (#9771) 2026-05-21 11:18:13 +02:00
Alonso Torres c0e7bfae00 🐛 Fix problem with shift and alt in numeric inputs 2026-05-21 11:02:47 +02:00
Elena Torró df0a58af93 🐛 Fix team members request loop on dashboard 2026-05-21 09:26:53 +02:00
Belén Albeza d7a50735ba 🐛 Fix page and delete page icons in the sidebar
* 🐛 Fix page delete icon not disappearing on selected page

* 🐛 Fix page icon being clipped on its right side
2026-05-21 09:14:45 +02:00
Francis Santiago 0d8785a0bd Merge pull request #9738 from penpot/fc-8897-disable-ipv6-listen
🐳 Fix frontend startup on hosts without IPv6 support
2026-05-20 21:07:07 +02:00
Francis Santiago 2ad85db016 🐳 Fix frontend startup on hosts without IPv6 support
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-20 20:53:30 +02:00
Eva Marco 857aa4175c 🐛 Fix Numeric inputs rejects values with leading whitespaces 2026-05-20 17:38:16 +02:00
Belén Albeza d96442483e 🐛 Fix blurry boards (svg renderer) 2026-05-20 17:37:07 +02:00
Eva Marco f1c0ea2a19 🐛 Fix typing full token name on numeric input (#9725) 2026-05-20 13:23:21 +02:00
Andrés Moya 3e2b00b97f 🐛 Reload libraries when the tokens change (#9715) 2026-05-20 13:12:52 +02:00
Francis Santiago 106b10e971 📚 Clarify self-hosted OIDC configuration for containerized deployments (#9758)
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-20 13:05:40 +02:00
Alonso Torres 29d449b42f 🐛 Deactivate text update (#9757) 2026-05-20 11:08:41 +02:00
Elena Torro fb6c522cc0 🐛 Fix inner strokes clipped on boards created from rect 2026-05-20 00:15:53 +02:00
Eva Marco 27df2aebfc 🐛 Fix token pill not updating when token name is equal token group (#9742) 2026-05-19 18:58:18 +02:00
Eva Marco b72389b5e3 🐛 Fix unset color on delete invitations modal (#9747) 2026-05-19 18:56:39 +02:00
Andrey Antukh 5f30704b28 📚 Update changelog 2026-05-19 17:55:41 +02:00
Andrey Antukh 0b0bd72dce 📎 Backport opencode skills from staging 2026-05-19 17:47:00 +02:00
Andrey Antukh d0cc859bc2 Migrate svg-attrs, optimize set-shape-svg-attrs, filter invalid URLs (#9118)
*  Add svg-attrs casing fix migration

*  Optimize set-shape-svg-attrs by removing redundant operations

- Remove backward compatibility for kebab-case SVG attribute keys
  (fill-rule, stroke-linecap, stroke-linejoin) since svg-attrs are
  already normalized to camelCase by the attrs->props migration.
- Remove unnecessary select-keys filtering and intermediate map
  construction (dissoc :style + merge style).
- Directly extract values from style and attrs using or, avoiding
  any intermediate map allocation.

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

* 🐛 Filter non-http(s) URLs in upload-images to prevent invalid calls

Skip upload for image items that are not data URIs and do not have
an http:// or https:// URL, avoiding unnecessary RPC calls with
invalid URLs to create-file-media-object-from-url.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-19 17:41:41 +02:00
Alejandro Alonso 20c6da2138 Merge pull request #9745 from penpot/superalex-fix-numeric-input-unmount
🐛 Fix commit pending numeric input on unmount without blur side effects
2026-05-19 16:59:55 +02:00
Alejandro Alonso 1d2c158ebe 🐛 Fix commit pending numeric input on unmount without blur side effects 2026-05-19 16:59:39 +02:00
Alejandro Alonso 783cfd3e55 Merge pull request #9724 from penpot/alotor-fix-grid-position
🐛 Fix problem with grid child positions
2026-05-19 16:57:39 +02:00
Alejandro Alonso 0de351fcf6 Merge pull request #9734 from penpot/elenatorro-14211-fix-translation-drag-out-of-board
🐛 Clean modifiers when needed
2026-05-19 16:54:53 +02:00
Elena Torro 29ad9aa057 🐛 Fix redirect after leaving team 2026-05-19 15:44:27 +02:00
Andrey Antukh 405a73e8ba Add climit impl and config for file snapshot methods (#9722)
*  Add dedicated concurrency limit for restore-file-snapshot

This adds a dedicated climit configuration for the restore-file-snapshot
RPC method with :permits 1 per profile (plus queue of 2 and 60s timeout)
and a global limit of 3. Previously the method only used the generic
root/by-profile and root/global limits, allowing up to 7 concurrent
restore operations per profile which caused database row lock contention
on FOR UPDATE and connection pool exhaustion.

*  Skip locking on restore! to avoid blocking other operations

Changes the row lock acquisition in restore! from a blocking FOR UPDATE
to FOR UPDATE SKIP LOCKED. If the file row is already locked by another
concurrent operation (e.g., another restore or an update-file), the query
returns no rows and the caller fails fast with a clear conflict error
instead of blocking indefinitely holding a database connection.

*  Add queue and timeout limits to root/by-profile concurrency limit

Previously root/by-profile had no queue limit (unbounded Integer/MAX_VALUE)
and no timeout, allowing requests to pile up indefinitely behind a profile
whose permits were exhausted by long-running operations. This could lead
to memory pressure and cascading failures. Now limited to 30 queued
requests with a 30-second timeout so excess requests fail fast.

*  Move backup snapshot creation outside restore transaction

The backup snapshot (fsnap/create!) is now created in its own short-lived
connection before the actual restore transaction begins. This ensures the
backup is persisted independently of the restore outcome and reduces the
restore transaction window.

The restore itself runs inside a db/tx-run! block with an optimistic
locking check: it reads the file with FOR UPDATE and compares its revn
against the value captured at backup time. If the file was edited
concurrently, the restore aborts with a conflict error to prevent data
loss.

Co-dependent with the SKIP LOCKED change in restore! — the FOR UPDATE
acquired here is in the same transaction as restore!, so the SKIP LOCKED
inside restore! correctly sees the row as unlocked (same transaction).

* ♻️ Remove unused private function get-minimal-file

The local get-minimal-file function in file_snapshots.clj is no longer
used since restore! switched to direct exec-one! with FOR UPDATE SKIP
LOCKED. The sql:get-minimal-file SQL constant is still used directly.

*  Add minor improvements on db connection management

* ♻️ Refactor create-file-snapshot to use explicit transaction management

Remove automatic transaction wrapping (`::db/transaction true`) and
pass `cfg` through the call chain instead of destructured `conn`.
Wrap `fsnap/create!` in an explicit `db/tx-run!` for clearer
transaction boundaries.

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

*  Add dedicated concurrency limit for create-file-snapshot

This adds a dedicated climit configuration for the create-file-snapshot
RPC method with :permits 1 per profile (plus queue of 2 and 60s timeout)
and a global limit of 3. Previously the method only used the generic
root/by-profile and root/global limits, allowing up to 10 concurrent
snapshot creation operations per profile which could cause database
contention and connection pool exhaustion.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-19 14:30:44 +02:00
Alonso Torres ee6489b202 🐛 Fix problem with login shoing wrong credentials 2026-05-19 13:19:06 +02:00
Alonso Torres aa1fb718e0 🐛 Fix invalid token on anonymous session 2026-05-19 13:13:11 +02:00
Elena Torro c53856b5a9 🐛 Clean modifiers when needed 2026-05-19 12:45:14 +02:00
alonso.torres 8098250b23 🐛 Fix problem with grid child positions 2026-05-19 12:43:56 +02:00
Elena Torró d9ee28229c 🐛 Toggle token path on token rename 2026-05-19 11:35:30 +02:00
Eva Marco ed746bb694 🐛 Fix no gap on token list 2026-05-19 11:01:39 +02:00
Alonso Torres a9d0feb8fd 🐛 Fix problem with caret color value (#9717) 2026-05-19 09:56:16 +02:00
Elena Torró 8dd4b486e7 Improve drag performance avoiding unnecessary modifiers 2026-05-19 09:44:58 +02:00
Eva Marco 44f4c43f15 🐛 Fix apply tokens on token creation (#9713) 2026-05-19 09:40:10 +02:00
Andrey Antukh 46c35b01a8 📎 Update changelog 2026-05-19 09:02:34 +02:00
Andrey Antukh d9bcc1431c 📎 Update the 'update-changelog' opencode skill 2026-05-19 09:02:28 +02:00
Andrey Antukh 5b7c732449 Merge remote-tracking branch 'origin/main' into staging 2026-05-18 19:59:46 +02:00
Andrey Antukh 87b969bd05 📎 Update changelog 2026-05-18 19:59:12 +02:00
Andrey Antukh 1161a163a7 ⬆️ Update root repo opencode dependency 2026-05-18 19:59:12 +02:00
Andrey Antukh 4ad137aef3 📎 Update gh-issue-from-pr opencode skill 2026-05-18 19:59:12 +02:00
Andrey Antukh 1b6b367951 Add diagnostic keys to SSRF validation exceptions
Add :uri and :scheme/:host keys to exceptions raised by
`validate-uri` for better error diagnostics. Also fix a bug
where (str url) was used instead of (str uri) in the
host-missing exception path.

Update the existing blocked-target test to verify the new :uri
key, and add three new tests covering scheme rejection, missing
host, and DNS failure error paths. All 27 tests pass with 60
assertions and 0 failures.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-18 15:57:55 +00:00
Belén Albeza 5c423c3678 🐛 Fix measurement guides not showing up in wasm when user has viewer role 2026-05-18 17:17:18 +02:00
Eva MarcoandAndrey Antukh 53530e958a 🐛 Fix incorrect warning when token applied (#9708)
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-18 16:26:28 +02:00
Andrey Antukh 4d9c6eba38 📎 Add missing bugfix entries to changelog 2026-05-18 16:20:27 +02:00
Andrey Antukh 208182cab1 Merge remote-tracking branch 'origin/main' into staging 2026-05-18 15:23:46 +02:00
Andrey Antukh f5acea7cd7 📎 Update opencode 'update-changelog' skill 2026-05-18 15:22:32 +02:00
Andrey Antukh 7e522ae777 📎 Fix inconsistencies on CHANGES.md 2026-05-18 15:11:11 +02:00
Andrés MoyaandAndrey Antukh 82169bc0a3 🐛 Fix loss of swap slot in some cases of variant switch (#9147)
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-18 14:25:32 +02:00
Andrey Antukh 725a0c966c 📎 Fix incorrect entries on changelog 2026-05-18 14:23:18 +02:00
Andrés Moya ab284febf7 🐛 Fix token application to grid padding (#9630) 2026-05-18 13:32:28 +02:00
Andrey Antukh 9de25c5404 🐛 Fix incorrect content-type on doc endpoint response (#9681)
The /api/main/doc endpoint was returning HTML content with a
text/plain content-type header instead of text/html. This caused
browsers to render the response as plain text.

Added content-type: text/html; charset=utf-8 header to the
response in the doc handler and added a regression test to
verify the fix.

Closes #9680

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-18 12:54:16 +02:00
Alonso Torres 9928249d4f ⬆️ Downgrade archive dependency (#9704) 2026-05-18 12:47:41 +02:00
Alejandro Alonso 0956becd12 🎉 Reduce heap allocations 2026-05-18 12:35:16 +02:00
Andrés Moya 25ee8dee78 🐛 Fix editing a text element detaches applied tokens (#9525) 2026-05-18 12:28:48 +02:00
Alejandro Alonso 1ac503f6bc Merge pull request #9510 from penpot/alotor-fix-viewer-texts
🐛 Fix problem with viewer texts
2026-05-18 11:24:02 +02:00
alonso.torres b2bfd627ae 🐛 Fix problem with viewer texts 2026-05-18 11:00:45 +02:00
andrés gonzálezandCursor 24fe5559c5 📚 Update 2.16 changelog (#9689)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-18 10:31:24 +02:00
Belén Albeza 0300058605 🐛 Fix delete page icon being clipped (#9685) 2026-05-15 13:41:38 +02:00
Andrey Antukh ff23f786b4 🐛 Fix broken authentication on /assets handlers
- Add ::setup/props and ::db/pool to :app.http.assets/routes config
  so session renewal works correctly for asset requests.
- Add actoken/authz middleware to the assets middleware chain so
  access tokens are properly recognized.
- Add authenticated? helper that checks both ::session/profile-id
  and ::actoken/profile-id, fixing 401 errors when accessing
  protected assets with a valid access token.
- Add comprehensive test suite for assets auth scenarios.

Closes #9677

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-15 12:05:02 +02:00
Belén Albeza fc36fb0959 🐛 Fix text editor being hidden to Playwright when empty text (#9682) 2026-05-15 12:04:51 +02:00
Andrey Antukh 6ac8012258 Merge remote-tracking branch 'origin/main' into staging 2026-05-15 11:57:16 +02:00
Andrey Antukh 6cc36e4fcc 📎 Backport more changes for opencode 2026-05-15 11:56:30 +02:00
Andrey Antukh fe76567180 📎 Backport opencode skills from staging 2026-05-15 11:51:49 +02:00
Andrey Antukh 3db0e5ee0d 📎 Update changelog 2026-05-15 11:31:58 +02:00
Andrey Antukh 1f8ab6fed2 📎 Update the 'update-changelog' skill
And add specific tool for extracting info from github
2026-05-15 11:31:58 +02:00
Andrey Antukh 0b65431137 📎 Add taiga skill and script for opencode
Allows easy extraction of information from taiga urls
2026-05-15 11:10:02 +02:00
Elena Torró 053d4a23f5 🐛 Fix shape deletion after tiles refactor (#9678) 2026-05-15 11:06:17 +02:00
andrés gonzálezandAndrey Antukh 27ac0b7469 🐛 Unify layout creation telemetry for plugins and MCP (#9654)
* 🐛 Unify layout creation telemetry for plugins and MCP

* 📚 Update changelog for version 2.15.4

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-15 10:53:43 +02:00
Andrey Antukh 1dea84b7b1 📚 Update mcp readme 2026-05-15 10:19:29 +02:00
Aitor Moreno 58c42df37e 🐛 Fix atlas texture leak 2026-05-14 17:17:06 +02:00
310bf6fd6a 💄 Change auth illustration (#9552)
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 16:25:53 +02:00
andrés gonzálezandAndrey Antukh 7e7bf7c458 Update Open Graph and link preview metadata (#9557)
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 16:23:57 +02:00
andrés gonzález c62ce866a8 📚 Change sentence at MCP docs (#9568) 2026-05-14 16:23:13 +02:00
andrés gonzález 846958d79e 📚 Change slogan at Help Center footer (#9554) 2026-05-14 16:22:19 +02:00
Alonso Torres dc878572da 🐛 Fix problem with set activation after renaming (#9545) 2026-05-14 16:04:07 +02:00
Pablo AlbaandAndrey Antukh 5dafd44966 🐛 Fix library update button freezes and does not apply updates (#9513)
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 16:03:47 +02:00
Xaviju fb2734cd02 🐛 Save numeric input value on unmount (#9548) 2026-05-14 16:02:34 +02:00
Andrey Antukh 9021544c05 Merge remote-tracking branch 'origin/main' into staging 2026-05-14 15:24:29 +02:00
Andrey Antukh 05d40e3370 📚 Update changelog 2026-05-14 15:19:24 +02:00
Andrey Antukh 237f61fda0 📎 Add update changelog opencode skill 2026-05-14 15:19:03 +02:00
Andrey Antukh eb1707788b 📎 Add gh-issue-from-pr SKILL for opencode 2026-05-14 15:01:26 +02:00
Alonso Torres 8afe8a5dfa 🐛 Fix plugins schema validation error (#9632) 2026-05-14 15:00:41 +02:00
Andrey Antukh fd19bf121f 📎 Update changelog 2026-05-14 14:23:59 +02:00
Alejandro Alonso 134391bc3a Merge pull request #9633 from penpot/elenatorro-fix-auto-width
🐛 Fix regression on auto-width
2026-05-14 14:01:19 +02:00
Andrey Antukh 67d9567971 🐛 Prevent CSS injection vulnerability in font family names
Add a shared `schema:font-family` whitelist validator in
app.common.types.font that only allows letters, digits, spaces,
hyphens, underscores, and dots in font family names. Apply the schema
to create-font-variant and update-font RPC endpoints on the
backend, and add client-side validation in the dashboard fonts UI.
Include unit tests for the schema and integration tests for the RPC
handlers.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 13:46:02 +02:00
Elena Torro b13aedb231 🐛 Fix regression on auto-width 2026-05-14 13:43:00 +02:00
Alejandro Alonso 009e505ba1 Merge pull request #9570 from penpot/superalex-interactive-drag-crop-atlas-snapshopt
🎉 Rebuild drag-crop cache from tile textures with hybrid atlas fill
2026-05-14 13:14:36 +02:00
Alejandro Alonso 575f4b9df0 🎉 Optimize drag-crop cache rebuild path 2026-05-14 13:00:25 +02:00
Alejandro Alonso d4be6686c7 🎉 Rebuild drag-crop cache from tile textures with hybrid atlas fill 2026-05-14 13:00:25 +02:00
Aitor Moreno 64f73ef23b ♻️ Remove Mutex from mem buffer (#9479) 2026-05-14 12:57:10 +02:00
Belén Albeza f62ee7d1ae 🐛 Fix asset icon (#9612) 2026-05-14 12:56:54 +02:00
Belén Albeza 78e3077a37 🔧 Use polyfilled helpers instead of raf (#9628) 2026-05-14 12:42:58 +02:00
Alonso Torres f389fcf468 🐛 Fix problem with copy-as-image action (#9586) 2026-05-14 12:01:30 +02:00
Andrey Antukh 8b06096019 🐛 Fix playwright version inconsistencies 2026-05-14 11:40:33 +02:00
Andrey Antukh 29f940fb7a 🐛 Sanitize comment content on rendering (#9605)
Add escape-html function that escapes HTML special characters and apply
it in the comment editor at four dom/set-html! call sites where
user-provided text is inserted as innerHTML, preventing stored XSS.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 11:20:11 +02:00
Alonso Torres c752e194d6 🐛 Fix problem with preview version in svg render (#9626) 2026-05-14 11:11:01 +02:00
Andrey Antukh eec05271e0 Merge remote-tracking branch 'refs/remotes/origin/staging' into staging 2026-05-14 11:07:59 +02:00
Andrey Antukh d78074307f Merge remote-tracking branch 'origin/main' into staging 2026-05-14 11:07:42 +02:00
Alejandro Alonso 4bd0049ddf Merge pull request #9622 from penpot/superalex-fix-grid-not-visible-2
🐛 Fix grid not visible
2026-05-14 10:56:18 +02:00
Andrey AntukhandYamila Moreno 55dd6d2b00 🔧 Add cache to github tests CI worflow. (#9621)
*  Remove usage of RELEASE placeholder on deps.edn

* 🔧 Add Maven cache to CI

---------

Co-authored-by: Yamila Moreno <yamila.moreno@kaleidos.net>
2026-05-14 10:54:22 +02:00
Alejandro Alonso 8d51a88326 🐛 Fix grid not visible 2026-05-14 10:48:31 +02:00
Alejandro Alonso a961008188 Merge pull request #9550 from penpot/ladybenko-14033-fix-pasted-text-styles
🐛 Fix text styles sometimes being overidden when chaging shape text attrs
2026-05-14 10:09:06 +02:00
Belén Albeza abc290582d 🐛 Fix text styles sometimes being overidden when chaging shape text attrs 2026-05-14 09:42:55 +02:00
Alejandro Alonso 49d76d0f1c Merge pull request #9618 from penpot/luis-fix-linting-issue
🐛 Fix linting error
2026-05-14 08:59:54 +02:00
Luis de Dios 4c9e1d4015 🐛 Fix linting error 2026-05-14 08:53:23 +02:00
Alejandro Alonso 14fca46886 Merge pull request #9610 from penpot/ladybenko-14185-fix-exit-focus
🐛 Fix rendering glitch when exiting focus mode
2026-05-14 07:58:28 +02:00
Belén Albeza ae282eb6e2 🐛 Fix rendering glitch when exiting focus mode 2026-05-14 07:45:51 +02:00
Alejandro Alonso 009d805394 Merge pull request #9614 from penpot/elenatorro-fix-text-focus-on-empty-text-shape
🐛 Fix text focus on empty text
2026-05-14 07:26:30 +02:00
Elena Torro 374c64da74 🐛 Fix text focus on empty text 2026-05-14 07:12:30 +02:00
Alejandro Alonso 2685389aad Merge pull request #9562 from penpot/elenatorro-fix-layout-freeze
🐛 Reflow only when needed
2026-05-13 16:26:05 +02:00
Elena Torro 38c62a465f 🐛 Reflow only when needed 2026-05-13 16:22:21 +02:00
Alejandro Alonso 757aae1df3 Merge pull request #9572 from penpot/azazeln28-refactor-target-and-backbuffer-rendering
♻️ Refactor how target and backbuffer works
2026-05-13 16:11:17 +02:00
Aitor Moreno a5da9449b5 ♻️ Refactor how target and backbuffer works 2026-05-13 16:05:19 +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
Pablo Alba bf880467b4 🐛 Fix dependency libraries visible in UI after unlinking main library (#9511) 2026-05-13 15:35:42 +02:00
Andrey Antukh da85e02a6f ⬆️ Update dependencies (#9597)
* ⬆️ Update dependencies

* 📎 Fix playwright dep
2026-05-13 14:14:10 +02:00
Alejandro Alonso 7617e42547 Merge pull request #9576 from penpot/superalex-fix-atlas-issues
🐛 Fix atlas corruption when dragging large shapes after zoom change
2026-05-13 13:08:20 +02:00
Alejandro Alonso 1a3b057814 🐛 Fix atlas corruption when dragging large shapes after zoom change 2026-05-13 13:00:56 +02:00
Alejandro Alonso 4f5d269313 Merge pull request #9599 from penpot/superalex-fix-drag-crop-cache-viewport-clamp
🐛 Fix clamp backbuffer crop origin for partially off-screen shapes
2026-05-13 13:00:03 +02:00
Alejandro Alonso 9c61aa4f17 🐛 Fix clamp backbuffer crop origin for partially off-screen shapes 2026-05-13 12:48:05 +02:00
Yamila Moreno 4df707c0ff 🐳 Add enable-mcp to docker-compose as default behaviour 2026-05-13 11:53:10 +02:00
Pablo Alba fffafdab93 🐛 Fix library updates reappear after file is reloaded (#9563)
* 🐛 Fix library updates reappear after file is reloaded

Summary
Migrate synced_at timestamps to a standalone file_library_sync table to ensure sync state is tracked for both direct and transitive libraries.

Problem
Transitive libraries (libraries imported by other libraries) are not stored as direct rows in file_library_rel. Because the system previously coupled synced_at directly to the file_library_rel schema, transitive libraries lacked a persistent location for their sync timestamps. This caused sync states to be lost or incorrectly reported for nested dependencies.

Changes
Schema Migration: Created file_library_sync and migrated existing synced_at values from file_library_rel.

Decoupling: Removed tight Foreign Key coupling to allow sync rows to exist independently of specific relationship records.

Persistent Writes: Added upsert-file-library-sync! helper. Updated all import, duplication, and RPC write paths (v1/v2/v3 importers, link-file-library) to ensure every write persists a sync row.

Unified Reads: Updated both direct and recursive/transitive library queries to fetch synced_at from the new table.

Testing: Added regression tests to verify that sync rows are correctly created/updated even when a transitive relation is absent in file_library_rel.

Impact
This fix ensures that the system accurately records and retrieves sync states for the entire library dependency tree, resolving the bug where nested libraries appeared out of sync.

*  MR review
2026-05-13 11:29:05 +02:00
Yamila Moreno d4dade2c3e 🐳 Pin minor version in docker-compose.yaml 2026-05-13 07:48:06 +02:00
Andrey Antukh 382efe3449 📚 Update changelog 2026-05-12 23:33:39 +02:00
Yamila Moreno 4289cad9ab 🐳 Improve nginx configuration for MCP server (#9565) 2026-05-12 23:28:20 +02:00
Andrey Antukh db7fcfcb1a 🐛 Fix metrics for rpc methods 2026-05-12 19:06:25 +02:00
Andrey Antukh e5c99231da 📚 Update changelog 2026-05-12 18:43:08 +02:00
Yamila Moreno 02c3d2c27c 🐳 Add mcp server to release workflow 2026-05-12 18:38:17 +02:00
Yamila Moreno eb22c59e5a 🐳 Add penpot-mcp service to official docker-compose.yml 2026-05-12 18:31:13 +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 63ff5c87c2 Merge pull request #9523 from penpot/superalex-test-firefox
🐛 Fix WASM viewport interaction lifecycle for pan/zoom
2026-05-12 15:34:25 +02:00
Alejandro Alonso 9ebf7875ea 🐛 Fix WASM viewport interaction lifecycle for pan/zoom 2026-05-12 14:13:18 +02:00
Andrey Antukh ade0d2d0a8 📎 Update changelog with PR info 2026-05-12 13:01:42 +02:00
Alejandro Alonso 97c8fcd4ad Merge pull request #9509 from penpot/elenatorro-14038-fix-component-thumbnail-update
🐛 Fix component thumbnail sync
2026-05-12 12:29:28 +02:00
Elena Torro 76e3df5836 🐛 Use extrect to capture component's thumbnail 2026-05-12 12:21:06 +02:00
Elena Torro 847d55bfb4 🐛 Update component thumbnails to match current shape 2026-05-12 12:21:06 +02:00
Alejandro Alonso 086ed83847 Merge pull request #9389 from penpot/azazeln28-refactor-state
♻️ Refactor State into DesignState
2026-05-12 12:15:05 +02:00
Aitor Moreno a126379cc7 ♻️ Refactor State into DesignState 2026-05-12 12:14:38 +02:00
Luis de Dios f62a89dcc2 🐛 Fix font selection position is hiding fonts (#9499) 2026-05-12 10:25:16 +02:00
Alejandro Alonso f044104db1 Merge pull request #9524 from penpot/elenatorro-fix-blur-on-page-transition
🐛 Fix blur on page transition
2026-05-12 10:10:49 +02:00
Yamila Moreno 328efd4e16 📚 Add notice regarding architectural constraints with MCP Server (#9423) 2026-05-12 10:06:13 +02:00
Andrey Antukh 11a72abdcd 📎 Update version on mcp server 2026-05-12 10:04:12 +02:00
Elena Torro 463017b5c9 🐛 Fix blur on page transition 2026-05-12 09:47:00 +02:00
Andrey Antukh bd3ca6f8e5 📚 Update changelog 2026-05-12 09:33:33 +02:00
Alejandro Alonso 7650fa1716 Merge pull request #9500 from penpot/alotor-fix-text-edition
🐛 Fix problem when editing text not being redrawn
2026-05-12 06:50:02 +02:00
alonso.torres effb8bcf10 🐛 Fix problem when editing text not being redrawn 2026-05-12 06:45:28 +02:00
Andrey Antukh fd82744c62 Merge branch 'main' into staging 2026-05-11 17:52:52 +02:00
Andrey Antukh 843a4a5b58 🐛 Fix mattermost and database logger related to the audit event change 2026-05-11 17:08:38 +02:00
Andrey Antukh c76e536cd8 Merge remote-tracking branch 'origin/main' into staging 2026-05-11 16:24:27 +02:00
Andrey Antukh 102c97040a 🐛 Fix unexpected exception on handling webhook events 2026-05-11 16:23:14 +02:00
Andrey Antukh 1de2718d43 Merge remote-tracking branch 'origin/main' into staging 2026-05-11 15:24:15 +02:00
Andrey Antukh 8f4f948104 🐛 Skip the ssrf check on internal audit-log archive task 2026-05-11 15:22:59 +02:00
Andrey Antukh 6ef231bf38 Merge remote-tracking branch 'origin/main' into staging 2026-05-11 14:04:27 +02:00
Dr. Dominik Jain 1f9f4126b7 Improve MCP server logging, adding Loki support (#9425)
*  Improve MCP server logging

Log only fingerprints of user tokens

*  Add Loki transport support to MCP server logger

Loki logging is enabled iff PENPOT_LOGGERS_LOKI_URI is non-empty.

File logging is now enabled iff PENPOT_MCP_LOG_DIR is set to a non-empty value
(previously defaulted to the "logs" directory when unset).

GitHub #9415
2026-05-11 14:01:05 +02:00
Dr. Dominik Jain 313777d1c3 Improve MCP server logging, adding Loki support (#9425)
*  Improve MCP server logging

Log only fingerprints of user tokens

*  Add Loki transport support to MCP server logger

Loki logging is enabled iff PENPOT_LOGGERS_LOKI_URI is non-empty.

File logging is now enabled iff PENPOT_MCP_LOG_DIR is set to a non-empty value
(previously defaulted to the "logs" directory when unset).

GitHub #9415
2026-05-11 14:00:23 +02:00
Andrey Antukh feb49bc07a 🐛 Add missing migrations for audit-log tables 2026-05-11 13:28:53 +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 7d4be33d4f 🎉 Add telemetry anonymous event collection (#9483)
* 🎉 Add telemetry anonymous event collection

Rewrite the audit logging subsystem to support three operating modes and
add anonymous telemetry event collection:

Modes:
- A (audit-log only): events persisted with full context
- B (audit-log + telemetry): same as A, plus events are collected for
  telemetry shipping
- C (telemetry-only): events stored anonymously with PII stripped,
  telemetry flag active, audit-log flag inactive

Audit system refactoring (app.loggers.audit):
- Replace qualified map keys (::audit/name etc.) with plain keywords
- Rename submit! -> submit, insert! -> insert, prepare-event ->
  prepare-rpc-event
- Add submit* as a lower-level public API
- Add process-event dispatch function that handles all three modes and
  webhooks in a single tx-run!
- Add :id to event schema (auto-generated if omitted)
- Add filter-telemetry-props: anonymises event props per event type.
  Keeps UUID/boolean/number values; for login/identify events preserves
  lang, auth-backend, email-domain; for navigate events preserves route,
  file-id, team-id, page-id; instance-start trigger passes through.
- Add filter-telemetry-context: retains only safe context keys.
  Backend: version, initiator, client-version, client-user-agent.
  Frontend: browser, os, locale, screen metrics, event-origin.
- Timestamps truncated to day precision via ct/truncate for telemetry
  storage
- PII stripped: props emptied, ip-addr zeroed, session-linking and
  access-token fields removed from context

Config (app.config):
- Derive :enable-telemetry flag from telemetry-enabled config option

Email utilities (app.email):
- Add email/clean and email/get-domain helper functions for domain
  extraction from email addresses

Setup (app.setup):
- Emit instance-start trigger event at system startup
- Simplify handle-instance-id (remove read-only check)

RPC layer (app.rpc):
- wrap-audit now activates when :telemetry flag is set
- Add :request-id to RPC params context for event correlation

RPC commands (management, teams_invitations, verify_token, OIDC auth,
webhooks): migrate all audit call sites to use the new plain-key API

SREPL (app.srepl.main):
- Migrate all audit/insert! calls to audit/insert with plain keys

Telemetry task (app.tasks.telemetry):
- Restructure legacy report into make-legacy-request; distinguish
  payload type as :telemetry-legacy-report
- Add collect-and-send-audit-events: loop fetching up to 10,000 rows
  per iteration, encodes and sends each page, deletes on success,
  stops immediately on failure for retry
- Add send-event-batch: POSTs fressian+zstd batch (base64 via
  blob/encode-str) to the telemetry endpoint with instance-id per event
- Add gc-telemetry-events: enforces 100,000-row safety cap by dropping
  oldest rows first
- Add delete-sent-events: deletes successfully shipped rows by id

Blob utilities (app.util.blob):
- Add encode-str/decode-str: combine fressian+zstd encoding with URL-
  safe base64 for JSON-safe string transport

Database:
- Add migration 0145: index on audit_log (source, created_at ASC) for
  efficient telemetry batch collection queries

Frontend:
- Always initialize event system regardless of :audit-log flag
- Defer auth events (signin identify) to after profile is set
- Refactor event subsystem for telemetry support

Tests (21 test vars, 94 assertions in tasks-telemetry-test):
- Cover all code paths: disabled/enabled telemetry, no-events no-op,
  happy-path batch send and delete, failure retention, payload anonymity,
  context stripping, timestamp day precision, batch encoding round-trip,
  multi-page iteration, GC cap enforcement, partial failure handling
- blob encode-str/decode-str round-trip tests (14 test vars)
- RPC audit integration tests (5 test vars)

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

* 📎 Add pr feedback changes

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 12:42:01 +02:00
Andrey Antukh cd882f9ebd 🔧 Add minor changes to devenv config 2026-05-11 12:39:03 +02:00
Andrey Antukh cd4a4da0f2 🎉 Add telemetry anonymous event collection (#9483)
* 🎉 Add telemetry anonymous event collection

Rewrite the audit logging subsystem to support three operating modes and
add anonymous telemetry event collection:

Modes:
- A (audit-log only): events persisted with full context
- B (audit-log + telemetry): same as A, plus events are collected for
  telemetry shipping
- C (telemetry-only): events stored anonymously with PII stripped,
  telemetry flag active, audit-log flag inactive

Audit system refactoring (app.loggers.audit):
- Replace qualified map keys (::audit/name etc.) with plain keywords
- Rename submit! -> submit, insert! -> insert, prepare-event ->
  prepare-rpc-event
- Add submit* as a lower-level public API
- Add process-event dispatch function that handles all three modes and
  webhooks in a single tx-run!
- Add :id to event schema (auto-generated if omitted)
- Add filter-telemetry-props: anonymises event props per event type.
  Keeps UUID/boolean/number values; for login/identify events preserves
  lang, auth-backend, email-domain; for navigate events preserves route,
  file-id, team-id, page-id; instance-start trigger passes through.
- Add filter-telemetry-context: retains only safe context keys.
  Backend: version, initiator, client-version, client-user-agent.
  Frontend: browser, os, locale, screen metrics, event-origin.
- Timestamps truncated to day precision via ct/truncate for telemetry
  storage
- PII stripped: props emptied, ip-addr zeroed, session-linking and
  access-token fields removed from context

Config (app.config):
- Derive :enable-telemetry flag from telemetry-enabled config option

Email utilities (app.email):
- Add email/clean and email/get-domain helper functions for domain
  extraction from email addresses

Setup (app.setup):
- Emit instance-start trigger event at system startup
- Simplify handle-instance-id (remove read-only check)

RPC layer (app.rpc):
- wrap-audit now activates when :telemetry flag is set
- Add :request-id to RPC params context for event correlation

RPC commands (management, teams_invitations, verify_token, OIDC auth,
webhooks): migrate all audit call sites to use the new plain-key API

SREPL (app.srepl.main):
- Migrate all audit/insert! calls to audit/insert with plain keys

Telemetry task (app.tasks.telemetry):
- Restructure legacy report into make-legacy-request; distinguish
  payload type as :telemetry-legacy-report
- Add collect-and-send-audit-events: loop fetching up to 10,000 rows
  per iteration, encodes and sends each page, deletes on success,
  stops immediately on failure for retry
- Add send-event-batch: POSTs fressian+zstd batch (base64 via
  blob/encode-str) to the telemetry endpoint with instance-id per event
- Add gc-telemetry-events: enforces 100,000-row safety cap by dropping
  oldest rows first
- Add delete-sent-events: deletes successfully shipped rows by id

Blob utilities (app.util.blob):
- Add encode-str/decode-str: combine fressian+zstd encoding with URL-
  safe base64 for JSON-safe string transport

Database:
- Add migration 0145: index on audit_log (source, created_at ASC) for
  efficient telemetry batch collection queries

Frontend:
- Always initialize event system regardless of :audit-log flag
- Defer auth events (signin identify) to after profile is set
- Refactor event subsystem for telemetry support

Tests (21 test vars, 94 assertions in tasks-telemetry-test):
- Cover all code paths: disabled/enabled telemetry, no-events no-op,
  happy-path batch send and delete, failure retention, payload anonymity,
  context stripping, timestamp day precision, batch encoding round-trip,
  multi-page iteration, GC cap enforcement, partial failure handling
- blob encode-str/decode-str round-trip tests (14 test vars)
- RPC audit integration tests (5 test vars)

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

* 📎 Add pr feedback changes

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 12:19:59 +02:00
Andrey Antukh b312e6b059 📚 Update changelog 2026-05-11 11:26:54 +02:00
Andrey Antukh 15379f37f5 🐛 Fix maximum call stack size exceeded in SSE read-stream (#9484)
The recursive `read-items` function in `app.util.sse/read-stream`
caused a synchronous stack overflow when reading buffered stream
data. Each `rx/mapcat` call chained another recursive invocation
on the same call stack without yielding to the event loop.

Replace the recursive pattern with an `rx/create`-based async pump
that uses Promise `.then()` chaining, keeping the call stack depth
constant regardless of stream size.

Also add progress reporting with names and IDs during binfile
export and import, and bump `eventsource-parser` dependency.

Closes #9470

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 11:06:15 +02:00
Andrey Antukh ec0d692856 🐛 Fix maximum call stack size exceeded in SSE read-stream (#9484)
The recursive `read-items` function in `app.util.sse/read-stream`
caused a synchronous stack overflow when reading buffered stream
data. Each `rx/mapcat` call chained another recursive invocation
on the same call stack without yielding to the event loop.

Replace the recursive pattern with an `rx/create`-based async pump
that uses Promise `.then()` chaining, keeping the call stack depth
constant regardless of stream size.

Also add progress reporting with names and IDs during binfile
export and import, and bump `eventsource-parser` dependency.

Closes #9470

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-11 10:59:35 +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
Andrey Antukh 6eba2e6c42 📚 Update changelog 2026-05-10 20:23:10 +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
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 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
Francis Santiago e9588f3939 🐳 Reuse shared Nginx security headers (#9473)
Signed-off-by: Francis Santiago <francis.santiago@kaleidos.net>
2026-05-08 14:11:09 +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 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
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
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
Eva Marco c5f2ffab69 🐛 Fix internal error when applying not valid value to margin input (#9311) 2026-05-07 15:24:25 +02:00
Aitor Moreno 9fccee8689 ♻️ Refactor how viewport interest area works 2026-05-07 13:46:51 +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
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
Andrey Antukh 697a825d76 📚 Update opencode planner agent 2026-05-07 01:04:38 +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
Dexterity db77780227 🐛 Fix MCP "active in another tab" notification not clearing (#9321) 2026-05-06 17:42:34 +02:00
Dexterity ae7c7a7972 🐛 Fix swapped analytics event names on MCP tab-switch dialog (#9322) 2026-05-06 17:41:16 +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 14a0660352 Merge remote-tracking branch 'origin/main' into staging 2026-05-06 15:06:59 +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>
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)` > `:bug: 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>
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
Alejandro Alonso 708c4065b3 🐛 Fix drag and drop cache eligibility rules 2026-05-06 13:20:57 +02:00
Alonso Torres 2fbff4f88e Buffer update shapes changes on token application 2026-05-06 12:16:35 +02:00
Alejandro Alonso 528d006b8d Events enhancements
*  Compute moved-subtree data once in find-valid-parent-and-frame-ids
*  Smooth WASM drag preview stream
*  Limit WASM outline modifier application
2026-05-06 12:16:09 +02:00
Andrey Antukh 34cc0e9d56 🔥 Materialize the canary feature flag across the codebase
Remove the :canary flag from the flags definition and make all
features gated behind it always available:

- Enable "download font" option in dashboard fonts context menu
- Enable Tab/Shift+Tab keyboard navigation for renaming shapes
  in layer items
- Enable "duplicate color" option in asset panel when applicable
- Enable "duplicate typography" option in asset panel when applicable
- Enable "copy as image" context menu option for frame shapes

Also remove unused [app.config :as cf] requires from files that
no longer reference it after the materialization.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-05-06 11:19:04 +02:00
Eva Marco 5f40673fde 🐛 Remove drag to change when there is a token applied on numeric-inputs (#9314) 2026-05-06 10:31:39 +02:00
Andrey Antukh 1744d17385 Merge remote-tracking branch 'origin/develop' into staging 2026-05-06 09:34:46 +02:00
Hosted Weblate 94f8370d98 🌐 Merge branch 'origin/develop' into Weblate. 2026-05-06 08:58:11 +02:00
Eva Marco ce24fed32b 🐛 Fix incorrect text-edition warning when applying tokens (#9355) 2026-05-06 08:47:30 +02:00
Alejandro Alonso dc5f02a11c 📎 Fix linting issues 2026-05-05 18:50:25 +02:00
Alejandro Alonso 67bb109331 📎 Fix linting issues 2026-05-05 18:32:25 +02:00
Alejandro Alonso 00c27287bd Merge remote-tracking branch 'origin/staging' into develop 2026-05-05 18:27:09 +02:00
Alejandro Alonso b34054940f Merge pull request #9361 from penpot/elenatorro-14100-wasm-fast-paths
 Improve drag rendering on text and paths
2026-05-05 18:21:36 +02:00
Aitor Moreno 61f5df8461 Merge pull request #9354 from penpot/superalex-fix-allow-selection-while-bounding-box-cloaking-is-active
🐛 Fix allow selection while bounding-box cloaking is active
2026-05-05 18:20:45 +02:00
Elena Torro e950ec56eb Reduce per-render text layout work 2026-05-05 17:47:50 +02:00
Pablo Alba 2fbab08bde 🐛 Fix nitrate penpot-version schema 2026-05-05 17:47:47 +02:00
Elena Torro 4a0cd0b7ce Translation-only fast paths for Shape and Path transforms 2026-05-05 17:35:42 +02:00
Alejandro Alonso 2e8d188d87 🐛 Fix allow selection while bounding-box cloaking is active 2026-05-05 16:55:28 +02:00
Alejandro Alonso ce1045c265 Merge pull request #9335 from penpot/alotor-fix-flex-auto-height
🐛 Fix problem with flex layout and text auto-height
2026-05-05 15:50:22 +02:00
Marina López 41996ed9a5 Add nitrate subscription text 2026-05-05 13:52:01 +02:00
Pablo Alba 3431aee177 🐛 Fix move org dialog must be select 2026-05-05 12:34:40 +02:00
alonso.torres 843b2aebd4 🐛 Fix problem with flex layout and text auto-height 2026-05-05 10:40:25 +02:00
Alejandro Alonso 7d923f8e1d Merge remote-tracking branch 'origin/staging' into develop 2026-05-05 06:52:38 +02:00
Alejandro Alonso c794e0ed73 🎉 Events enhancements (#9310)
* 🎉 Coalesce viewport pointermove into one PointerEvent

* 🎉 Skip worker hover selection query during transform

* 🎉 Coalesce snap X/Y into one worker query-snap-xy
2026-05-04 17:57:00 +02:00
Dominik Jain 07ad152ae5 🐛 Fix .component() returning outermost component for nested instances
The shape API method .component() used locate-component which walks
to the outermost instance root via get-instance-root. For nested
component instances (e.g. a button inside a card), this incorrectly
returned the outer component (the card) instead of the nearest one
(the button).

Added locate-head-component in utils.cljs which uses get-head-shape
to find the nearest component head, and updated the :component
property in shape.cljs to use it.

Fixes #9183
2026-05-04 16:13:25 +02:00
Clayton 4ce56e96fe 🐛 Fix MCP media uploads and SVG data URI image parsing (#9201)
* 🐛 Fix MCP media uploads and SVG data URI image parsing

Signed-off-by: Clayton <claytonlin1110@gmail.com>

* 🐛 Fix lint

Signed-off-by: Clayton <claytonlin1110@gmail.com>

* 🐛 Fix test

Signed-off-by: Clayton <claytonlin1110@gmail.com>

---------

Signed-off-by: Clayton <claytonlin1110@gmail.com>
2026-05-04 13:33:58 +02:00
Eva MarcoandXavier Julian a2bcbe81dd 🎉 Add token numeric inputs for inputs on right sidebar (#9143)
Co-authored-by: Xavier Julian <xavier.julian@kaleidos.net>
2026-05-04 13:02:19 +02:00
Alejandro Alonso 164f0cba7a Merge remote-tracking branch 'origin/staging' into develop 2026-05-04 11:56:07 +02:00
María Valderrama 152967bea6 🐛 Fix sidebar overflow 2026-05-04 11:02:54 +02:00
Alonso Torres e948020886 🐛 Fix problem with rounding in flex elements 2026-05-04 10:35:16 +02:00
andrés gonzález 66337f2ab9 📚 Add WebGL Troubleshooting Guide 2026-05-04 09:34:19 +02:00
María Valderrama f24ad6bee4 Show current plan in Nitrate
*  Show current plan in Nitrate

* 📎 Code Review
2026-05-04 09:29:14 +02:00
alonso.torres f6bd991968 🐛 Improved e2e tests stability 2026-05-04 09:04:30 +02:00
Elena Torró 7c0465de6b Merge pull request #9268 from penpot/superalex-drag-backbuffer-crop-cache-3
🎉 Cache selection crops from Backbuffer during drag
2026-04-30 16:46:39 +02:00
Jack StormentandAndrey Antukh 8f03b5ed9c 🔥 Remove stray debug log in frame-preview load-ref callback (#9258)
`(.log js/console "load-ref" iframe-dom)` was left in the iframe
ref callback of `frame-preview`. Mirrors the defect PR #9243
removed from `color-row*` — fires on every ref invocation and
pollutes the browser console.

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-30 16:21:40 +02:00
JeffandAndrey Antukh d09985edee 🐛 Preserve Inkscape labels when pasting SVGs (#9252)
Steps to reproduce: paste an SVG authored in Inkscape (or any editor
that follows the inkscape:label convention) into a penpot file. The
group/element names visible in the source editor are dropped — penpot
shows generic auto-ids like 'g1234' or 'path5678' instead.

Root cause: parse-svg-element in common/src/app/common/files/shapes_
builder.cljc derived the shape name from (or (:id attrs) (tag->name
tag)). Inkscape stores user-given element labels in the inkscape:label
and sodipodi:label namespaced attributes while id holds an auto-
generated technical id, so the operator's chosen name was always
overridden by the technical id when present.

tubax/xml->clj (the SVG parser the import pipeline already uses for
upload, paste, and library import) keeps namespaced attributes as
:prefix:name keywords — the same shape this file already reads
:xlink:href from on line 134, and that app.common.svg uses for the
xlink: namespace at lines 300-307.

Fix: extract the name-resolution logic into a public resolve-element-
name helper that prefers :inkscape:label, then :sodipodi:label, then
:id, then (tag->name tag). Existing SVGs that don't carry either label
namespace fall through the same chain as before, so the behaviour for
non-Inkscape-authored SVGs is unchanged.

This restores the behaviour dfelinto's penpot-icon-generator-plugin
relies on (linked from the issue body) — that plugin reads element
names from the imported SVG to map Blender icons to penpot components.

Tests: 6 deftest blocks in common/test/common_tests/files/shapes_
builder_test.cljc covering the priority order (inkscape > sodipodi >
id > tag), each fallback in isolation, and the empty-attrs case.
Registered in common-tests.runner.

Closes #7869

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-30 16:17:34 +02:00
Andrey Antukh 13414e7bed 📚 Update changelog 2026-04-30 16:15:04 +02:00
Alejandro Alonso 17e0b545d2 🎉 Cache selection crops from Backbuffer during drag 2026-04-30 16:13:57 +02:00
Alejandro Alonso ddb6eca5ea Merge pull request #9269 from penpot/superalex-optimize-wasm-release-profile
🎉 Optimize wasm release profile (thin LTO, size optimizations)
2026-04-30 16:13:46 +02:00
Andrey Antukh b42e81e1a4 📎 Update changelog (candidate for freeze) 2026-04-30 15:57:41 +02:00
FairyPiggyDevandAndrey Antukh 9c2a80bfa1 🐛 Fix crash pasting component with variants from shared library (#9136)
Copying a component with variants from a shared library file ("Lib")
and pasting it into a file that uses that library ("Using Lib") would
crash the destination file with the referential-integrity validator
error:

    {:code :component-main-external
     :hint "Main instance should refer to a component in the same file"}

Root cause
----------

Paste goes through `generate-duplicate-shape-change` in
`common/src/app/common/logic/libraries.cljc`. When the shape is a
main instance of a known component and the copy set includes its
variant container, dispatch lands in `duplicate-variant`, then
`generate-duplicate-component`, and finally `duplicate-component`,
which clones the main-instance shape tree. Its `update-new-shape`
helper already re-links the new outer main's `:component-id` to the
freshly created local component (`new-component-id`), but it never
touches `:component-file`. The cloned shape therefore inherits
`:component-file` from the source library while the new component is
registered in the destination's local library
(`:apply-changes-local-library? true`), leaving the main-instance
dangling.

Fix
---

Extend `update-new-shape` with a second clause, sibling to the
existing `:component-id` rewrite: when a destination file id is
provided and differs from the new main's current `:component-file`,
re-root the shape. The same `(= (:component-id new-shape) (:id
component))` guard already used for the id rewrite ensures only the
outer main-instance is touched; nested shapes are unaffected.

The destination file id is threaded from the paste entry point
through the two orchestration functions that already knew the
source/destination distinction:

- `generate-duplicate-shape-change` — supplies the destination
  `file-id` it already has in scope when dispatching to
  `generate-duplicate-component-change`.
- `generate-duplicate-component-change` — accepts `:new-component-file`
  as a kwarg; renames its internal `file-id` binding to
  `source-file-id` for clarity (it was always the component's
  originating library file); forwards `new-component-file` to
  `duplicate-variant`.
- `duplicate-variant` — takes and forwards the `new-component-file`
  positional arg.
- `generate-duplicate-component` — accepts `:new-component-file` kwarg
  and passes it to `duplicate-component`.
- `duplicate-component` — applies the rewrite inside
  `update-new-shape`. The `new-component-file` parameter is placed
  right after `new-component-id` since component-id and component-file
  are typically managed together.

Same-file duplication is not affected: without `:new-component-file`
the new clause is skipped, and when source and destination match the
`(not= new-component-file (:component-file new-shape))` guard fails.

Tests
-----

Added in `common/test/common_tests/logic/comp_creation_test.cljc`:

- `test-duplicate-component-rewrites-component-file-to-destination`
  asserts that passing `:new-component-file` to
  `generate-duplicate-component` produces a main-instance with
  `:component-file` equal to the destination id.
- `test-duplicate-component-keeps-component-file-without-dest`
  baseline: without `:new-component-file`, `:component-file` is left
  untouched, matching pre-existing same-file behavior.

Github #8144

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-30 15:54:24 +02:00
FairyPiggyDev 76c1b9afab ♻️ Migrate navigation-bullets to modern component syntax (#9265)
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).

Component
---------

`app.main.ui.releases.common/navigation-bullets` is a small (7-line)
self-contained presentational component used by every release-notes
modal to render the slide-progress dots. It already used standard
keyword destructuring (`[{:keys [slide navigate total]}]`), had no
`?`-suffixed props, no `unchecked-get`, no `obj/merge!`, no
`::mf/wrap-props false`, and (importantly) no `::mf/register`, so it
satisfies the migration pre-flight checks unchanged.

Changes
-------

- `releases/common.cljs` — definition renamed to `navigation-bullets*`.
  Body, props and metadata are otherwise unchanged.

- `releases/v1_4.cljs` … `v2_15.cljs` (29 files) — every existing call
  site `[:& c/navigation-bullets {…}]` becomes
  `[:> c/navigation-bullets* {…}]`. The `:slide`, `:navigate`, `:total`
  props are passed exactly as before. The `:as c` alias of the require
  is unchanged, so no require edits are needed.

No props were renamed (none ended in `?`); no helpers had to be
swapped for `mf/spread-props` / `mf/props` (callers pass plain literal
maps); no metadata had to be removed (none of the legacy options were
in use).

Github #9260

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
2026-04-30 15:50:38 +02:00
4902037c7d Add HEX, HSB, and HSL support in the third color tab (#9134)
*  Add HEX, HSB, and HSL support in the third color tab

Relabel the existing HSVA tab to HSBA (the math was already HSB) and add
an inline HSB ↔ HSL model toggle inside the tab, matching Figma's color
panel. Sliders, gradients, and labels update dynamically per mode; HSL
values roundtrip through RGB/HSV so no color-storage changes are needed.
Model choice persists across sessions.

* 💄 Fix lint errors

Signed-off-by: juan-flores077 <toptalent399@gmail.com>

* 🐛 Fix Plugin API token application for JS array of strings (#9166)

* 🐛 Fix Plugin API token application for JS array of strings

Plugin code calling `shape.applyToken(token, ["fill"])` or
`token.applyToShapes([rect], ["fill"])` from JavaScript supplies a JS
array of strings. The plugin proxies expected a Clojure set of
keywords, and two coupled defects made the calls silently no-op (or,
with `throwValidationErrors` enabled, throw "check error"):

1. `token-attr-plugin->token-attr` only consulted its alias map when
   the input was already a keyword. String inputs like "fill" fell
   through to the identity branch, so the downstream
   `cto/token-attr?` predicate (which checks against a set of
   keywords) returned false for every string. Coerce strings to
   keywords first.

2. The `applyToken` / `applyToShapes` / `applyToSelected` schemas
   used plain `[:set ...]`, which has no `:decode/json` transformer
   for JS array → Clojure set coercion. Switch to the registered
   `[::sm/set ...]` (in `app.common.schema`) which provides the
   array → set decoder. After the switch, the standard JSON pipeline
   converts `["fill"]` to `#{"fill"}`, then the inner
   `[:and ::sm/keyword [:fn token-attr?]]` decodes each element to a
   keyword and validates it.

Also extends the docstring on `token-attr-plugin->token-attr` to make
the string-friendly contract explicit, and registers a new
`tokens-test` ns under `frontend/test/frontend_tests/plugins/` with
six `deftest` blocks covering:

- known keywords passing through unchanged
- keyword aliases (`:r1` → `:border-radius-top-left`, etc.)
- string inputs coerced to keywords (regression for #9162)
- `token-attr?` accepting both keyword and string inputs
- `token-attr?` rejecting unknown attrs and nil

Closes #9162

* 🐛 Fix wrong direction in plugin-name alias tests

The added tests in tokens_test.cljs and the new docstring in tokens.cljs
described the alias resolution in the wrong direction. The map is
{:r1 :border-radius-top-left, …} then map-invert'd, so
token-attr-plugin->token-attr maps verbose plugin-side names
(:border-radius-top-left) to canonical internal short names (:r1),
not the other way around. Inputs already in canonical form (:r1, :fill,
"fill", …) pass through unchanged. Flipped the alias-resolution test
expectations and the keyword/string-input cases, refreshed the docstring
and the regression-coverage comment to match.

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>

* 💄 Fix sucess typo in subscription dialog i18n keys (#9204)

Rename subscription.settings.sucess.dialog.{title,footer} to
subscription.settings.success.dialog.{title,footer} in en.po and
update the three callsites in subscription.cljs.

Closes #9203

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

* 🐛 Fix HSVA → HSBA test rename and Prettier formatting

Signed-off-by: juan-flores077 <toptalent399@gmail.com>

* 🐛 Fix CI failures and address review feedback for HSB color tab

Signed-off-by: juan-flores077 <toptalent399@gmail.com>

* 💄 Resolve Conflicts

Signed-off-by: juan-flores077 <toptalent399@gmail.com>

---------

Signed-off-by: juan-flores077 <toptalent399@gmail.com>
Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: boskodev790 <boskomaljkovic790@outlook.com>
Co-authored-by: Jack Storment <88656337+jack-stormentswe@users.noreply.github.com>
2026-04-30 15:41:04 +02:00
Andrés Moya 9f94566005 💄 Rename i18n keys for tokens errors (#9207) 2026-04-30 15:30:20 +02:00
Statxc 547750e8bf 🐛 Preserve OpenType variant name for custom fonts (#9193) 2026-04-30 15:29:04 +02:00
Alejandro Alonso 97688cb790 🎉 Optimize wasm release profile (thin LTO, size optimizations) 2026-04-30 14:17:25 +02:00
Elena Torro 27d854ed5b Skip component-sync on pure-translation drag commits 2026-04-30 13:45:45 +02:00
alonso.torres c14dbba7fd 🐛 Fix z-index for profile menu 2026-04-30 12:59:27 +02:00
Andrey Antukh 22a325cc72 📎 Fix linter issue 2026-04-30 11:42:21 +02:00
Xaviju aa87ae194c 🔥 Remove unused var (#9262) 2026-04-30 11:29:15 +02:00
Alonso Torres c9b81284d2 🐛 Fix z-index for profile menu (#9257) 2026-04-30 11:28:27 +02:00
Alonso Torres de9170d96b 🐛 Fix z-index for profile menu (#9257) 2026-04-30 11:27:50 +02:00
Andrey Antukh 1de8a074ef 🐛 Fix incorrect gh client install on devenv 2026-04-30 11:23:44 +02:00
Aitor Moreno acb3997ed7 🐛 Fix text editor v2 min size 2026-04-30 09:21:53 +02:00
FairyPiggyDevandAndrey Antukh ed021711b6 ♻️ Extract make-delete-asset-group-fn helper for assets panel (#9211)
Reviewer follow-up on PR #9151. The "Delete group" handler was
duplicated across the three assets-panel sections (colors,
typographies, components), each carrying the same skeleton — filter
by group path, build an undo-id, run the deletes inside one
transaction, and show the same confirm modal — with only the path
predicate and the per-asset delete event differing.

Add `app.main.ui.workspace.sidebar.assets.common/make-delete-asset-group-fn`
that takes the differing parts as options:

- `:assets`             collection to filter.
- `:on-clear-selection` invoked before the deletes.
- `:delete-events`      `(fn [matching-assets] => seq-of-events)`.
- `:path-filter`        predicate (defaults to `str/starts-with?`),
                        overridden to `cpn/inside-path?` for
                        components so nested group paths match the
                        same way the existing ungroup/combine helpers
                        do.

The factory returns `(fn [path] …)` so each call site stays a
straight `mf/use-fn`. The variant-container dedup in components
(one `delete-shapes` per container, not one per sibling variant)
moves into that section's `:delete-events` fn and is unchanged in
behavior.

Cleanup
-------

The `:as i18n` alias is no longer needed in any of the three section
files (its only use was `i18n/c` for the modal count, which the
helper now handles); reduced to `:refer [tr]`.

Github #9141

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-30 08:48:04 +02:00
TinyClawandiot2edge 400414776b 🐛 Fix :heigth typo in clipboard frame-same-size? (#9250)
The height comparison in frame-same-size? used the misspelled keyword
:heigth on both sides. (:heigth selrect) returns nil for any selrect,
so (= nil nil) is always true and the function degenerated to a width-only
comparison.

Result: the 'paste next to selected frame' branch in clipboard.cljs fired
whenever pasted-content width matched a target frame's width, even if the
heights differed.

Introduced in #9033 ( Add paste to replace (Cmd+Shift+V)).

Signed-off-by: iot2edge <tylerprice830@gmail.com>
Co-authored-by: iot2edge <tylerprice830@gmail.com>
2026-04-30 08:37:00 +02:00
Dexterity 25c5bb2019 Restore deleted team files in bulk instead of per file (#9248) 2026-04-30 08:35:02 +02:00
Andrey Antukh fc414b23d2 📚 Update changelog with entries for 2.17.0
Add 3 new features/enhancements (file import errors, read-only version
preview, clipboard permissions) and 4 bug fixes (restore typo, layer
sibling selection, tooltip duplication, library notification link) to
the 2.17.0 changelog section.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 17:59:33 +00:00
Andrey Antukh 346614edc3 🐛 Fix SCSS mixin names in v2_15 release modal styles
Rename 5 deprecated mixin calls from camelCase to kebab-case to
match the actual mixin names defined in common-refactor.scss:

- deprecated.flexCenter -> deprecated.flex-center
- deprecated.headlineSmallTypography -> deprecated.headline-small-typography
- deprecated.headlineLargeTypography -> deprecated.headline-large-typography
- deprecated.bodyLargeTypography -> deprecated.body-large-typography
- deprecated.bodyMediumTypography -> deprecated.body-medium-typography

This fixes the "Undefined mixin" SCSS compilation error.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 17:36:20 +00:00
Andrey Antukh 404ebcc63e 💄 Fix SCSS linter errors in v2_15 release modal styles
Replace class selectors with placeholder selectors for @extend
(.modal-overlay-base -> %modal-overlay-base,
 .button-primary -> %button-primary).

Add blank lines after @include mixin calls in 6 rulesets
(.version-tag, .modal-title, .feature-title, .feature-content,
 .feature-list, .next-btn) to satisfy the
declaration-empty-line-before stylelint rule.

Fixes 8 SCSS linter errors in total.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 17:31:59 +00:00
Andrey Antukh a004219405 Merge remote-tracking branch 'origin/staging' into develop 2026-04-29 19:28:02 +02:00
Andrey Antukh 8b29ca61c6 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-29 19:23:38 +02:00
Ingrid Pigueron b5cd4d96ee 🌐 Add translations for: French
Currently translated at 98.8% (2050 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-04-29 19:10:01 +02:00
Andrey Antukh e81dad21ea 🎉 Add backport-commit skill for manual diff-based commit porting
Introduce a new OpenCode workflow skill that guides users through
backporting commits by applying diffs instead of using cherry-pick.
This is useful when cherry-pick is undesirable (e.g. divergent
histories, binary conflicts, or partial porting).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 19:09:25 +02:00
Andrey Antukh d06b45ec90 🐛 Fix Plugin API token application for JS array of strings
Two coupled defects made shape.applyToken(), token.applyToShapes() and

token.applyToSelected() silently no-op when invoked from JavaScript with

an array of strings (e.g. token.applyToShapes([rect], ["fill"])):

1. token-attr-plugin->token-attr only consulted its alias map when the

   input was already a keyword; string inputs fell through unchanged,

   causing downstream token-attr? to return false.

2. The inner schemas used plain [:set ...] which lacks the :decode/json

   transformer for JS array -> Clojure set coercion. Switching to

   Penpot's custom [::sm/set ...] lets the standard JSON decoder

   pipeline handle the conversion automatically.

This is a backport of commit 1eac3e2be5

which fixes GitHub #9162.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 19:09:25 +02:00
Dexterity 1213640693 🐛 Fix typo in restore-deleted-team-files reduce accumulator (#9241) 2026-04-29 18:48:07 +02:00
Dexterity f530a0ba26 🔥 Remove stray debug log in color-row component (#9243) 2026-04-29 18:47:27 +02:00
Andrey Antukh 1e09e00634 Encourage use of layouts and proper naming in MCP
Improve MCP instructions on design creation:
 * Agents should make use of layouts when appropriate
 * Agents should name all elements appropriately

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 16:12:50 +00:00
Jack StormentandAndrey Antukh 710fd30f78 🐛 Preserve renamed layer name when re-entering edit mode (#9231)
* 🐛 Preserve renamed layer name when re-entering edit mode

When a layer was renamed and the user clicked its name again to edit
it, the input opened with the type-based default name instead of the user's saved name. Pressing Enter then
silently overwrote the saved name with the default. Read the current
shape :name when seeding the rename input so the user's previous
rename is preserved.

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

* 💄 Remove redundant DOM-refresh effect from layer rename input

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

---------

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 18:07:48 +02:00
RenzoandAndrey Antukh 8821ada1bb 🐛 Suppress browser context menu on empty workspace sidebar space (#9196)
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-04-29 17:58:38 +02:00
Jack StormentandAndrey Antukh 22b85f1a92 Show specific error messages for invitation token failures (#9223)
*  Show specific error messages for invitation token failures

Surface distinct error messages for the three invitation-token failure
modes that the backend already distinguishes: email mismatch, expired
token, and invalid/corrupted token. Replaces the single generic
could not accept invitation message with actionable text so the
user knows what went wrong and how to recover.

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

* 💄 Update CHANGE.md

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

* 💄 Address review feedback on invitation-error messages

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

---------

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 17:57:59 +02:00
Andrey Antukh 4829b843b2 🐛 Fix dashboard modal clipping behind sidebar (#9233)
Backport from develop commit 510a015424.

- Fix release notes modal appearing behind the dashboard sidebar (by @RenzoMXD)
- Change sidebar z-index from dropdown to panels layer

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 15:52:48 +00:00
Renzo 510a015424 🐛 Fix dashboard modal clipping behind sidebar (#9233)
Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
2026-04-29 17:44:49 +02:00
alonso.torres 5e3e66a99b 📚 Updated docs for MCP development environment 2026-04-29 16:39:19 +02:00
Andrey Antukh 05b4760583 📚 Set clearer expectations for PR reviews and prior discussion
Explicitly state that the team is small and reviews may take a
few days as they are handled in dedicated time blocks.

Remove mention of GitHub Discussions since it is not used.

Reword the discussion requirement to manage expectations: do not
expect a PR to be accepted without prior discussion.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 16:27:44 +02:00
StatxcandAndrey Antukh fd170b23f6 🐛 Fix Heroicons arrow paths broken after SVG import (#5283) (#9156)
Signed-off-by: statxc <181730535+statxc@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 15:45:22 +02:00
DreamandAndrey Antukh d668744a1f Add search to board size presets dropdown (#9117)
Closes #4658

Signed-off-by: eureka0928 <meobius123@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 15:44:53 +02:00
Elena Torró 1c129ded1f Merge pull request #9200 from penpot/azazeln28-build-time-performance-optimizations
🔧 Change build and cargo settings
2026-04-29 15:12:23 +02:00
Aitor Moreno 73944e46b7 🔧 Change build and cargo settings 2026-04-29 14:35:00 +02:00
María Valderrama e22a03e7e8 Subscribe to nitrate with an activation code
*  Subscribe to nitrate with an activation code

* 📎 Code review
2026-04-29 12:42:25 +02:00
Jack Storment 3f40be6b4d 💄 Fix sucess typo in subscription dialog i18n keys (#9204)
Rename subscription.settings.sucess.dialog.{title,footer} to
subscription.settings.success.dialog.{title,footer} in en.po and
update the three callsites in subscription.cljs.

Closes #9203

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
2026-04-29 11:07:30 +02:00
boskodev790andAndrey Antukh 1eac3e2be5 🐛 Fix Plugin API token application for JS array of strings (#9166)
* 🐛 Fix Plugin API token application for JS array of strings

Plugin code calling `shape.applyToken(token, ["fill"])` or
`token.applyToShapes([rect], ["fill"])` from JavaScript supplies a JS
array of strings. The plugin proxies expected a Clojure set of
keywords, and two coupled defects made the calls silently no-op (or,
with `throwValidationErrors` enabled, throw "check error"):

1. `token-attr-plugin->token-attr` only consulted its alias map when
   the input was already a keyword. String inputs like "fill" fell
   through to the identity branch, so the downstream
   `cto/token-attr?` predicate (which checks against a set of
   keywords) returned false for every string. Coerce strings to
   keywords first.

2. The `applyToken` / `applyToShapes` / `applyToSelected` schemas
   used plain `[:set ...]`, which has no `:decode/json` transformer
   for JS array → Clojure set coercion. Switch to the registered
   `[::sm/set ...]` (in `app.common.schema`) which provides the
   array → set decoder. After the switch, the standard JSON pipeline
   converts `["fill"]` to `#{"fill"}`, then the inner
   `[:and ::sm/keyword [:fn token-attr?]]` decodes each element to a
   keyword and validates it.

Also extends the docstring on `token-attr-plugin->token-attr` to make
the string-friendly contract explicit, and registers a new
`tokens-test` ns under `frontend/test/frontend_tests/plugins/` with
six `deftest` blocks covering:

- known keywords passing through unchanged
- keyword aliases (`:r1` → `:border-radius-top-left`, etc.)
- string inputs coerced to keywords (regression for #9162)
- `token-attr?` accepting both keyword and string inputs
- `token-attr?` rejecting unknown attrs and nil

Closes #9162

* 🐛 Fix wrong direction in plugin-name alias tests

The added tests in tokens_test.cljs and the new docstring in tokens.cljs
described the alias resolution in the wrong direction. The map is
{:r1 :border-radius-top-left, …} then map-invert'd, so
token-attr-plugin->token-attr maps verbose plugin-side names
(:border-radius-top-left) to canonical internal short names (:r1),
not the other way around. Inputs already in canonical form (:r1, :fill,
"fill", …) pass through unchanged. Flipped the alias-resolution test
expectations and the keyword/string-input cases, refreshed the docstring
and the regression-coverage comment to match.

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 11:02:01 +02:00
Aitor Moreno f59301a3d6 🐛 Fix text editor v2 with 0 width 2026-04-29 10:53:07 +02:00
Andrey Antukh 9751ac2b41 📎 Update changelog 2026-04-29 09:43:13 +02:00
Andrey Antukh ea971a0109 🐛 Fix redundant longhand margin-block properties in options.scss
Combine margin-block-start and margin-block-end into the margin-block
shorthand to satisfy the declaration-block-no-redundant-longhand-properties
stylelint rule.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 07:39:05 +00:00
Yamila Moreno d627d1cfac Improve team name validation (#9176) 2026-04-29 08:59:09 +02:00
Pablo Alba b8f1b6e0c3 Add nitrate api notify-user-orgs-deletion 2026-04-28 19:47:28 +02:00
bobsonzu0a5d198e17c343cb f060b8d3fa 🌐 Add translations for: Russian
Currently translated at 81.8% (1697 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-04-28 15:09:52 +00:00
Ingrid Pigueron a3ddf54043 🌐 Add translations for: French
Currently translated at 96.9% (2010 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-04-28 15:09:50 +00:00
FairyPiggyDev 61ce4b9e0d Add "Delete group" to assets panel context menu (#9151)
When working with large asset groups, users asked for a one-click way
to remove every asset under a group path. Multi-select across hundreds
of items is impractical, and ungrouping first and then deleting leaves
the orphaned items in the flat list.

This change adds a "Delete group" option to the assets-panel
context-menu for three asset types that already carry group structure:

- Components (including variants — sibling variants sharing a variant
  container are deduplicated, and the container is deleted once via
  the same dispatch the per-item delete uses in file_library.cljs).
- Colors.
- Typographies.

A confirmation modal is shown before deletion, with the count of
assets to be removed, so the action is never silent. All deletes run
inside a single undo transaction, so one Cmd+Z restores the whole
group.

Changes
-------

- `assets/groups.cljs` — `asset-group-title*` accepts an optional
  `on-delete-group` prop and conditionally adds the menu entry
  between "Ungroup" and "Combine as variants". When the callback is
  not supplied the option is hidden, so asset sections that do not
  implement it stay unaffected.
- `assets/components.cljs` — threads `on-delete-group` through the
  recursive `components-group*` and defines the section-level
  handler, dispatching to `dwsh/delete-shapes` for variant containers
  and `dwl/delete-component` for plain components.
- `assets/colors.cljs` — same threading + a simple `dwl/delete-color`
  dispatch per color in the group.
- `assets/typographies.cljs` — same threading + a
  `dwl/delete-typography` dispatch per typography in the group.
- `translations/en.po` — three new strings: the menu label
  (`workspace.assets.delete-group`) and the modal title/message
  (`modals.delete-asset-group.title`/`.message`, plural-aware).

Github #9141

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Signed-off-by: FairyPiggyDev <luislee3108@gmail.com>
2026-04-28 15:59:05 +02:00
Elena Torró 2aff116906 Merge pull request #9190 from penpot/elenatorro-improve-performance-on-dragging
 Improve drag performance
2026-04-28 13:01:13 +02:00
Andrey Antukh 94827f1848 📎 Update versionon mcp/package.json 2026-04-28 12:38:44 +02:00
Andrey Antukh 42c9c4a929 Merge remote-tracking branch 'origin/main-staging' 2026-04-28 12:37:02 +02:00
Alejandro Alonso e4af37a7ff 🎉 Use backbuffer + direct-to-target tiles during drag 2026-04-28 12:19:00 +02:00
Elena Torro 483ce8b1c9 Improve drag performance 2026-04-28 12:18:24 +02:00
Elena Torro 0f65774ba9 🐛 Fix render wasm generated component thumnails for instant changes 2026-04-28 11:10:35 +02:00
Aitor Moreno 31b09be405 🎉 Add overtype mode to text editor 2026-04-28 11:10:22 +02:00
Andrey Antukh ccd1da40ca 📎 Update mcp package.json version 2026-04-28 10:26:23 +02:00
Eva Marco c269df1441 🐛 Fix empty warning on login (#9056) 2026-04-28 10:25:33 +02:00
Juan de la Cruz 40ee1960a1 📚 Add 2.15.0 onboarding slides (#9172)
* 🎉 Add new slides content

* 🎉 Add new slides imgs

* 🐛 Fix a typo
2026-04-28 10:21:56 +02:00
Andrey Antukh b0ce644752 Merge remote-tracking branch 'origin/staging' into develop 2026-04-28 10:12:40 +02:00
Andrey Antukh 19e81560be Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-28 10:09:18 +02:00
Andrey Antukh c0989d4261 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-28 10:08:51 +02:00
alonso.torres ad1111a613 🐛 Fix problem with align center + grow auto-width 2026-04-28 09:44:57 +02:00
andrés gonzález aabdb69218 📚 Update MCP docs for public release (#9184) 2026-04-28 09:37:40 +02:00
Luis de Dios a35b61ee0c 🐛 Fix put onboarding modals of top of libraries & templates panel (#9178) 2026-04-28 09:26:15 +02:00
Andrey Antukh d9f099841a 🔥 Remove redundant mf/props metadata from modern components (#9192)
The ::mf/props and ::mf/wrap-props metadata keys are no-ops on modern
components (those defined with mf/defc and the * suffix) since the *
suffix already triggers the props behavior these keys attempt to
configure. This cleanup removes the redundant metadata from modern
components across all UI directories.

Changes:
- comments/: comments
- dashboard/: comments, deleted, files, fonts, grid, import, libraries,
  pin_button, projects, search, sidebar, subscription, team, templates
- exports/: files
- modal/: modal
- settings/: subscription
- static/: static
- viewer/: comments, interactions, viewer
- workspace/: context_menu, libraries, sidebar/assets,
  viewport/gradients, tokens/settings/menu
2026-04-28 09:23:56 +02:00
Andrey Antukh 4e1968bbab 📎 Add updated version of github cli to devenv 2026-04-28 00:01:46 +02:00
Jack Storment aa5bfe6dda Add customizable pixel grid color (#9155)
Let users pick the pixel grid color from a standard color picker.
The grid color was previously hardcoded, making it invisible on
mid-tone canvases. Choice is stored on the file so it persists
across sessions. Defaults preserve the current appearance when
unset.

Closes #7750

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
2026-04-27 21:37:27 +02:00
Milos Milic bd1e0fb23f Add Alt+click to expand a layer subtree in the Layers sidebar (#9179)
Closes #7736.

The Layers sidebar offered no way to expand every nested level of a
single subtree at once. Unfolding a layer that wraps a deep tree
required clicking each disclosure indicator one level at a time -
O(siblings * depth) clicks. The asymmetry was particularly visible
next to the existing Shift+click gesture, which collapses every
layer in the panel in a single action via `dwc/collapse-all`, with
no expand counterpart for either a single subtree or the whole
tree.

Add a new `dwc/expand-subtree` event in
`app.main.data.workspace.collapse` that uses
`cfh/get-children-ids-with-self` to gather the shape's id together
with every descendant id, then merges `{descendant-id true}` entries
into `[:workspace-local :expanded]` so the entire subtree opens in
one update. Existing expansion state on unrelated branches is left
untouched (`merge`, not `assoc`), matching the per-key shape used by
`toggle-collapse` and `expand-collapse`.

Wire the gesture into `layer_item.cljs` `toggle-collapse` callback as
a third branch:

  - Shift+click while expanded - collapse every layer (existing).
  - Alt+click while collapsed   - expand the entire subtree (new).
  - Otherwise                   - toggle this single level (existing).

Alt is chosen instead of Shift to avoid the ambiguity the issue
author flagged: "for a layer of middle depth it is unclear whether
[Shift+click] should fold all (up to the topmost parent) or expand
all (only the current subtree)". Alt is a common platform
convention for "do this recursively" (Finder, file managers,
several IDEs), so the asymmetric mapping matches user expectations.
The callback's `mf/deps` vector is extended with `id` and `objects`
so the closure refreshes when the shape tree changes.

CHANGES.md entry added under the 2.17.0 New features section.
2026-04-27 21:36:15 +02:00
RenzoandAndrey Antukh 8a8ebb7943 Preserve vector content when pasting from external tools (#9182)
Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-27 21:35:52 +02:00
Andrey Antukh 84b3d467cf Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-27 20:36:45 +02:00
Andrey Antukh 592cc47336 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-27 20:36:21 +02:00
Andrey Antukh a58dbec8f2 ⬆️ Update root repo deps 2026-04-27 20:35:46 +02:00
Andrey Antukh df4ffb9147 📚 Update prompt-assistant agent file 2026-04-27 20:35:28 +02:00
Andrey Antukh ac5736957e 📚 Update commiter opencode agent 2026-04-27 20:34:45 +02:00
Andrey Antukh eba4f15bba Backport transit and plugins hardening compatibility issue
From staging
2026-04-27 19:04:43 +02:00
boskodev790andAndrey Antukh ea265da1f3 🐛 Fix plugin library.connectLibrary breaking Promise contract on permission failure (#9158)
`library.connectLibrary()` declared its permission check **outside** the
`js/Promise.` wrapper, so when a plugin without `library:write` permission
called `await library.connectLibrary(id)` the method did not return a
`Promise` at all:

- With the default `throwValidationErrors` flag off → `u/not-valid`
  logs to console and returns `nil`. `await nil` resolves to `nil`, so
  the plugin sees a "successful" result and crashes later when it tries
  to use methods on what it thinks is a `LibraryProxy`.
- With `throwValidationErrors` on → `u/not-valid` throws synchronously,
  so the caller gets a thrown exception instead of a rejected promise —
  inconsistent with every other `library:*` / `content:*` method which
  always returns a Promise that rejects via `reject-not-valid`.

Additionally, the in-Promise `(not (string? library-id))` branch used
`(reject nil)` — the plugin got a rejected Promise but with no error
message.

Move the permission check inside the Promise constructor and replace
both validation errors with `u/reject-not-valid`, matching the pattern
used by the sibling methods `restore`, `remove`, `pin`, `saveVersion`,
`findVersions` in `frontend/src/app/plugins/file.cljs` and every other
promise-returning plugin method. No new imports.

Also add a CHANGES.md entry under the 2.17.0 Unreleased bugs-fixed section.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-27 17:59:09 +02:00
Andrey Antukh f4cf667d2f 📚 Update changelog 2026-04-27 17:57:00 +02:00
Pablo Alba f8e40a1ca5 🐛 Fix can't add team to nitrate organization 2026-04-27 17:50:42 +02:00
Belén Albeza e99ed5e9f9 🐛 Fix modifiers streams 2026-04-27 17:45:18 +02:00
Belén Albeza 0bee3993ab 🐛 Avoid race condition in initialize-workspace 2026-04-27 17:45:18 +02:00
Belén Albeza 8f905be511 Ignore user renderer pref when render-switch flag is disabled 2026-04-27 17:45:18 +02:00
Belén Albeza 8afadb5199 Show toast when switching renderer 2026-04-27 17:45:18 +02:00
Belén Albeza 6ba68c1ac0 Hot swap of viewport when renderer changes 2026-04-27 17:45:18 +02:00
Belén Albeza ffdbe242a7 🎉 Disable wasm if renderer profile prop is set to svg 2026-04-27 17:45:18 +02:00
Belén Albeza 46b81f4302 🎉 Store chosen renderer in user profile 2026-04-27 17:45:18 +02:00
Belén Albeza 12549df65c 🎉 Add UI for webgl rendering setting (under config flag) 2026-04-27 17:45:18 +02:00
Andrey Antukh c41537eb55 Merge remote-tracking branch 'origin/staging' into develop 2026-04-27 17:31:15 +02:00
Andrey Antukh 82f1606377 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-27 17:31:00 +02:00
Andrey Antukh 839754715a 📚 Update changelog 2026-04-27 17:30:02 +02:00
Andrey Antukh db8aa9bccc Merge remote-tracking branch 'origin/staging' into develop 2026-04-27 17:27:45 +02:00
Andrey Antukh ef2fe78aac Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-27 17:27:28 +02:00
Andrey Antukh a3b9d7bed7 📎 Fix fmt issue 2026-04-27 17:26:59 +02:00
Andrey Antukh 57f1b80013 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-27 17:26:30 +02:00
Andrey Antukh cbd5f7795b Add minor compatibility adjustments for audit archive task (#8491) 2026-04-27 16:15:35 +02:00
Elenzakaleidos 99f006d728 💄 Update README.md (#9171)
We modified the Images and the text of the Read me page

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2026-04-27 15:44:01 +02:00
Luis de Dios edccda2038 🐛 Fix remove prints 2026-04-27 15:26:55 +02:00
Marina López 4867358428 Add modal to subscribe to nitrate from unlimited 2026-04-27 14:11:47 +02:00
Pablo Alba c6bea65a48 Add organization logo to nitrate invitations emails 2026-04-27 11:14:47 +02:00
boskodev790andAndrey Antukh e5314f4a13 🐛 Fix restore-version-from-plugin promise hanging on restore failure (#9111)
Closes #9092.

`restore-version-from-plugin` accepted `_reject` as a dead parameter and
its stream had no `rx/catch`, so errors raised during the restore flow
(failed `rp/cmd! :restore-file-snapshot`, persistence timeouts, or
exceptions inside the watch body) silently swallowed instead of
rejecting the plugin-facing promise at `file.cljs:81`. Plugin code
that did `await version.restore()` would hang indefinitely on any
failure.

Wire `reject` through and wrap the emission with the same `rx/catch`
pattern already used by `create-version-from-plugins` in this file.

- Rename `_reject` to `reject` in the function signature
- Wrap the `rx/concat` body with `rx/catch` that calls `(reject error)`
  and returns `rx/empty` on error, mirroring `create-version-from-plugins`
- Add a CHANGES.md entry under the 2.17.0 Unreleased bugs-fixed section

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-27 10:17:00 +02:00
Pablo Alba 9c6cc5ec32 💄 Fix nitrate org arrow style 2026-04-27 10:16:40 +02:00
Andrey Antukh feec89679a Merge remote-tracking branch 'origin/main' into main-staging 2026-04-27 09:58:13 +02:00
boskodev790andAndrey Antukh 77c507000b 🐛 Fix LDAP schema typo bind-passwor -> bind-password (#9165)
The malli schema for the LDAP provider params (`schema:params` in
`backend/src/app/auth/ldap.clj`) declared the bind-password slot as
`:bind-passwor` (missing trailing `d`). The runtime code in the same
file uses `:bind-password` everywhere — `prepare-params` reads
`(:bind-password cfg)` on line 21 and `try-connectivity` reads
`(:bind-password cfg)` on line 89. Effects of the typo:

1. The schema slot for `:bind-password` is missing, so a wrong type
   (e.g. a number or vector instead of a string) for the actual key
   slips through `check-params` unvalidated. Malli `[:map ...]` is
   open by default, so the genuine `:bind-password` key is silently
   accepted as an unknown extra key.

2. Anyone reading the schema (operator, future contributor, or
   tooling generating docs) sees a non-existent `:bind-passwor`
   parameter and could legitimately set that key — schema would
   accept it, runtime would never read it, LDAP bind would silently
   fail with a confusing "no password" error.

Cross-checked against the pre-malli `clojure.spec` shape removed in
commit 88fb5e7ab (2024-10-29, "♻️ Update integrant to latest
version", which carried the spec→malli migration). The deleted spec
defined `(s/def ::bind-password ::us/string)` correctly — the typo
was introduced when re-typing the keys into the new malli vector-of-
tuples form.

Add a CHANGES.md entry under the 2.17.0 Unreleased 🐛 Bugs fixed
section.

One-character fix.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-27 09:41:21 +02:00
Luis de Dios a5a8ab5de6 🐛 Fix MCP status is displayed as disabled when setting MCP key without expiration date
Fixes #14058 and #14061 in Taiga
2026-04-27 09:37:11 +02:00
boskodev790 5ee65c5efb 🐛 Fix :hide typo dropping LDAP not-initialized error hint (#9159)
login-with-ldap raised a :restriction exception with the message
"ldap auth provider is not initialized" stored under :hide instead
of :hint. ex/raise (common/src/app/common/exceptions.cljc:33-34)
uses :hint as the ExceptionInfo message and the downstream error
formatters only read :hint (line 250, 312) — :hide is unread
anywhere in the codebase (0 other occurrences vs 447 for :hint).

Effect: when LDAP is misconfigured, operators saw the generic
"restriction" error message instead of the diagnostic string. The
typo has been present since the LDAP command was first introduced
by commit 14d1cb90bd (2022-06-30, "Refactor auth code") and was
carried forward through 6cdf696fc (2023-01-05, "Fix issues on ldap
provider and rpc method") without ever surfacing as a code-review
comment.

One-character fix: :hide -> :hint. Add a CHANGES.md entry under
the 2.17.0 Unreleased 🐛 Bugs fixed section.
2026-04-27 09:30:07 +02:00
Alejandro Alonso 7504c3b53e Merge pull request #9167 from penpot/superalex-fix-text-ellipsis
🐛 Fix text ellipsis merging error
2026-04-27 09:09:14 +02:00
Alejandro Alonso c4e508a606 🐛 Fix text ellipsis merging error 2026-04-27 08:49:22 +02:00
Andrey Antukh 37cba3355d 🔧 Update opencode tooling, agents, and devenv
Update agent configurations: change commiter mode to all, rename
engineer agent to "Penpot Engineer", and remove obsolete testing agent.

Add new read-only planner agent for architecture analysis and planning.

Add four new skills: bat-cat (syntax-highlighted cat clone), fd-find
(fast file finder), jq-json-processor (JSON processor), and ripgrep
(fast text search).

Add fd-find and bat packages to devenv Dockerfile.

Update .gitignore to exclude opencode package-lock and plans directory.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-27 00:25:23 +02:00
Egor Filatov d4955c7b78 🌐 Add translations for: Russian
Currently translated at 80.6% (1673 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-04-25 16:09:52 +02:00
bobsonzu0a5d198e17c343cb 63829d5fb7 🌐 Add translations for: Russian
Currently translated at 80.6% (1673 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-04-25 16:09:50 +02:00
Andrey Antukh 6d9019c383 📚 Improve pull request documentation in CONTRIBUTING.md
Expand the Pull Requests section with detailed guidance on PR title
format, description expectations, branch naming conventions, the review
process, and a list of PRs that will not be accepted. Also clarify the
'Discuss Before Building' rule to link to GitHub Issues and Discussions
and reference Taiga stories. Update the Table of Contents with nested
links for all new subsections.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-25 13:22:17 +00:00
Pablo Alba 700f3e9c10 MR changes 2026-04-24 17:19:41 +02:00
Pablo Alba debfe5490f 🐛 Fix switching a team nitrate organization lose the background 2026-04-24 17:19:41 +02:00
Yamila Moreno 7031052c4e 🐛 Prevent invitations to blacklisted domains 2026-04-24 16:48:59 +02:00
Andrey Antukh 01d68ec09b Merge remote-tracking branch 'origin/staging' into develop 2026-04-24 14:16:03 +02:00
Andrey Antukh 35f8e1b084 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-24 14:09:21 +02:00
Andrey Antukh 0b6416e53b Merge remote-tracking branch 'origin/main' into main-staging 2026-04-24 14:09:03 +02:00
Andrey Antukh d380efdb0c ⬆️ Update devenv dependencies (#9142)
* ⬆️ Update devenv dependencies

*  Fix formatting issues

* 📎 Fix linter issues
2026-04-24 14:07:51 +02:00
moorsecopers99andAndrey Antukh 7e499c5e5f 🐛 Fix Settings/Notifications submit button always active with no changes (#9091)
The "Update Settings" button in Your Account > Settings and Notifications
was always enabled, even when the form had no changes, and clicking it
emitted a success notification despite no data being modified.

Disable the submit button when the current form data equals its initial
state, so it activates only when there are actual changes to persist.

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 13:24:55 +02:00
Juan Flores 38d67c8e96 🐛 Fix Help & Learning submenu vertical alignment in account menu (#9138)
The submenu opened by hovering Help & Learning in the user account
menu rendered with a vertical offset, making it appear visually
disconnected from its parent row and aligned instead with the
Community

Signed-off-by: Juan Flores <112629487+juan-flores077@users.noreply.github.com>
2026-04-24 13:17:57 +02:00
Eva Marco 6c4ab8940d 🐛 Fix colorpicker eyedropper on gradients tab (#9125)
* 🐛 Fix colorpicker eyedropper on gradients tab

* 🐛 Fix gradient test deleting opacity input
2026-04-24 12:48:58 +02:00
boskodev790andAndrey Antukh 9ebd17f31f 🐛 Fix PENPOT_OIDC_USER_INFO_SOURCE flag being silently ignored (#9114)
Closes #9108.

The `case` expression in `get-info` (`backend/src/app/auth/oidc.clj`)
dispatched on `:token` and `:userinfo` keywords, but the provider map's
`:user-info-source` value is a string — both from config (the malli
schema in `app.config` pins it to one of `"token"`, `"userinfo"`,
`"auto"`) and from the hard-coded Google / GitHub provider maps (which
already write `"userinfo"`). Strings never equal keywords in Clojure
`case`, so every call fell through to the auto-fallback that prefers
ID-token claims and only hits the UserInfo endpoint when claims are
empty. The net effect: setting `PENPOT_OIDC_USER_INFO_SOURCE=userinfo`
did nothing, and OIDC flows whose IdP requires the UserInfo endpoint
(so claims come back empty/partial) failed with "incomplete user info".

- Extract a pure helper `select-user-info-source` that maps the raw
  config string to a dispatch keyword (`:token`, `:userinfo`, `:auto`),
  falling back to `:auto` for unknown / missing / accidentally-keyword
  values
- Rewrite `get-info`'s `case` to dispatch on the helper's output so
  the arms unambiguously match the normalised keyword
- Add vitest-style deftests in `auth_oidc_test.clj` pinning the three
  valid strings, the nil / "auto" / unknown fallback, and the reverse
  regression (a keyword input must not slip through as if it were the
  matching string)
- Add a CHANGES.md entry under the 2.17.0 Unreleased `:bug: Bugs fixed`
  section linking back to #9108

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 12:14:46 +02:00
Alejandro Alonso 89a1ee7813 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-24 12:06:27 +02:00
Andrey Antukh 29ba336928 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-24 11:58:50 +02:00
Andrey Antukh cfb076dd61 📚 Update AGENTS.md with common github operations 2026-04-24 11:45:36 +02:00
Eva Marco 4a7140d82d 🐛 Fix theme modal height (#9105)
* 🐛 Fix CI

* 🐛 Fix theme modal height
2026-04-24 11:38:34 +02:00
Pablo Alba 4061673528 Add nitrate api endpoints to get and cancel org invitations (#9124)
*  Add nitrate api endpoints to get and cancel org invitations

*  MR changes
2026-04-24 11:35:53 +02:00
Alejandro Alonso e05ea1392a Merge pull request #9140 from penpot/superalex-fix-merge-develop
🐛 Fix text.cljs error from staging merge
2026-04-24 10:57:59 +02:00
Alejandro Alonso 58fae0a04d 🐛 Fix text.cljs error from staging merge 2026-04-24 10:10:00 +02:00
Alejandro Alonso 078663b0fa 🔧 Fix rust linter errors 2026-04-24 09:52:51 +02:00
Eva MarcoandAndrey Antukh 5a7ba7ee7e 🐛 Fix multiple selection on shapes with token applied to stroke-color (#9110)
*  Remove the need to navigate to page for deletion operation

* 🐛 Fix multiple selection with applied-tokens on stroke-color

* 🐛 Fix button position on page header

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:47:44 +02:00
Alejandro Alonso 7532bf411c Merge remote-tracking branch 'origin/develop' into develop 2026-04-24 09:32:35 +02:00
Alejandro Alonso 984d292ab2 Merge remote-tracking branch 'origin/staging' into develop 2026-04-24 09:29:24 +02:00
Full Stack Developer 25e6b939ba Show detailed messages on file import errors (#9004)
*  Show detailed messages on file import errors

Signed-off-by: jsdevninja <topit89807@gmail.com>

*  Fix test

*  Fix build error

---------

Signed-off-by: jsdevninja <topit89807@gmail.com>
2026-04-24 09:13:46 +02:00
FairyPiggyDevandAndrey Antukh 361c1c574b 🐛 Fix plugin parse-point returning plain map instead of Point record (#9129)
The plugin parser's parse-point returned a plain `{:x … :y …}` map,
but shape interaction schemas (for example schema:open-overlay-interaction)
require the attribute to be a `::gpt/point` record. `(instance? Point {:x 0 :y 0})`
is false, so validation silently rejected plugin `addInteraction` calls
that passed `manualPositionLocation`; only a console warning was produced.

Change parse-point to return a `gpt/point` record via `gpt/point`.
All three call sites (parser.cljs:open-overlay, plugins/page.cljs,
plugins/comments.cljs) continue to work because Point records support
the same `:x`/`:y` access plain maps do.

Add a unit test that covers nil input and verifies the returned value
satisfies `gpt/point?`.

Github #8409

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:12:13 +02:00
Juan Flores 841b2e156e 🐛 Fix typography style creation with tokenized line-height (#9121)
When a text element has a line-height coming from a design token, the value
may be a number (e.g. 1.5) and fails frontend data validation expecting a
string. Normalize line-height before creating the typography style so the
operation succeeds without throwing an assertion error.

Signed-off-by: juan-flores077 <toptalent399@gmail.com>
2026-04-24 09:11:31 +02:00
boskodev790andAndrey Antukh 6c7843f4b6 🐛 Fix obfuscate-email crashing on malformed email or dotless domain (#9120)
The viewer-side `obfuscate-email` helper used by `anonymize-member` when
building share-link bundles called `clojure.string/split` on the raw
email input and then on the extracted domain. Two failure modes:

1. When the stored email had no `@` (legacy data, LDAP-sourced UIDs, direct
   DB inserts, or fixtures that bypassed `::sm/email`), destructuring
   left `domain` bound to `nil` and the follow-up `(str/split nil "." 2)`
   raised `NullPointerException`. Because `obfuscate-email` runs inside
   `get-view-only-bundle`, the exception aborted the whole RPC response
   for share-link viewers, not just the field.

2. When the stored email used a single-label domain (`alice@localhost`),
   `(str/split "localhost" "." 2)` returned `["localhost"]`; destructuring
   bound `rest` to `nil` and the final `(str name "@****." rest)` produced
   a dangling-dot output `"****@****."` (nil coerces to empty in `str`).

Guard both split calls with `(or x "")` so the chain is nil-safe, and
emit the trailing `.<tld>` segment only when `rest` is present. Add three
`deftest` groups covering the happy path, dotless domains, and malformed
inputs (nil / empty / no-`@`), plus a CHANGES.md entry under the 2.17.0
Unreleased bugs-fixed section.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:09:49 +02:00
Renzo 8aacda2249 Add Shift+Numpad0/1/2 zoom shortcut aliases (#2457) (#9063)
Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
2026-04-24 09:08:31 +02:00
50bee5e176 Add clipboard:read/write permissions to plugin system (#6980) (#9053)
*  Add clipboard:read/write permissions to plugin system (#6980)

* 🔧 Fix prettier formatting in clipboard permission files

---------

Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:07:58 +02:00
Andrey Antukh 20c6a28b52 📎 Add commit agent for opencode 2026-04-24 08:54:01 +02:00
Andrey Antukh 7135782e7d Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-24 08:19:47 +02:00
Andrey Antukh fd38f5b431 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-24 08:18:55 +02:00
Andrey Antukh 2d5e50f352 ⬆️ Update root repo deps 2026-04-24 08:17:32 +02:00
e280168de9 Add read-only preview mode for saved versions (#7622) (#8976)
*  Add read-only preview mode for saved versions (#7622)

* 🔧 Address review feedback on version preview (#7622)

* 🐛 Fix version preview for WASM renderer (#7622)

* 🐛 Fix stylelint color-named and color-function-notation in preview banner (#7622)

* 🐛 Fix invalid-arity call to initialize-workspace in exit-preview (#7622)

* 🐛 Fix unclosed defn paren in exit-preview (#7622)

* ♻️ Refactor version preview/restore flow

Separate enter-preview and enter-restore flows with dedicated dialogs
instead of a persistent banner. Removes preview-banner component in favor
of inline actions dialog. Uses backup/restore pattern for exit-preview
instead of full workspace reinitialization. Adds analytics events for
preview/restore actions.

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

*  Extract on-name-input-focus as namespace-level private function

The callback had no dependencies on component-local state or props,
making it a pure function that can be hoisted to a defn-. This avoids
recreating the same callback identity on every render of version-entry*.

*  Extract extract-id-from-event helper to deduplicate snapshot callbacks

Three callbacks in snapshot-entry* shared the same DOM extraction logic
(get current target, read data-id, parse UUID). Extracted into a private
defn- to remove the duplication and simplify each callback.

*  Extract pure state-update callbacks from versions-toolbox* to namespace level

Eight callbacks that only emit fixed Potok events with no meaningful
deps were hoisted out of the component as defn- functions:

- on-create-version
- on-edit-version
- on-cancel-version-edition
- on-rename-version
- on-delete-version
- on-pin-version
- on-lock-version
- on-unlock-version

These no longer need mf/use-fn wrappers since namespace-level functions
have stable identity across renders, avoiding unnecessary callback
recreation on each render cycle.

*  Rename filter parameter to filter-value in on-change-filter to avoid core shadowing

The parameter name 'filter' shadowed clojure.core/filter within the
function scope. Renamed to 'filter-value' for clarity and to prevent
potential bugs if core/filter were needed in future changes.

* 🔧 Fix linter warnings and errors across version-related namespaces

frontend/src/app/main/ui/workspace.cljs:
- Remove unused requires: app.common.data, app.main.data.notifications,
  app.main.data.workspace.versions

frontend/src/app/main/data/workspace/versions.cljs:
- Remove unused require: app.common.uuid
- Fix duplicate reify type: enter-restore used ::restore-version
  (same as the private restore-version fn), renamed to ::enter-restore
- Remove unused bindings: state in enter-restore, team-id in
  exit-preview and restore-version-from-plugin

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Signed-off-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 08:13:16 +02:00
Renzo 7c1a29ccf7 🐛 Remove corepack dependency from MCP server for Node.js 25+ (#9119)
* 🐛 Remove corepack dependency from MCP server for Node.js 25+

* 🐛 Update
2026-04-23 22:08:11 +02:00
Luis de Dios cd417443f6 🐛 Fix layer hierarchy to match old and new SCSS (#9126) 2026-04-23 18:00:40 +02:00
Eva MarcoandAndrey Antukh 0c60db56a2 🐛 Fix multiselection error with typography texts (#9071)
* 🐛 Ensure typography-ref attrs are always present and fix nil encoding

Add :typography-ref-file and :typography-ref-id (both defaulting to nil)
to default-text-attrs so these keys are always present in text node maps,
whether or not a typography is attached.

Skip nil values in attrs-to-styles (Draft.js style encoder) and in
attrs->styles (v2 CSS custom-property mapper) so nil typography-ref
entries are never serialised to CSS.

Replace when with if/acc in get-styles-from-style-declaration to prevent
the accumulator from being clobbered to nil when a mixed-value entry is
skipped during style decoding.

* 🎉 Add test

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-23 16:08:56 +02:00
Marina López a3c330d6e7 Add downgrade nitrate to unlimited modal 2026-04-23 12:54:42 +02:00
Stas Haas 7428cfa684 🌐 Add translations for: German
Currently translated at 95.8% (1988 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-04-23 10:09:50 +00:00
Elena Torro 96722fde4b 🐛 Support EvenOdd SVG attribute across all path operations 2026-04-23 12:02:40 +02:00
Elena Torro 4a549d0907 Drain GPU queue during pan/zoom to avoid render_from_cache hitch 2026-04-23 11:19:51 +02:00
Eva Marco d6b341c053 🐛 Fix color token (#9095) 2026-04-23 10:51:30 +02:00
Eva Marco 5c9696e20c 🐛 Fix color dropdown option update (#9100) 2026-04-23 10:51:20 +02:00
Eva Marco 28b33b9acc 🐛 Fix props on text components (#9099) 2026-04-23 10:49:48 +02:00
Andrey Antukh c6b6b9ce00 📎 Update changelog 2026-04-23 09:59:11 +02:00
Yamila Moreno 5f7de04efe 🚑 Fix email blacklisting (#9122) 2026-04-23 09:42:40 +02:00
Elena Torró d43d1f431f Merge pull request #9112 from penpot/superalex-improve-atlas-growth
🎉 Improve atlas growth
2026-04-23 09:22:39 +02:00
Yamila Moreno dc8073f924 🐳 Add PENPOT_PUBLIC_URI to penpot-frontend 2026-04-23 09:06:10 +02:00
Renzo 5bbb2c5cff 🐛 Fix Copy as SVG for multi-shape selection (#838) (#9066)
Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
2026-04-22 19:46:38 +02:00
Alejandro Alonso 9e990a975a 🎉 Improve atlas growth 2026-04-22 17:21:11 +02:00
Andrey Antukh ba42cc04b7 ♻️ Derive v-sizing from values instead of passing as prop
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 14:17:15 +00:00
Luis de Dios b60695f54a 🐛 Fix indicate that the mcp is disabled if the mcp key has expired
If the mcp key has expired, the switch that indicates the status in the dashboard will appear as disabled, and will show a modal for regenerate the key. It will also appear as disabled in the workspace, not allowing the plugin to connect
2026-04-22 16:00:52 +02:00
Yamila Moreno 3c542a1abc 🐛 Fix email validation (#9037) 2026-04-22 15:59:28 +02:00
DexterityandAndrey Antukh 3fd976c551 🐛 Fix UI bugs in account settings forms (#8997)
Closes #8977
Closes #8979

Signed-off-by: Dexterity <173429049+Dexterity104@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 15:21:02 +02:00
Edwin Rivera 7dbd602d1e 🐛 Fix text export with custom fonts across SVG, PNG and JPG (#9094)
* 🐛 Fix text export with custom fonts across SVG, PNG and JPG

Text layers using custom or non-standard fonts were rendered incorrectly
on export regardless of the target format. The exporter was not resolving
the font face correctly before rasterization/serialization, causing the
output to fall back to a default glyph set and producing broken or
misaligned text. This fix ensures font data is resolved and embedded
consistently in the export pipeline for all output formats.

Signed-off-by: Edwin Rivera <bytelogic772@gmail.com>

* 📚 Add entry to CHANGES.md under 2.17.0

Signed-off-by: edwin-rivera-dev <bytelogic772@gmail.com>

---------

Signed-off-by: Edwin Rivera <bytelogic772@gmail.com>
Signed-off-by: edwin-rivera-dev <bytelogic772@gmail.com>
2026-04-22 15:19:58 +02:00
Juanfran 7d4092eeba 🐛 Fix column name mismatch when accepting org invitation 2026-04-22 14:24:03 +02:00
Elena Torro f673b32567 🐛 Fix image loading callback 2026-04-22 14:00:49 +02:00
Full Stack Developer d384f47253 🐛 Fix internal error on layer prev/next sibling selection (#9003)
Signed-off-by: jsdevninja <topit89807@gmail.com>
2026-04-22 13:59:42 +02:00
Andrey Antukh 8ad30e14b6 Merge remote-tracking branch 'origin/staging' into develop 2026-04-22 13:34:00 +02:00
Andrey Antukh b0b2c0d264 📎 Update version on mcp/ module 2026-04-22 13:18:24 +02:00
Andrey Antukh f00ea8789f 📎 Update version on mcp module 2026-04-22 13:16:34 +02:00
Andrey Antukh 112e81c397 📎 Fix the version reference
Caused by the recent version changes
2026-04-22 13:14:04 +02:00
moorsecopers99andAndrey Antukh b6487015b8 Add loader feedback while importing and exporting files (#9024)
*  Add loader feedback while importing and exporting files

Show a loader icon with a status label ("Importing files…" /
"Exporting files…") in the import and export dialog footers while the
operation is running, so users get clear in-progress feedback and
cannot retrigger the action by mistake.

Closes #9020

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>

*  Address import/export loader feedback PR review

- Show the loader beside file names in the import dialog while files
  are being imported (previously queued entries kept showing the
  Penpot logo until each one moved into :import-progress).
- Drop the loader from the "Importing files…" / "Exporting files…"
  footer status, leaving just the text styled with the modal title
  color, per the design proposal.

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>

*  Match design proposal for import/export progress feedback

- Move the in-progress label from the modal footer into the modal
  body, under the file rows, styled italic with the modal title
  color.
- Rename the labels to match the design wording: "Uploading file…"
  for import and "Downloading file…" for export.
- Restore the disabled "Accept" button in the import footer during
  the import-progress phase, mirroring the disabled "Close" button
  used by export.

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>

* 🐛 Rename deprecated bodySmallTypography mixin to body-small-typography

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>

---------

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 13:12:48 +02:00
Andrey Antukh 88008ce16c 📎 Update mcp types yaml file 2026-04-22 13:11:10 +02:00
Andrey Antukh 75d99a0725 🔧 Add missing public uri handling on nginx entrypoint 2026-04-22 13:11:10 +02:00
Andrey Antukh 09637f9794 Allow render entrypoint load alternative config
The render entrypoint is used by exporter
2026-04-22 13:11:10 +02:00
Andrey Antukh 3225319e0c 🐛 Fix frontend tests 2026-04-22 12:54:07 +02:00
Edwin Rivera 2579527e64 🎉 Add get-file-stats RPC command (#9074)
* 🎉 Add get-file-stats RPC command

Introduce a new lightweight RPC query that returns aggregate statistics
for a single file: page count, shape counts by type, component/color/
typography counts, and inbound and outbound library reference counts.
Mirrors the existing get-file-summary permission and decoding pattern.

Useful for plugin authors enforcing per-file budgets, the
@penpot/library npm SDK, and future admin dashboards. Purely additive
— no migrations, no UI, no breaking changes.

Signed-off-by: edwin-rivera-dev <bytelogic772@gmail.com>

* 🐛 Bind *load-fn* around file data walk in get-file-stats

The binding previously wrapped only  — a plain key
lookup that does not realize any pointers — so by the time
 walked  and accessed  on
each page,  was unbound and every PointerMap
dereference threw , failing the three new  tests.

Move  inside the  form so the walk runs
with  available, matching the existing pattern used in
.

Signed-off-by: Edwin Rivera <bytelogic772@gmail.com>

---------

Signed-off-by: edwin-rivera-dev <bytelogic772@gmail.com>
Signed-off-by: Edwin Rivera <bytelogic772@gmail.com>
2026-04-22 12:49:39 +02:00
Elena Torró 3561b2d1eb Merge pull request #9078 from penpot/alotor-fix-thumbnail-errors
🐛 Fix errors in thumbnails
2026-04-22 12:00:56 +02:00
Elena Torró ba1842792f Merge pull request #9076 from penpot/alotor-fix-flex-align-self
🐛 Fix default alignself behavior
2026-04-22 11:55:00 +02:00
Eva Marco 09fca1c820 🐛 Fix tooltip appearing two times when nested elements (#9031) 2026-04-22 11:33:43 +02:00
Aitor Moreno e3981a0cf3 Merge pull request #9077 from penpot/ladybenko-13971-fix-trailing-whitespace
🐛 Fix trailing whitespace behavior in v2 editor
2026-04-22 11:30:01 +02:00
Eva Marco c02f0a2bc9 🐛 Fix grow options (#9097)
* 🐛 Fix grow options

* 🐛 Fix props when hidden is nil
2026-04-22 11:09:44 +02:00
Yamila Moreno 6de5370a0b 🐛 Fix nginx configuration for mcp 2026-04-22 10:29:52 +02:00
Andrey Antukh 448b5d4786 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-22 09:58:20 +02:00
Andrey Antukh 47b3667248 🐛 Fix exporter renderer URI path construction
Apply consistent path construction across bitmap, PDF, and SVG
renderers in the exporter. Use path join utilities instead of
hardcoding the render.html path, ensuring the path is properly
appended to the public URI base path.

- bitmap.cljs: Use u/ensure-path-slash and u/join for path
- pdf.cljs: Use u/join and ensure-path-slash on base-uri
- svg.cljs: Use u/ensure-path-slash and u/join for path
2026-04-22 09:52:44 +02:00
Andrey Antukh 98e8160875 ♻️ Remove worker URI from global templates and compute from public URI
- Remove penpotWorkerURI from index.mustache and rasterizer.mustache templates
- Remove worker_main entry from the build manifest
- Construct worker URI in config.cljs by joining public-uri with worker path
- Fix global variable casing for plugins-list-uri and templates-uri
- Fix alignment in worker.cljs let bindings
2026-04-22 09:52:44 +02:00
Eva Marco d549be3376 ♻️ Remove duplicated code (#9096) 2026-04-22 09:39:38 +02:00
María Valderrama b67394199b Add the ability to upload organization profile image
*  Upload org logo

* 📎 Code review

* 📎 Code review 2

* 📎 Code review 3
2026-04-22 09:37:09 +02:00
Yamila Moreno 6ea7a64e01 Add nginx configuration for mcp server 2026-04-22 09:33:58 +02:00
Pablo Alba 534701f04f 🐛 Fix org options space should be hidden when there are no options 2026-04-22 09:33:42 +02:00
Pablo Alba ad974f4047 💄 Unify naming on nitrate-api 2026-04-22 09:31:09 +02:00
Alejandro Alonso 8bf8601d29 Merge pull request #9093 from penpot/superalex-avoid-unnecesary-rerenders-on-frame-hover
🐛 Avoid unnecessary repainting of frames when mouse hover
2026-04-22 09:28:49 +02:00
Andrey Antukh 81faa5a728 Replace duplicate on-blur lambda with stable on-text-blur ref
The inline (fn [] (ts/schedule ...)) passed as :on-blur to text-options
was an exact copy of the on-text-blur callback already defined via
mf/use-fn earlier in the same let block. Pass on-text-blur directly
to avoid allocating a new function object on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh 7751d9a69b Remove spurious deps from toggle callbacks in text-menu*
toggle-main-menu and toggle-more-options close over a state atom and
need no external deps. The declared deps (main-menu-open? /
more-options-open?) were unused inside the function bodies, causing
each callback to be reallocated on every toggle — self-reinforcing
churn. Drop the mf/deps calls to make both callbacks stable.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh 74d1288003 Hoist token-typography-row? flag check to namespace level
(contains? cf/flags :token-typography-row) is a pure constant:
cf/flags is immutable after startup. Define it once as a private
namespace-level var token-typography-row? instead of re-evaluating
the check on every render in text-decoration-options* and text-menu*.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh d28c0ea066 Memoize label computation in text-menu* component
Wrap the (case type (tr ...) ...) expression in mf/with-memo [type]
so the translation is resolved only when the type prop changes
instead of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh a94a7221fb Memoize options in text-decoration-options* component
Wrap the two radio-button option maps in mf/with-memo [token-applied]
so the vector and its (tr ...) calls are evaluated only when the
token-applied prop changes, not on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh b8aa243c2b Memoize static options in grow-options* component
Wrap the three radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh dfd992aa49 Memoize static options in text-direction-options* component
Wrap the two radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh 466f27eb7c Memoize static options in text-align-options* component
Wrap the four radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh 6c19c7c0c4 Memoize static options in vertical-align* component
Wrap the radio-button options vector in `mf/with-memo []` so the
vector allocation and `(tr ...)` calls happen once per component
mount instead of on every render.

Also document the translation memoization rule in frontend/AGENTS.md:
`(tr ...)` must never be called at namespace level (locale is
runtime-only), and static option lists should always be wrapped in
`mf/with-memo []`.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh 97d234a566 Add 2h min-age threshold to storage/gc_touched task
Skip storage objects touched less than 2 hours ago, matching the pattern
used by upload-session-gc. Update all affected tests to advance the clock
past the threshold using ct/*clock* bindings.
2026-04-22 08:48:04 +02:00
Marina López 11c970a945 Add nitrate trial text 2026-04-22 08:17:28 +02:00
Alejandro Alonso ca97a28408 🐛 Avoid unnecesary repainting of frames when mouse hover 2026-04-22 07:37:38 +02:00
Andrey Antukh 6723e3bbea 🐛 Fix issues after staging merge 2026-04-21 22:52:19 +02:00
Andrey Antukh c259fbdb5b Merge remote-tracking branch 'origin/staging' into develop 2026-04-21 22:43:46 +02:00
Andrey Antukh f331325941 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-21 21:42:03 +02:00
Andrey Antukh f716995ffd 📚 Update changelog 2026-04-21 21:08:57 +02:00
Andrey Antukh e5f9c1e863 🎉 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-21 18:51:10 +00:00
Andrey Antukh a395768987 🐛 Fix incorrect handlig of version restore operation (#9041)
- Add session ID tracking to RPC layer (backend and frontend)
- Send session ID header with RPC requests for request correlation
- Rename file-restore to file-restored for consistency
- Extract initialize-file function from initialize-workspace flow
- Improve file restoration initialization with wait-for-persistence
- Extract initialize-version event handler for version restoration
- Fix viewport key generation with file version numbers for proper re-renders
- Update layout item schema and constraints to use internal sizing state
- Add v-sizing state retrieval in layout-size-constraints component
- Refactor file-change notifications stream handling with rx/map
- Fix team-id lookup in restore-version-from-plugins

Improves request traceability across frontend/backend sessions and streamlines
the workspace initialization flow for file restoration scenarios.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 20:50:41 +02:00
Andrey Antukh 8f2c467b82 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-21 20:44:31 +02:00
Andrey Antukh c1688edf66 ♻️ Convert frame-grid and grid-options to modern * suffix
Rename grid-options -> grid-options*, frame-grid -> frame-grid*.
Update internal call and call site in shapes/frame.cljs to [:> ...] syntax.
2026-04-21 20:31:30 +02:00
Andrey Antukh 08247aec3f ♻️ Convert svg-attrs-menu and attribute-value to modern * suffix
Rename attribute-value -> attribute-value*, svg-attrs-menu -> svg-attrs-menu*.
Update all call sites (circle, path, svg_raw, rect, group) to use [:> ...] syntax.
2026-04-21 20:31:30 +02:00
Andrey Antukh 95ca68e2b8 ♻️ Convert history-entry-details to modern rumext * format
Rename to history-entry-details* and update internal call site.
2026-04-21 20:31:30 +02:00
Andrey Antukh e9e6796f05 ♻️ Convert text-menu and sub-components to modern rumext * format
Rename text-align-options, text-direction-options, vertical-align,
grow-options, text-decoration-options and text-menu to their * variants.
Update all call sites in shapes/text.cljs, shapes/group.cljs and
shapes/multiple.cljs.
2026-04-21 20:31:30 +02:00
Andrey Antukh 0e5d3e2619 ♻️ Convert blur-menu, constraints-menu and stroke-menu to modern rumext * format
Rename components to blur-menu*, constraints-menu* and stroke-menu*.

Update :refer imports and all [:& ...] call sites to [:> ...*] for
blur-menu*, constraints-menu* and stroke-menu* across all nine
shapes-specific options panels.
2026-04-21 20:31:30 +02:00
Andrey Antukh f0d6e8cb2f ♻️ Convert text-edition-outline to modern rumext * format
Rename to text-edition-outline*, update call sites in viewport.cljs and
viewport_wasm.cljs to [:> text-edition-outline* ...].
2026-04-21 20:31:30 +02:00
Andrey Antukh 304a324529 ♻️ Convert image-upload to modern rumext * format
Rename to image-upload*, update internal call site in top-toolbar* to
[:> image-upload*].
2026-04-21 20:31:30 +02:00
Andrey Antukh 14ff56bc89 ♻️ Convert text-palette-ctx-menu to modern rumext * format
Rename to text-palette-ctx-menu*, rename show-menu? prop to show-menu,
update call site in palette.cljs to [:> text-palette-ctx-menu* ...].
2026-04-21 20:31:30 +02:00
Andrey Antukh d8340d765a Merge remote-tracking branch 'origin/staging' into develop 2026-04-21 20:28:38 +02:00
Andrey Antukh 2bbd63287f Merge remote-tracking branch 'origin/main' into staging 2026-04-21 19:22:50 +02:00
Andrey Antukh 6eccffb8bb 🐛 Fix incorrect handlig of version restore operation (#9041)
- Add session ID tracking to RPC layer (backend and frontend)
- Send session ID header with RPC requests for request correlation
- Rename file-restore to file-restored for consistency
- Extract initialize-file function from initialize-workspace flow
- Improve file restoration initialization with wait-for-persistence
- Extract initialize-version event handler for version restoration
- Fix viewport key generation with file version numbers for proper re-renders
- Update layout item schema and constraints to use internal sizing state
- Add v-sizing state retrieval in layout-size-constraints component
- Refactor file-change notifications stream handling with rx/map
- Fix team-id lookup in restore-version-from-plugins

Improves request traceability across frontend/backend sessions and streamlines
the workspace initialization flow for file restoration scenarios.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 19:19:51 +02:00
moorsecopers99andAndrey Antukh 95b2d7b083 🐛 Add ability to delete uploaded profile avatar (#9068)
Fixes #9067. Adds a delete button that appears on hover over an
uploaded profile photo; clicking it opens a confirm modal and, on
accept, clears the stored photo so the generated fallback avatar is
shown again. A new :delete-profile-photo RPC schedules the old
storage object for garbage collection and sets photo-id to null.

Signed-off-by: moorsecopers99 <patellscott18@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 19:19:30 +02:00
DexterityandAndrey Antukh bb91c06390 🐛 Show check icon after copying team invitation link (#8996)
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 17:32:07 +02:00
DexterityandAndrey Antukh e1d3106f61 Add color customization for ruler guides (#8986)
*  Add customizable colors for ruler guides

*  Update CHANGES.md

* 💄 Move guide color menu styles to SCSS

* 💄 Fix trailing whitespace in guides.cljs

---------

Signed-off-by: Dexterity <173429049+Dexterity104@users.noreply.github.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 17:31:39 +02:00
Andrey Antukh aed2f8a8f8 🐛 Fix removeChild errors from unmount race conditions (#8927)
Guard imperative DOM operations (removeChild, RAF callbacks) against
race conditions where React has already unmounted the target nodes.

- assets/common.cljs: add dom/child? guard before removeChild in RAF
- dynamic_modifiers.cljs: capture RAF IDs and cancel them on cleanup;
  add null guards for DOM nodes that may no longer exist
- hooks.cljs: guard portal container removal with dom/child? check
- errors.cljs: extract is-ignorable-exception? to a top-level defn
  and add NotFoundError/removeChild to ignorable exceptions, since
  these are caused by browser extensions modifying React-managed DOM
- Add unit tests for is-ignorable-exception? predicate

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 17:31:05 +02:00
Dr. Dominik Jain 77560b9305 Encourage use of layouts and proper naming #9081 (#9084)
Improve MCP instructions on design creation:
 * Agents should make use of layouts when appropriate
 * Agents should name all elements appropriately
2026-04-21 17:23:58 +02:00
Pablo Alba cd320c0cd6 On profile deletion, remove the user from nitrate too 2026-04-21 15:44:37 +02:00
Belén Albeza f9f3955503 🐛 Fix trailing whitespace behavior in v2 editor 2026-04-21 15:43:10 +02:00
Yamila Moreno f19c968bc6 🔧 Add main-staging workflow 2026-04-21 15:40:51 +02:00
Yamila Moreno d5cf7dcf9d 🔧 Add main-staging workflow 2026-04-21 15:40:37 +02:00
Yamila Moreno bd82829cb7 🔧 Add main-staging workflow 2026-04-21 15:40:16 +02:00
Yamila Moreno 66e34950b2 🔧 Add main-staging workflow 2026-04-21 15:39:35 +02:00
alonso.torres 2901d00862 🐛 Fix errors in thumbnails 2026-04-21 15:35:06 +02:00
Eva Marco f18670ed00 🐛 Fix errors from numeric input design review (#8993)
* 🐛 Fix blur input after enter value

* 🐛 Catch error on invalid maths

* 🐛 Fix race condition

* 🎉 Add tests that cover issues

* 🐛 Fix padding applying only to one side

* 🐛 Fix show broken pill when reference is on not active set
2026-04-21 14:39:06 +02:00
alonso.torres 719f4a5035 🐛 Fix default alignself behavior 2026-04-21 14:33:47 +02:00
Elena Torró c636517499 Merge pull request #9072 from penpot/alotor-fix-swap-component
🐛 Fix problem on component swap
2026-04-21 13:53:21 +02:00
Elena Torró 04f29a0d72 Merge pull request #9059 from penpot/azazeln28-fix-caret-dimensions
🐛 Fix caret dimensions
2026-04-21 13:41:07 +02:00
Xaviju 78c48f1953 🐛 Fix broken update library notification link UI (#9070)
* 🐛 Fix broken update library notification link UI

* ♻️ Format and lint
2026-04-21 13:33:38 +02:00
alonso.torres f89f4e0047 🐛 Fix problem on component swap 2026-04-21 13:11:03 +02:00
alonso.torres 3da74ed864 🐛 Fix problem with component thumbnails in swap component panel 2026-04-21 13:11:03 +02:00
Aitor Moreno 612855452a 🎉 Add render perf options 2026-04-21 11:43:22 +02:00
Elena Torro 62ec66b974 Add lazy async rendering for component thumbnails 2026-04-21 11:40:53 +02:00
alonso.torres 88ec9e4ff1 🐛 Fix problem with store font after cleanup 2026-04-21 11:03:04 +02:00
Xaviju cd9151bf9f 🐛 Fix duplicate modal title (#9064) 2026-04-21 09:54:30 +02:00
Aitor Moreno 7efc4d6d53 🐛 Fix caret dimensions 2026-04-21 09:44:44 +02:00
Elena Torró 0b49c1f3e9 Merge pull request #9069 from penpot/superalex-improve-modifiers-flickering
🐛 Avoid sequential tile draws and flicker during shape transforms
2026-04-21 09:20:29 +02:00
Alejandro Alonso 0d17debde7 Merge remote-tracking branch 'origin/staging' into develop 2026-04-21 08:24:29 +02:00
Alejandro Alonso 98c8bb1746 🐛 Avoid sequential tile draws and flicker during shape transforms 2026-04-21 07:45:27 +02:00
Xaviju e9105f3670 ♻️ Fix linter errors under legacy resources scss (#9035) 2026-04-20 23:58:53 +02:00
Andrey Antukh eeeb698d91 ⬆️ Bump opencode-ai dev dependency 1.4.3 -> 1.14.19
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-20 20:13:15 +02:00
Andrey Antukh 3a39676969 Backport MCP from staging (part 1) 2026-04-20 19:37:02 +02:00
alonso.torres c42eb6ff86 🐛 Fix problem with selection performance 2026-04-20 17:30:07 +02:00
Elena Torro b5701923ba 🐛 Fix dragging shape 2026-04-20 16:49:18 +02:00
Elena Torró 9ba8d4667c Merge pull request #9044 from penpot/superalex-atlas-fixes
🐛 Atlas fixes
2026-04-20 16:23:06 +02:00
Alejandro Alonso 1d454f3790 🐛 Fix stale tile cache when flex reflow changes modifier set between frames 2026-04-20 15:50:22 +02:00
Juan de la CruzandAndrey Antukh 876b8d645d 🎉 Add new page separators feature (#8561)
* 🎉 Add new page separators feature

* 📎 Add PR feedback changes

* 🐛 Fix page sitemap icons

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-20 15:33:42 +02:00
Eva Marco adea81ceee ♻️ Update icon on typography section and scss files (#9021)
* ♻️ Update icon on new buttons

* ♻️ Update scss on typography section
2026-04-20 15:32:55 +02:00
Alejandro Alonso bc9496deaa 🎉 Replace run_script tiles-complete dispatch with wapi extern binding 2026-04-20 15:29:45 +02:00
Alejandro Alonso 88dbfe7602 🐛 Fix restore renderer state after thumbnail render_shape_pixels 2026-04-20 15:27:46 +02:00
Alejandro Alonso 9cf787d154 🐛 Update atlas when removing shape 2026-04-20 15:27:46 +02:00
Elena Torró 3f0d103cb3 Merge pull request #9036 from penpot/alotor-fix-thumbnail-images
🐛 Fix problem with dashboard thumbnails images
2026-04-20 13:03:53 +02:00
Eva Marco 003b54421d 🐛 Fix empty warning on login (#9056) 2026-04-20 12:23:47 +02:00
Pablo Alba 73b55ee47e Add nitrate api method get-remove-from-org-summary 2026-04-20 11:18:07 +02:00
Pablo Alba ae66317d6c Add nitrate api to remove user from org 2026-04-20 11:18:07 +02:00
Pablo Alba b2c9e08d42 🐛 Fix bad check on leave nitrate 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
DreamandAndrey Antukh 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
Stas Haas e14de6ea30 🌐 Add translations for: German
Currently translated at 95.2% (1976 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-04-18 13:10:42 +00:00
wdeveloper16andwdeveloper16 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
Alejandro Alonso ec773703cc Merge pull request #9042 from penpot/alotor-fix-thumbnail-generation
🐛 Fix thumbnail generation
2026-04-17 13:06:43 +02:00
alonso.torres 97496d8ad7 🐛 Fix thumbnail generation 2026-04-17 12:30:13 +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
Alejandro Alonso f1f612f265 Merge pull request #9038 from penpot/andy-antialias-threshold
 Update antialias threshold
2026-04-17 11:24:22 +02:00
Andres Gonzalez ea53d24dde Update antialias threshold 2026-04-17 10:55:21 +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
Ingrid Pigueron e54e02b736 🌐 Add translations for: French
Currently translated at 96.8% (2009 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-04-16 18:09:53 +00: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
Andrey Antukh 974beca12d Add 2h min-age threshold to storage/gc_touched task
Skip storage objects touched less than 2 hours ago, matching the pattern
used by upload-session-gc. Update all affected tests to advance the clock
past the threshold using ct/*clock* bindings.
2026-04-16 19:43:57 +02:00
Yamila Moreno b38912f3cb 🔧 Add short tag to DocherHub release (#8864) 2026-04-16 18:22:22 +02:00
Yamila Moreno 697de53c16 🔧 Add short tag to DocherHub release (#8864) 2026-04-16 18:21:35 +02:00
Yamila Moreno 32d9688c3c 🔧 Add short tag to DocherHub release (#8864) 2026-04-16 18:20:44 +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 65a0fcb15b 🐛 Fix on nitrate leave org default org team must be deleted if empty 2026-04-16 11:45:37 +02:00
Pablo Alba ac472c615a 🐛 Fix nitrate invitations org ux review 2026-04-16 11:18:11 +02:00
aliworksx08andAndrey Antukh 81061013b1 Add openid-attr support and dot notation for OIDC attribute (#8946)
*  Add openid-attr support and dot notation for OIDC attribute paths

* ♻️ Simplify OIDC: add dot-notation for attr paths and retain sub claim

* ♻️ Fix OIDC: fix

* 🐛 Fix OIDC nested attr lookup for dot notation

* ♻️ Remove unused OIDC openid-attr support

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-16 11:12:37 +02:00
Andrey Antukh b5922d32ca Merge remote-tracking branch 'origin/main' into staging 2026-04-16 10:59:36 +02:00
Andrey Antukh b2f173675e 📎 Fix changelog 2026-04-16 10:56:44 +02:00
Andrey Antukh 69e505a6a2 📎 Update changelog 2026-04-16 10:21:15 +02:00
Andrey Antukh 390796f36e 📎 Update changelog 2026-04-16 10:20:05 +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 146219a439 Add tests for app.common.geom namespaces 2026-04-15 23:37:53 +02:00
Andrey Antukh fa89790fd6 🐛 Fix grid layout case dispatch, divide-by-zero, and add set-auto-multi-span tests
Three critical fixes for app.common.geom.shapes.grid-layout.layout-data:

1. case dispatch on runtime booleans in get-cell-data (case→cond fix)
   In get-cell-data, column-gap and row-gap were computed with (case ...)
   using boolean locals auto-width? and auto-height? as dispatch values.
   In Clojure/ClojureScript, case compares against compile-time constants,
   so those branches never matched at runtime. Replaced both case forms
   with cond, using explicit equality tests for keyword branches.

2. divide-by-zero guards in fr/auto/span calc (JVM ArithmeticException fix)
   Guard against JVM ArithmeticException when all grid tracks are fixed
   (no flex or auto tracks):
   - (get allocated %1) → (get allocated %1 0) in set-auto-multi-span
   - (get allocate-fr-tracks %1) → (get allocate-fr-tracks %1 0) in set-flex-multi-span
   - (/ fr-column/row-space column/row-frs) guarded with (zero?) check
   - (/ auto-column/row-space column/row-autos) guarded with (zero?) check
   In JS, integer division by zero produces Infinity (caught by mth/finite),
   but on the JVM it throws before mth/finite can intercept.

3. Exhaustive tests for set-auto-multi-span behavior
   Cover all code paths and edge cases:
   - span=1 cells filtered out (unchanged track-list)
   - empty shape-cells no-op
   - even split across multiple auto tracks
   - gap deduction per extra span step
   - fixed track reducing budget; only auto tracks grow
   - smaller children not shrinking existing track sizes (max semantics)
   - flex tracks causing cell exclusion (handled by set-flex-multi-span)
   - non-spanned tracks preserved via (get allocated %1 0) default
   - :row type symmetry with :column type
   - row-gap correctly deducted in :row mode
   - documents that (sort-by span -) yields ascending order (smaller spans
     first), correcting the misleading code comment

All tests pass on both JS (Node.js) and JVM environments.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh 71904c9ab6 🐛 Fix CLJS bounds-map deduplication and update intersect test
In the CLJS branch of resolve-modif-tree-ids, get-parent-seq returns
shape maps, but the js/Set was populated with UUIDs. As a result,
.has and .add were passing full shape maps instead of their :id
values, so parent deduplication never worked in ClojureScript.
Fixed both .has and .add calls to extract (:id %) from the shape map.

Also update the collinear-overlap test in geom-shapes-intersect-test
to expect true now that the ::coplanar keyword fix (commit 847bf51)
makes on-segment? collinear checks actually reachable.
2026-04-15 23:37:53 +02:00
Andrey Antukh d13e464ed1 🐛 Fix three flex layout bugs in drop-area, positions and layout-data
drop_area.cljc: v-end? was guarded by row? instead of col?, making
vertical-end alignment check fire under horizontal layout conditions.
Aligned with v-center? which correctly uses col?.

positions.cljc: In get-base-line, the col? around? branch passed 2 as
a third argument to max instead of as a divisor in (/ free-width
num-lines 2). This made the offset clamp to at least 2 pixels rather
than computing half the per-line free space. Fixed parenthesization.

layout_data.cljc: The second cond branch (and col? space-evenly?
auto-height?) was permanently unreachable because the preceding branch
(and col? space-evenly?) is a strict superset. Removed the dead branch.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh 7e9fac4f35 🐛 Fix constraint-modifier :default arity mismatch
All concrete constraint-modifier methods accept 6 arguments
(type, axis, child-before, parent-before, child-after, parent-after)
but the :default fallback only declared 5 parameters. Any unknown
constraint type would therefore receive 6 args and throw an arity
error at runtime. Added the missing sixth underscore parameter.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh 80124657b8 🐛 Fix double rotation negation in adjust-shape-flips
When both dot-x and dot-y were negative (both axes flipped),
(update :rotation -) was applied twice which cancelled itself out,
leaving rotation unchanged. The intended behaviour is to negate
rotation once per flip, but flipping both axes simultaneously is
equivalent to a 180° rotation and should not alter the stored angle.

Replaced the two separate conditional rotation updates with a single
one gated on (not= (neg? dot-x) (neg? dot-y)) so the rotation is
negated only when exactly one axis is flipped.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh cf47d5e53e 🐛 Fix coplanar keyword mismatch in intersect-segments?
orientation returns the auto-qualified keyword ::coplanar
(app.common.geom.shapes.intersect/coplanar) but intersect-segments?
was comparing against the plain unqualified :coplanar keyword, which
never matches. This caused all collinear/on-segment edge cases to be
silently skipped, potentially missing valid segment intersections.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh adfe4c3945 🐛 Fix update-rect :size and unqualified abs in corners->rect
update-rect with :size type was only updating :x2 and :y2 but not
:x1 and :y1, leaving the Rect record in an inconsistent state (x1/y1
would not match x/y). Aligned its behaviour with update-rect! which
correctly updates all four corner fields.

corners->rect was calling unqualified abs which is not imported in
app.common.geom.rect namespace. Replaced with mth/abs which is
the proper namespaced version already available in the ns require.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh 179bb51c76 🐛 Fix gpt/multiply docstring and gpt/abs Point record downcast
gpt/multiply had a copy-paste docstring from gpt/subtract claiming it
performs subtraction; corrected to accurately describe multiplication.

gpt/abs was using clojure.core/update on a Point record, which returns
a plain IPersistentMap instead of a Point instance, causing point?
checks on the result to return false. Replaced with a direct pos->Point
constructor call using mth/abs on each coordinate.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +02:00
Andrey Antukh 3d4c914daa 🐛 Fix trailing comma in matrix->str and remove duplicate dispatch
matrix->str was producing malformed strings like '1,0,0,1,0,0,'
instead of '1,0,0,1,0,0', breaking string serialization of matrix
values used in transit and print-dup handlers.

Also remove the first pp/simple-dispatch registration for Matrix at
line 362 which was dead code shadowed by the identical registration
further down in the file.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-15 23:37:53 +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
Andrey Antukh a7e362dbfe 📎 Add commented helpers on backend _env for testing nexus 2026-04-15 18:03:11 +02:00
Andrey Antukh f8f7a0828e Add missing indexes on audit_log table 2026-04-15 18:02:13 +02:00
Elena Torró e186a27174 Merge pull request #9019 from penpot/azazeln28-fix-pointer-selection
🐛 Fix text editor v3 pointer selection
2026-04-15 17:15:20 +02:00
Aitor Moreno 1477758656 🐛 Fix pointer selection 2026-04-15 16:44:24 +02:00
Elena Torro 41bc8c9b9d 🐛 Fix masked shapes causing render cuts at tile boundaries 2026-04-15 16:36:16 +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
Ingrid Pigueron e131fba675 🌐 Add translations for: French
Currently translated at 95.9% (1989 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-04-15 13:09:52 +00:00
Stas Haas 44536e2eaa 🌐 Add translations for: German
Currently translated at 95.2% (1975 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-04-15 13:09:50 +00: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
Elena Torró 9cd1542dd9 Merge pull request #9000 from penpot/niwinz-main-bugfixes
🐛 Add several fixes for app.common.types namespace
2026-04-15 11:51:14 +02:00
Andrey Antukh 2e97f01838 🐛 Fix safe-subvec 2-arity rejecting start=0
The guard used (> start 0) instead of (>= start 0), so
(safe-subvec v 0) returned nil instead of the full vector.
2026-04-15 11:42:49 +02:00
Andrey Antukh 176edadb6f 🐛 Fix nan? returning false for ##NaN on JVM
Clojure's = uses .equals on doubles, and Double.equals(Double.NaN)
returns true, so (not= v v) was always false for NaN. Use
Double/isNaN with a number? guard instead.
2026-04-15 11:42:49 +02:00
Andrey Antukh b26ef158ef 📚 Fix typos in vec2, zip-all, and map-perm docstrings 2026-04-15 11:42:49 +02:00
Andrey Antukh 95d4d42c91 🐛 Add missing string? guard to num-string? on JVM
The CLJS branch of num-string? checked (string? v) first, but the
JVM branch did not. Passing non-string values (nil, keywords, etc.)
would rely on exception handling inside parse-double for control
flow. Add the string? check for consistency and to avoid using
exceptions for normal control flow.
2026-04-15 11:42:49 +02:00
Andrey Antukh bba3610b7b ♻️ Rename shadowed 'fn' parameter to 'pred' in removev
The removev function used 'fn' as its predicate parameter name,
which shadows clojure.core/fn. Rename to 'pred' for clarity and
to follow the naming convention used elsewhere in the namespace.
2026-04-15 11:42:49 +02:00
Andrey Antukh 83da487b24 🐛 Fix append-class producing leading space for empty class
When called with an empty string as the base class, append-class
was producing " bar" (with a leading space) because (some? "")
returns true. Use (seq class) instead to treat both nil and empty
string as absent, avoiding invalid CSS class strings with leading
whitespace.
2026-04-15 11:42:49 +02:00
Andrey Antukh da8e44147c Remove redundant str call in format-number
format-precision already returns a string, so wrapping its result
in an additional (str ...) call was unnecessary.
2026-04-15 11:42:49 +02:00
Andrey Antukh 69e25a4998 📚 Fix typo in namespace docstring ('if' -> 'of') 2026-04-15 11:42:49 +02:00
Andrey Antukh eca9b63d68 Remove redundant map lookups in map-diff
The :else branch of diff-attr was calling (get m1 key) and
(get m2 key) again, but v1 and v2 were already bound to those
exact values. Reuse the existing bindings to avoid the extra
lookups.
2026-04-15 11:42:49 +02:00
Andrey Antukh 29ea1cc495 📚 Fix misleading without-obj docstring
The docstring claimed the function removes nil values in addition to
the specified object, but the implementation only removes elements
equal to the given object. Fix the docstring in both data.cljc and
the local copy in files/changes.cljc.
2026-04-15 11:42:49 +02:00
Andrey Antukh d73ab3ec92 🐛 Fix safe-subvec 3-arity evaluating (count v) before nil check
The 3-arity of safe-subvec called (count v) in a let binding before
checking (some? v). While (count nil) returns 0 in Clojure and does
not crash, the nil guard was dead code. Restructure to check (some? v)
first with an outer when, then compute size inside the guarded block.
2026-04-15 11:42:49 +02:00
Andrey Antukh 1cc860807e Use seq/next idiom in enumerate instead of empty?/rest
Replace (empty? items) + (rest items) with (seq items) + (next items)
in enumerate. The seq/next pattern is idiomatic Clojure and avoids
the overhead of empty? which internally calls seq and then negates.
2026-04-15 11:42:49 +02:00
Andrey Antukh 92dd5d9954 🐛 Fix index-of-pred early termination on nil elements
The index-of-pred function used (nil? c) to detect end-of-collection,
which caused premature termination when the collection contained nil
values. Rewrite using (seq coll) / (next s) pattern to correctly
distinguish between nil elements and end-of-sequence.
2026-04-15 11:42:49 +02:00
Andrey Antukh 057c6ddc0d 🐛 Fix deep-mapm double-applying mfn on leaf entries
The deep-mapm function was applying the mapping function twice on
leaf entries (non-map, non-vector values): once when destructuring
the entry, and again on the already-transformed result in the else
branch. Now mfn is applied exactly once per entry.
2026-04-15 11:42:49 +02:00
Andrey Antukh a2e6abcb72 🐛 Fix spurious argument to dissoc in patch-object
The patch-object function was calling (dissoc object key value) when
handling nil values. Since dissoc treats each argument after the map
as a key to remove, this was also removing nil as a key from the map.
The correct call is (dissoc object key).
2026-04-15 11:42:49 +02:00
Xaviju 431056404c 🎉 Save tokens tree state in local storage (#8922) 2026-04-15 11:24:01 +02:00
Clayton 5dec75fe62 📚 Clarify manifest version 2 for relative plugin asset paths (#8992)
Signed-off-by: Clayton <claytonlin1110@gmail.com>
2026-04-15 10:38:53 +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
Juanfran de577a803c 🎉 Add get-org-member-team-counts endpoint to Nitrate API 2026-04-15 08:50:13 +02:00
Andrés MoyaandAndrey Antukh 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
Andrey Antukh 6d1d044588 ♻️ Move app.common.types.color tests to their own namespace
Tests that exercise app.common.types.color were living inside
common-tests.colors-test alongside the app.common.colors tests. Move
them to common-tests.types.color-test so the test namespace mirrors
the source namespace structure, consistent with the rest of the
types/ test suite.

The [app.common.types.color :as colors] require is removed from
colors_test.cljc; the new file is registered in runner.cljc.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 1e0f10814e 🔥 Remove duplicate gradient helpers from app.common.colors
The five functions interpolate-color, offset-spread, uniform-spread?,
uniform-spread, and interpolate-gradient duplicated the canonical
implementations in app.common.types.color. The copies in colors.cljc
also contained two bugs: a division-by-zero in offset-spread when
num=1, and a crash on nil idx in interpolate-gradient.

All production callers already use app.common.types.color. The
duplicate tests that exercised the old copies are removed; their
coverage is absorbed into expanded tests under the types-* suite,
including a new nil-idx guard test and a single-stop no-crash test.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh db7c646568 Add missing tests for session bug fixes and uniform-spread?
Add indexed-access-with-default in fill_test.cljc to cover the two-arity
(nth fills i default) form on both valid and out-of-range indices, directly
exercising the CLJS Fills -nth path fixed in 593cf125.

Add segment-content->selrect-multi-line in path_data_test.cljc to cover
content->selrect on a subpath with multiple consecutive line-to commands
where move-p diverges from from-p, confirming the bounding box matches
both the expected coordinates and the reference implementation; this
guards the calculate-extremities fix in bb5a04c7.

Add types-uniform-spread? in colors_test.cljc to cover
app.common.types.color/uniform-spread?, which had no dedicated tests.
Exercises the uniform case (via uniform-spread), the two-stop edge case,
wrong-offset detection, and wrong-color detection.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh caac452cd4 🐛 Fix wrong extremity point in calculate-extremities for line-to
In the :line-to branch of calculate-extremities, move-p (the subpath
start point) was being added to the extremities set instead of from-p
(the actual previous point). For all line segments beyond the first one
in a subpath this produced an incorrect bounding-box start point.

The :curve-to branch correctly used from-p; align :line-to to match.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 30931839b5 🐛 Fix reversed d/in-range? args in CLJS Fills -nth with default
In the ClojureScript Fills deftype, the two-arity -nth implementation
called (d/in-range? i size) but the signature is (d/in-range? size i).
This meant -nth always fell through to the default value for any valid
index when called with an explicit default, since i < size is the
condition but the args were swapped.

The no-default -nth sibling on line 378 and both CLJ nth impls on
lines 286 and 291 had the correct argument order.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 6da39bc9c7 🐛 Fix ObjectsMap CLJS negative cache keyed on 'key' fn instead of 'k'
In the CLJS -lookup implementation, when a key is absent from data the
negative cache entry was stored under 'key' (the built-in map-entry
key function) rather than the 'k' parameter.  As a result every
subsequent lookup of any missing key bypassed the cache and repeated
the full lookup path, making the negative-cache optimization entirely
ineffective.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 2b67e114b6 🐛 Fix inside-layout? passing id instead of shape to frame-shape?
`(cfh/frame-shape? current-id)` passes a UUID to the single-arity
overload of `frame-shape?`, which expects a shape map; it always
returns false. Fix by passing `current` (the resolved shape) instead.
Update the test to assert the correct behaviour.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 8b08c8ecc9 🐛 Fix wrong mapcat call in collect-main-shapes
`(mapcat collect-main-shapes children objects)` passes `objects` as a
second parallel collection instead of threading it as the second
argument to `collect-main-shapes` for each child. Fix by using an
anonymous fn: `(mapcat #(collect-main-shapes % objects) children)`.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:09 +02:00
Andrey Antukh 8253738f01 🐛 Fix reversed get args in convert-dtcg-shadow-composite
\`(get "type" shadow)\` always returns nil because the map and key
arguments were swapped. The correct call is \`(get shadow "type")\`,
which allows the legacy innerShadow detection to work correctly.
Update the test expectation accordingly.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:25:03 +02:00
Andrey Antukh c30c85ff07 🐛 Remove duplicate font-weight-keys in typography-keys union
font-weight-keys was listed twice in the set/union call for
typography-keys, a copy-paste error. The duplicate entry has no
functional effect (sets deduplicate), but it is misleading and
suggests a missing key such as font-style-keys in its place.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:17:14 +02:00
Andrey Antukh ff41d08e3c 🐛 Fix stale accumulator in get-children-in-instance recursion
get-children-rec passed the original children vector to each recursive
call instead of the updated one that already includes the current
shape. This caused descendant results to be accumulated from the wrong
starting point, losing intermediate shapes. Pass children' (which
includes the current shape) into every recursive call.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:17:14 +02:00
Andrey Antukh 08ca561667 🐛 Add better nil handling in interpolate-gradient when offset exceeds stops
When no gradient stop satisfies (<= offset (:offset %)),
d/index-of-pred returns nil. The previous code called (dec nil) in
the start binding before the nil check, throwing a
NullPointerException/ClassCastException. Guard the start binding with
a cond that handles nil before attempting dec.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:17:14 +02:00
Andrey Antukh 7b0ea5968d 🚑 Fix typo :podition in swap-shapes grid cell
The key :podition was used instead of :position when updating the
id-from cell in swap-shapes, silently discarding the position value
and leaving the cell's :position as nil after every swap.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:17:14 +02:00
rockchris099andAndrey Antukh 8cc05d9579 Show alpha percentage in asset library color names (#8975)
When several library colors share the same RGB value but differ only
in opacity, append the alpha percentage (e.g. "#ff0000 50%") next to
the displayed default name and in the color bullet tooltip so users
can tell them apart at a glance.

Closes #6328

Signed-off-by: rockchris99 <chrisleo0721@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 21:02:42 +02:00
Andrey Antukh f07b954b7e Add efficiency improvements to workspace components (refactor part 1) (#8887)
* ♻️ Convert snap-points components to modern rumext format

Migrate snap-point, snap-line, snap-feedback, and snap-points from
legacy mf/defc format to modern * suffix format. This enables
optimized props handling by the rumext macro, eliminating implicit
JS object wrapping overhead on each render. All internal and
external call sites updated to use [:> component* props] syntax.

* ♻️ Convert frame-title to modern rumext format

Migrate frame-title from legacy mf/defc format to modern * suffix
format. The component was using legacy implicit props wrapping without
::mf/wrap-props false or ::mf/props :obj, causing unnecessary JS
object conversion overhead on each render. The parent frame-titles*
call site updated to use [:> frame-title* props] syntax.

* ♻️ Convert interactions components to modern rumext format

Migrate interaction-marker, interaction-path, interaction-handle,
overlay-marker, and interactions from legacy mf/defc format to modern
* suffix format. These five components had zero props optimization
applied, causing implicit JS object wrapping on every render. All
internal and external call sites updated to use [:> component* props]
syntax.

* ♻️ Convert rulers components to modern rumext format

Migrate rulers-text, viewport-frame, and selection-area from legacy
mf/defc format to modern * suffix format. These three components in
the always-visible rulers layer had zero props optimization applied.
Internal call sites in the parent rulers component updated to use
[:> component* props] syntax.

* ♻️ Convert frame-grid components to modern rumext format

Migrate square-grid, layout-grid, grid-display-frame, and frame-grid
from legacy mf/defc format to modern * suffix format. These four
components render grid patterns per-frame with zero props optimization.
All internal and external call sites updated to use [:> component* props]
syntax.

* ♻️ Convert gradient handler components to modern rumext format

Migrate shadow, gradient-color-handler, and gradient-handler-transformed
from legacy mf/defc format to modern * suffix format. These components
are rendered during gradient editing with zero props optimization applied.
Internal call sites in gradient-handler-transformed and
gradient-handlers-impl updated to use [:> component* props] syntax.

* ♻️ Rename ?-ending props in modernized workspace viewport components

Apply prop naming rules to all * components migrated in the previous batch:
- remove-snap? -> remove-snap in snap-feedback* (and get-snap helper)
- selected? -> is-selected in interaction-path*
- hover-disabled? -> is-hover-disabled in overlay-marker* and interactions*
- show-rulers? -> show-rulers in viewport-frame*

Update all internal and external call sites consistently.

* 🐛 Fix get-snap call in snap-feedback* using JS props object

Modern rumext *-suffix components receive props as JS objects, not
Clojure maps. snap-feedback* was pushing the raw props object into the
rx/subject and get-snap was destructuring it as a Clojure map, causing
all keys to resolve to nil.

Fix by:
- Changing get-snap to take positional arguments (coord, shapes,
  page-id, remove-snap, zoom) instead of a map-destructured opts arg
- Building an explicit Clojure map from the bound locals before pushing
  to the subject
- Destructuring that map inside the rx/switch-map callback and calling
  get-snap with positional args

Also mark get-snap and add-point-to-snaps as private (defn-), consistent
with the other helpers in the namespace — none are referenced externally.
2026-04-14 19:48:59 +02:00
Andrey Antukh 6c90ba1582 🐛 Fix move-files allowing same project as target when multiple files selected
The 'Move to' menu in the dashboard file context menu only filtered
out the first selected file's project from the available target list.
When multiple files from different projects were selected, the other
files' projects still appeared as valid targets, causing a 400
'cant-move-to-same-project' backend error.

Now all selected files' project IDs are collected and excluded from
the available target projects.
2026-04-14 15:19:19 +02:00
Andrey Antukh 18f0ad246f 🐛 Fix parse-long crash when index query param is duplicated in URL
lambdaisland/uri's query-string->map uses :multikeys :duplicates by
default: a key that appears once yields a plain string, but the same
key repeated yields a vector. cljs.core/parse-long only accepts
strings and therefore threw "Expected string, got: object" whenever
a URL contained a duplicate 'index' parameter.

Add rt/get-query-param to app.main.router. The helper returns the
scalar value of a query param key, taking the last element when the
value is a sequential (i.e. the key was repeated). Use it at every
call site that feeds a query-param value into parse-long, in both
app.main.ui (page*) and app.main.data.viewer.
2026-04-14 15:16:04 +02:00
alonso.torres dc5f222230 🐛 Improve change token set performance 2026-04-14 14:51:12 +02:00
rockchris099andAndrey Antukh 207cb87d5e Reorder prototype overlay options (position before relative to) (#8972)
Closes #2910

Signed-off-by: rockchris99 <chrisleo0721@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 13:56:26 +02:00
Andrey Antukh 650f725f11 📎 Update changelog 2026-04-14 13:43:41 +02:00
ClaytonandAndrey Antukh 39b0e011fc Differentiate incoming and outgoing interaction link colors (#8923)
*  Color incoming and outgoing interaction links differently

* 🐛 Fix lint

---------

Signed-off-by: Clayton <claytonlin1110@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 13:30:22 +02:00
DexterityandAndrey Antukh 7c3a1a905e Fix locked elements not selectable in viewer + add guide locking (#8949)
* 🐛 Allow viewers to select locked elements in canvas

*  Add ability to lock guides to prevent accidental movement

---------

Signed-off-by: Dexterity104 <hatanokanjiro@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 13:21:51 +02:00
Eva Marco 3469e867ff 🐛 Fix gap input throwing an error (#8984) 2026-04-14 13:02:03 +02:00
JamesandAndrey Antukh b211594ce8 🐛 Fix hyphens stripped from export filenames (#8944)
Replace str/slug with a targeted regex that only removes
filesystem-unsafe characters when generating export filenames.
The slug function strips all non-word characters including hyphens,
causing names like "my-board" to become "myboard" on export.

Fixes #8901

Signed-off-by: jamesrayammons <jamesrayammons@outlook.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 12:36:26 +02:00
Andrey Antukh 62f3454607 🔧 Backport ci configuration changes from develop 2026-04-14 12:34:04 +02:00
Andrey Antukh 3264bc746f 🔧 Backport ci configuration changes from develop 2026-04-14 12:33:10 +02:00
DreamandAndrey Antukh 68595e90eb Persist asset search query when switching sidebar tabs (#8985)
When users switch between the Layers and Assets sidebar tabs, the
`assets-toolbox*` component unmounts and its local `use-state` is
discarded, so the search query and section filter are lost.

Lift the search term and section filter into a per-file, in-memory
session atom that survives tab switches but doesn't leak across files
or persist across reloads. Ordering and list-style continue to use
localStorage as before.

Closes #2913

Signed-off-by: eureka0928 <meobius123@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 12:31:25 +02:00
Andrey Antukh 6788df02ca 🔧 Add minor adjustments on ci workflow related to e2e tests 2026-04-14 11:49:16 +02:00
Dream 8b14de2610 Sort asset subfolders alphabetically (#8952)
Subgroups in asset libraries were rendered in hash-map order because
update-in descends into plain maps instead of sorted ones. Add a
recursive post-process that rebuilds every level as a sorted-map so
subfolders are alphabetical at every nesting depth.

Closes #2572

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-14 10:50:46 +02:00
Andrey Antukh a81cded0aa Make the common fressian module more testable (#8859)
*  Add exhaustive unit tests for app.common.fressian encode/decode

Add a JVM-only test suite (41 tests, 172 assertions) for the fressian
serialisation layer, covering:

- All custom handlers: char, clj/keyword, clj/symbol, clj/vector,
  clj/set, clj/map, clj/seq, clj/ratio, clj/bigint, java/instant,
  OffsetDateTime, linked/map (order preserved), linked/set (order preserved)
- Built-in types: nil, boolean, int, long, double (NaN, ±Inf, boundaries),
  String, byte[], UUID
- Edge cases: empty collections, nil values, ArrayMap/HashMap size boundary,
  mixed key types
- Penpot-domain structures: shape maps with UUID keys, nested objects maps
- Correctness: encode→decode→encode idempotency, independent encode calls

* ♻️ Extract fressian handler helpers to private top-level functions

Extract adapt-write-handler, adapt-read-handler, and merge-handlers
out of the letfn in add-handlers! into reusable private functions.
Also creates xf:adapt-write-handler and xf:adapt-read-handler
transducers and adds overwrite-read-handlers and overwrite-write-handlers
for advanced handler override use cases.
2026-04-14 10:48:58 +02:00
Andrey Antukh c39609b991 ♻️ Use shared singleton containers for React portals (#8957)
Refactor use-portal-container to allocate one persistent <div> per
logical category (:modal, :popup, :tooltip, :default) instead of
creating a new div for every component instance. This keeps the DOM
clean with at most four fixed portal containers and eliminates the
arbitrary growth of empty <div> elements on document.body while
preserving the removeChild race condition fix.
2026-04-14 10:48:30 +02:00
StatxcandAndrey Antukh d90e7f8164 Add Find & Replace for text content and layer names (#8899)
*  Add Find & Replace for text content and layer names

* 💄 Fix cross-browser styling for Find & Replace radio buttons and action buttons

* 💄 Fix stylelint empty line before declaration in layers.scss

*  Improve match-filters and match-ids efficiency

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-14 10:41:31 +02:00
Dream 19b9c696fc 🐛 Reset account submenu state when profile menu closes (#8953)
Closes #8947

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-14 10:33:32 +02:00
Dream 4703fe6e3b Add visibility toggle for strokes (#8913)
*  Add visibility toggle for strokes

* ♻️ Use single emit! call for stroke visibility toggle

* 💄 Disable stroke controls when hidden, matching shadow/blur pattern

When a stroke is hidden, the alignment/style selects, cap selects, and
cap switch button are now disabled. A .hidden CSS class dims the
options area with reduced opacity. This matches the existing behavior
in shadow_row and blur menu where controls are disabled when the
effect is hidden.

* 💄 Move stroke hide button before remove button

---------

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-14 10:08:13 +02:00
Andrey Antukh b3645658fb Merge pull request #8963 from penpot/raguirref-fix/builder-bool-media-validation
🐛 Fix builder bool styles and media validation
2026-04-13 18:35:12 +02:00
Andrey Antukh 9106a994f1 Merge remote-tracking branch 'origin/staging' into develop 2026-04-13 18:31:50 +02:00
Andrey Antukh bc47b992eb Merge remote-tracking branch 'origin/main' into staging 2026-04-13 18:31:32 +02:00
Eva Marco a3f7a1def6 🐛 Fix bugs with multiselection (#8932)
* 🐛 Fix app crash when selecting shapes with one hidden

* 🐛 Fix opacity input when mixed values
2026-04-13 18:29:15 +02:00
Alejandro Alonso 2ccaa3f0c5 Merge remote-tracking branch 'origin/staging' into develop 2026-04-13 16:51:51 +02:00
Alejandro Alonso 367262f5a0 Merge pull request #8959 from penpot/elenatorro-fix-render-wasm-loading
🔧 Improve loading times
2026-04-13 16:50:30 +02:00
Alejandro Alonso dfc5a256b4 Merge remote-tracking branch 'origin/staging' into develop 2026-04-13 16:47:18 +02:00
Elena Torro 6b3d5d930f 🔧 Improve zoom and pan performance 2026-04-13 16:35:36 +02:00
Marina López a52831aa8c Show professional card when has nitrate subscription 2026-04-13 16:07:51 +02:00
rockchris099 bbd200f869 🐛 Fix dashboard Recent/Deleted titles overlapped by scrolling content (#8945)
Add z-index to the sticky .nav element in the dashboard so that
section titles (Recent, Deleted) stay above scrolling content
instead of being obscured by project cards and file thumbnails.

Fixes #8577
Signed-off-by: rockchris99 <chrisleo0721@gmail.com>
2026-04-13 15:55:13 +02:00
Juanfran 87179e806f Add subscribe-nitrate route with post-registration nitrate modal (#8941) 2026-04-13 15:49:22 +02:00
Andrey Antukh e46b34efc7 📎 Fix formatting issues 2026-04-13 15:41:38 +02:00
raguirrefandCopilot 94c6045dd9 🔥 Remove accidental dev_server.pid
Remove unrelated local pid file that was accidentally included in previous commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: raguirref <ricardoaguirredelafuente@gmail.com>
2026-04-13 15:40:40 +02:00
raguirrefandCopilot f656266e5c Fix builder bool and media handling
Fixes three concrete builder issues in common/files/builder:\n- Use bool type from shape when selecting style source for difference bools\n- Persist :strokes correctly (fix typo :stroks)\n- Validate add-file-media params after assigning default id\n\nAlso adds regression tests in common-tests.files-builder-test and registers them in runner.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: raguirref <ricardoaguirredelafuente@gmail.com>
2026-04-13 15:40:40 +02:00
Elena Torró a6c3767e2b Merge pull request #8956 from penpot/alotor-poc-nofast-mode
🐛 Fix problem with fast mode
2026-04-13 15:38:04 +02:00
alonso.torres 2d07b9e77c 🐛 Fix problem with fast mode 2026-04-13 15:18:12 +02:00
Andrey Antukh 0fc2050526 ⬆️ Update deps on root package.json 2026-04-13 15:00:47 +02:00
Elena Torro 47eadab82e 🔧 Include DropShadows surface to reset 2026-04-13 14:42:03 +02:00
Elena Torro d85d63ef3c 🔧 Improve page loading 2026-04-13 14:42:03 +02:00
Aitor Moreno 83e9f85ccf Merge pull request #8943 from penpot/ladybenko-13949-fix-resize-call
🐛 Fix initializing guards in viewport loading
2026-04-13 13:37:25 +02:00
Pablo Alba d91ce0f9d1 🐛 Fix nitrate go to control center 2026-04-13 12:30:49 +02:00
Andrey Antukh 28f65fec91 📚 Update changelog 2026-04-13 12:15:17 +02:00
Aitor Moreno 9c44f5bf65 🐛 Fix text editor v1 focus not being handled correctly (#8942) 2026-04-13 12:08:06 +02:00
Eva Marco 443fb60743 🐛 Fix highlight on frames after rename (#8938) 2026-04-13 12:04:04 +02:00
Luis de DiosandAndrey Antukh cbe9d31599 🐛 Fix dashboard navigation tabs overlap with content when scrolling (#8937)
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-13 12:01:10 +02:00
Luis de DiosandAndrey Antukh 599a66979a 🐛 Fix dashboard navigation tabs overlap with content when scrolling (#8937)
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-13 11:59:19 +02:00
Pablo Alba 5c761125f3 Add invite-to-org to Nitrate API 2026-04-13 11:49:01 +02:00
Andrey Antukh d6045c80a1 💄 Fix docstrings and clarify filter expression in path namespaces
- Fix 'conten' typo to 'content' in path.cljc docstring
- Fix 'curvle' typo to 'curve' in shape_to_path.cljc docstring
- Replace confusing XOR-style filter with readable
  (contains? #{:line-to :curve-to} ...) in bool.cljc
- Align handler-indices and opposite-index docstrings with
  matching API in path.cljc
2026-04-13 11:48:30 +02:00
Andrey Antukh 8d1906f56e 🐛 Fix ^:cosnt typo to ^:const on bool-group-style-properties
The metadata key was misspelled as :cosnt instead of :const,
preventing the compiler from recognizing the Var as a compile-time
constant.
2026-04-13 11:48:30 +02:00
Andrey Antukh 2eaf117b56 🐛 Fix swapped arguments in CLJS PathData -nth with default
The CLJS implementation of PathData's -nth protocol method had
swapped arguments in the 3-arity version (with default value).
The call (d/in-range? i size) should be (d/in-range? size i)
to match the CLJ implementation. With swapped args, valid indices
always returned the default value, and invalid indices attempted
out-of-bounds buffer reads.
2026-04-13 11:48:30 +02:00
Andrey Antukh e511576f66 🐛 Normalize PathData coordinates to safe integer bounds on read
Add normalize-coord helper function that clamps coordinate values to
max-safe-int and min-safe-int bounds when reading segments from PathData
binary buffer. Applies normalization to read-segment, impl-walk,
impl-reduce, and impl-lookup functions to ensure coordinates remain
within safe bounds.

Add corresponding test to verify out-of-bounds coordinates are properly
clamped when reading PathData.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-13 11:48:30 +02:00
Marina López 707cc53ca4 Revert Add can use trial prop in nitrate profile (#8954) 2026-04-13 11:41:32 +02:00
Andrey Antukh a403175d5c 🐛 Fix TypeError in sd-token-uuid when resolving tokens interactively (#8929)
The backtrace-tokens-tree function used a namespaced keyword :temp/id
which clj->js converted to the JS property "temp/id". The sd-token-uuid
function then tried to access .id on the sd-token top-level object,
which was undefined, causing "Cannot read properties of undefined
(reading uuid)".

Fix by using the existing token :id instead of generating a temporary
one, and read it from sd-token.original (matching sd-token-name pattern).
2026-04-13 11:34:15 +02:00
Aitor Moreno bb85b312d6 🐛 Fix text editor v1 focus not being handled correctly (#8942) 2026-04-13 10:00:56 +02:00
Dream 78a16d99a9 Add clear artboard guides option to context menu (#8936)
*  Add clear artboard guides option to context menu

Adds a "Clear artboard guides" option to the right-click context menu
when one or more frames with guides are selected. Closes #6987

* ♻️ Address review feedback from niwinz

- Replace deprecated dm/assert! with assert
- Replace (map :id) with d/xf:map-id

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-13 09:58:23 +02:00
Dream 8dccb2a427 Make links in comments clickable (#8894)
*  Make links in comments clickable

Detect URLs in comment text and render them as clickable links that
open in a new tab. Extends the existing mention parsing to also split
text elements by URL patterns, handling trailing punctuation and
mixed mention+URL content.

Closes #1602

* 📚 Add changelog entry for clickable links in comments

* 🐛 Fix URL elements dropped in comment input initialization

* 🐛 Keep empty text elements in parse-urls to preserve cursor anchors

The remove filter in parse-urls was stripping empty text elements
produced by str/split at URL boundaries. These elements are needed
as cursor anchor spans in the contenteditable input, without them
ESC keydown and visual layout broke.

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-13 09:55:53 +02:00
Eva Marco 6d1a2d449a 🐛 Fix highlight on frames after rename (#8938) 2026-04-13 09:09:03 +02:00
Yamila Moreno e7e5a19db7 🔧 Prevent draft pr from executing the CI (#8934) 2026-04-10 14:43:29 +02:00
Belén Albeza eb811621a9 🐛 Fix initializing guards in viewport loading 2026-04-10 13:54:06 +02:00
Andrés Moya 3312bfe62c Force current set as active when resolving tokens in sidebar 2026-04-10 13:33:29 +02:00
Pablo AlbaandAndrey Antukh ef6eeb5693 🐛 Fix variants corner cases with selrect and points (#8882)
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-10 11:23:03 +02:00
Marek HrabeandAndrey Antukh 9785a13e67 🐛 Add webp export format to plugin types (#8870)
* 🐛 Add webp export format to plugin types

Align plugin API typings with runtime export support by including 'webp' in
'Export.type' and updating the exported formats documentation.

Signed-off-by: Marek Hrabe <marekhrabe@me.com>

* 📚 Add plugin-types changelog entry for missing webp export format

Signed-off-by: Marek Hrabe <marekhrabe@me.com>

---------

Signed-off-by: Marek Hrabe <marekhrabe@me.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-10 11:22:20 +02:00
DexterityandAndrey Antukh 240e8ce50c 🐛 Use page name for multi-export downloads (#8874)
* 🐛 Use page name for multi-export downloads

* ♻️ Refactor parameter formatting in asset export function

*  Use page name for multi-export ZIP/PDF downloads [Github #8773]

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-10 11:21:14 +02:00
Dr. Dominik Jain 8101f58651 📚 Add improvements for mcp readme (#8930)
* 📚 Update description of mcp-remote usage

* Use Streamable HTTP endpoint instead of SSE
* Remove redundant global installation

* 📚 Add recommendations on model selection

* 📚 Use new tags in npx launch commands
2026-04-10 10:49:13 +02:00
Xaviju 9e4c8981be 🎉 Duplicate token group (#8886) 2026-04-10 10:42:35 +02:00
Elena Torró a87552bc45 Merge pull request #8926 from penpot/superalex-wasm-render-performance
🎉 Wasm render performance improvements
2026-04-10 09:11:50 +02:00
Marek Hrabe a803bde2ff 🐛 Fix plugin modal dragging bugs (#8871)
* 🐛 Fix plugin modal drag and close interactions

Switch plugin modal dragging to pointer-capture semantics from the header so drag state remains stable when crossing iframe boundaries. Prevent drag start from close-button pointerdown and add regression tests for both non-draggable close-button interaction and close-event dispatch.

Signed-off-by: Marek Hrabe <marekhrabe@me.com>

* 📚 Update changelog for plugin modal drag fix

Document plugin modal drag and close-button interaction fixes in the unreleased changelog.

Signed-off-by: Marek Hrabe <marekhrabe@me.com>

* 🐛 Simplify plugin modal drag CSS selection rules

Keep user-select disabled at the modal wrapper level and keep touch-action scoped to the header drag handle to remove redundant declarations while preserving drag behavior.

Signed-off-by: Marek Hrabe <marekhrabe@me.com>

---------

Signed-off-by: Marek Hrabe <marekhrabe@me.com>
2026-04-09 21:13:10 +02:00
Alejandro Alonso 5eebc17ce2 🎉 Support for debugging cache texture 2026-04-09 19:02:14 +02:00
Alejandro Alonso 434e27bbe8 🎉 Improve panning performance 2026-04-09 19:02:14 +02:00
Dexterity e49b7ce14c 🐛 Fix warnings for unsupported token $type (#8873)
* 🐛 Fix warnings for unsupported token $type

Signed-off-by: Dexterity104 <hatanokanjiro@gmail.com>

* 🐛 Add changelog entry for Github #8790

---------

Signed-off-by: Dexterity104 <hatanokanjiro@gmail.com>
Signed-off-by: Dexterity <173429049+Dexterity104@users.noreply.github.com>
2026-04-09 17:09:19 +02:00
Alejandro Alonso 5c67cd0a4b 🐛 Avoid unnecesary text editor pointer movements 2026-04-09 16:18:58 +02:00
Yamila Moreno d2050d5331 🔧 Update tests-mcp.yml
Add a more explicit name for a workflow

Signed-off-by: Yamila Moreno <yamila.moreno@kaleidos.net>
2026-04-09 15:38:34 +02:00
Eva Marco 5b78de3594 🐛 Fix selected colors with tokens (#8889) 2026-04-09 14:10:23 +02:00
Xaviju 666313c2c3 🐛 Close expanded tree when switching or creating sets (#8920) 2026-04-09 12:37:29 +02:00
Eva Marco 290f37425f 🐛 Fix id prop on switch component (#8915) 2026-04-09 12:35:34 +02:00
Andrey Antukh ef39afe9b5 Merge remote-tracking branch 'origin/main' into staging 2026-04-09 12:24:18 +02:00
Pablo Alba d65f3b5396 Add nitrate api endpoints to get an user profile 2026-04-09 12:10:06 +02:00
Pablo Alba fe2023dde5 Add nitrate api endpoints to get an user profile 2026-04-09 12:10:06 +02:00
Elena Torró a88f8f1394 Merge pull request #8918 from penpot/niwinz-main-path-preview-issue
🐛 Fix path drawing preview passing shape instead of content to next-node
2026-04-09 11:48:58 +02:00
Eva Marco b0a99b65e4 🐛 Fix highlight on shape after rename (#8890) 2026-04-09 11:27:36 +02:00
Andrey Antukh 388775413e 🐛 Fix path drawing preview passing shape instead of content to next-node
In `preview-next-point`, `st/get-path` was called without extra keys,
which returns the full Shape record. That value was then passed directly
to `path/next-node` as its `content` argument.

`path/next-node` delegates to `impl/path-data`, which only accepts a
`PathData` instance, `nil`, or a sequential collection of segments. A
Shape record matches none of those cases, so `path-data` threw
"unexpected data" every time the user moved the mouse while drawing a
path.

The fix is to call `(st/get-path state :content)` so that only the
`:content` field (a `PathData` instance) is extracted and forwarded to
`path/next-node`.
2026-04-09 09:21:57 +00:00
Marina López 1c68810521 Add can use trial prop in nitrate profile 2026-04-09 11:15:21 +02:00
Aitor Moreno 38a5a67b86 Merge pull request #8912 from penpot/niwinz-main-text-editor-fixes
🐛 Fix TypeError when deleting text at edge spans in text editor
2026-04-09 10:53:07 +02:00
Andrés Moya deb3af23d4 🐛 Normalize token set names in themes (#8914) 2026-04-09 10:33:56 +02:00
Eva Marco da6bd7509b 🐛 Fix hot reload on color-row text (#8880) 2026-04-09 10:18:21 +02:00
Eva Marco c1d815f97c 🐛 Fix go to viewer with frame selected (#8878) 2026-04-09 10:05:56 +02:00
Dream 21217c5622 Add per-group add button for typographies (#8895)
*  Add per-group add button for typographies

Add a "+" button to each typography group header, allowing users to
create new typographies directly inside a group instead of only at
the top level. The button only appears for local, editable files.

Closes #5275

* 📚 Add changelog entry for typography group add button

* 🐛 Fix typography group title button layout wrapping

* ♻️ Address review feedback for typography group add button

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-04-09 09:32:56 +02:00
Alejandro Alonso f8dd64611f Merge pull request #8625 from penpot/azazeln28-apply-styles-to-selection
🎉 Feat apply styles to selection
2026-04-09 09:22:24 +02:00
Juanfran e51e0c7933 Add theme field to nitrate authenticate response 2026-04-09 09:19:36 +02:00
Eva Marco 62b59991a9 🔧 Add guard to apply-token (#8879) 2026-04-09 09:16:28 +02:00
Andrey Antukh 5937a8b0fc Merge remote-tracking branch 'origin/staging' into develop 2026-04-09 09:13:02 +02:00
Andrey Antukh 11fbd4cb21 Merge remote-tracking branch 'origin/main' into staging 2026-04-09 09:12:23 +02:00
Andrey Antukh dfa45ec8d8 ⬆️ Update deps on root package.json 2026-04-09 09:10:44 +02:00
Alejandro Alonso 27449139ad Merge remote-tracking branch 'origin/staging' into develop 2026-04-09 09:05:02 +02:00
Alejandro Alonso 90fcc9f597 Merge pull request #8903 from penpot/alotor-fix-text-grow-problem
🐛 Fix problem with text auto grow in layouts
2026-04-09 08:26:18 +02:00
Andrey Antukh 5a2c09f246 🐛 Fix TypeError when deleting text at edge spans in text editor
The text editor's SelectionController threw 'TypeError: Invalid text
node' when:

- Pressing Backspace to delete the only character of the **first** text
  span in a paragraph that contains multiple spans.
- Pressing Delete to delete the only character of the **last** text
  span in the same situation.
- Pressing a word-backward shortcut that empties the first span of a
  multi-span paragraph.

In all three cases the tree-iterator (previousNode / nextNode) returned
null because no sibling text node existed in that direction, and that
null was subsequently passed to getTextNodeLength() which calls
isTextNode() — which unconditionally throws when given a falsy value.

Fix: use the null-coalescing fallback to the first/last remaining
child's text node of the paragraph before calling collapse() /
getTextNodeLength().
2026-04-08 21:03:46 +02:00
Alejandro Alonso 8f6133ddac Merge pull request #8853 from penpot/alotor-performance-tokens
🐛 Fix problem with token performance
2026-04-08 18:15:26 +02:00
andrés gonzález 6063c1c532 :books:Clarify remote MCP availability in production (#8910) 2026-04-08 17:48:05 +02:00
Andrey Antukh ffac8d2861 📎 Update changelog 2026-04-08 17:34:00 +02:00
Andrey Antukh f97df3e8ab 🐛 Fix PathData corruption root causes across WASM and CLJS
Replace unsafe std::mem::transmute calls in Rust WASM path code with
validated TryFrom conversions to prevent undefined behavior from invalid
enum discriminant values. This was the most likely root cause of the
"No matching clause: -19772" production crash -- corrupted bytes flowing
through transmute could produce arbitrary invalid enum variants.

Fix byteOffset handling throughout the CLJS PathData serialization
pipeline. DataView instances created via buf/slice carry a non-zero
byteOffset, but from-bytes, transit write handler, -write-to,
buf/clone, and buf/equals? all operated on the full underlying
ArrayBuffer, ignoring offset and length. This could silently produce
PathData with incorrect size or content.

Rust changes (render-wasm):
- RawSegmentData: From<[u8; N]> -> TryFrom<[u8; N]> with discriminant
  validation (must be 0x01-0x04) before transmuting
- RawBoolType: From<u8> -> TryFrom<u8> with explicit match on 0-3
- Add #[wasm_error] to set_shape_path_content, current_to_path,
  convert_stroke_to_path, and set_shape_bool_type so panics are caught
  and routed through the WASM error protocol instead of crashing
- set_shape_path_content: replace .expect() with proper Result/? error
  propagation per segment
- Remove unused From<BytesType> bound from SerializableResult trait

CLJS changes (common):
- from-bytes: use DataView.byteLength instead of ArrayBuffer.byteLength
  for DataView inputs; preserve byteOffset/byteLength when converting
  from Uint8Array, Uint32Array, and Int8Array
- Transit write handler: construct Uint8Array with byteOffset and
  byteLength from the DataView, not the full backing ArrayBuffer
- -write-to: same byteOffset/byteLength fix
- buf/clone: copy only the DataView byte range using Uint8Array with
  proper offset, not Uint32Array over the full ArrayBuffer
- buf/equals?: compare DataView byte ranges using Uint8Array with
  proper offset, not the full backing ArrayBuffers

Frontend changes:
- shape-to-path, stroke-to-path, calculate-bool*: wrap WASM call and
  buffer read in try/catch to ensure mem/free is always called, even
  when an exception occurs between the WASM call and the free call

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:24:09 +02:00
Andrey Antukh b63e4a297b 🐛 Handle corrupted PathData segments gracefully instead of crashing
Add nil defaults to all case expressions that match binary segment
type codes so that corrupted/unknown values are skipped instead of
throwing 'No matching clause'. This prevents a React render crash
(triggered via shape-with-open-path? -> get-subpaths -> reduce)
when a PathData buffer contains invalid bytes, e.g. from a WASM
data transfer or deserialization of damaged stored data.

Affected functions: read-segment, impl-walk, impl-reduce,
impl-lookup, to-string-segment*, and the seq/reduce protocol
implementations on both JVM and CLJS PathData types.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:21:03 +02:00
Andrey Antukh 6a0d131715 🐛 Fix swapped move-to/line-to type codes in PathData binary readers
The impl-walk, impl-reduce, and impl-lookup functions had the binary
type codes for move-to and line-to swapped (1 mapped to :line-to and
2 to :move-to). This is inconsistent with from-plain, read-segment,
to-string-segment*, and the Rust RawSegmentData enum which all use
1=move-to and 2=line-to. The swap caused incorrect command types to
be reported to callers like get-handlers and get-points.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:20:47 +02:00
Andrey Antukh 92de9ed258 🐛 Fix PathData corruption root causes across WASM and CLJS
Replace unsafe std::mem::transmute calls in Rust WASM path code with
validated TryFrom conversions to prevent undefined behavior from invalid
enum discriminant values. This was the most likely root cause of the
"No matching clause: -19772" production crash -- corrupted bytes flowing
through transmute could produce arbitrary invalid enum variants.

Fix byteOffset handling throughout the CLJS PathData serialization
pipeline. DataView instances created via buf/slice carry a non-zero
byteOffset, but from-bytes, transit write handler, -write-to,
buf/clone, and buf/equals? all operated on the full underlying
ArrayBuffer, ignoring offset and length. This could silently produce
PathData with incorrect size or content.

Rust changes (render-wasm):
- RawSegmentData: From<[u8; N]> -> TryFrom<[u8; N]> with discriminant
  validation (must be 0x01-0x04) before transmuting
- RawBoolType: From<u8> -> TryFrom<u8> with explicit match on 0-3
- Add #[wasm_error] to set_shape_path_content, current_to_path,
  convert_stroke_to_path, and set_shape_bool_type so panics are caught
  and routed through the WASM error protocol instead of crashing
- set_shape_path_content: replace .expect() with proper Result/? error
  propagation per segment
- Remove unused From<BytesType> bound from SerializableResult trait

CLJS changes (common):
- from-bytes: use DataView.byteLength instead of ArrayBuffer.byteLength
  for DataView inputs; preserve byteOffset/byteLength when converting
  from Uint8Array, Uint32Array, and Int8Array
- Transit write handler: construct Uint8Array with byteOffset and
  byteLength from the DataView, not the full backing ArrayBuffer
- -write-to: same byteOffset/byteLength fix
- buf/clone: copy only the DataView byte range using Uint8Array with
  proper offset, not Uint32Array over the full ArrayBuffer
- buf/equals?: compare DataView byte ranges using Uint8Array with
  proper offset, not the full backing ArrayBuffers

Frontend changes:
- shape-to-path, stroke-to-path, calculate-bool*: wrap WASM call and
  buffer read in try/catch to ensure mem/free is always called, even
  when an exception occurs between the WASM call and the free call

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:14:18 +02:00
Andrey Antukh 2eaa2dc807 🐛 Handle corrupted PathData segments gracefully instead of crashing
Add nil defaults to all case expressions that match binary segment
type codes so that corrupted/unknown values are skipped instead of
throwing 'No matching clause'. This prevents a React render crash
(triggered via shape-with-open-path? -> get-subpaths -> reduce)
when a PathData buffer contains invalid bytes, e.g. from a WASM
data transfer or deserialization of damaged stored data.

Affected functions: read-segment, impl-walk, impl-reduce,
impl-lookup, to-string-segment*, and the seq/reduce protocol
implementations on both JVM and CLJS PathData types.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:14:18 +02:00
Andrey Antukh 0dfa450cc8 🐛 Fix swapped move-to/line-to type codes in PathData binary readers
The impl-walk, impl-reduce, and impl-lookup functions had the binary
type codes for move-to and line-to swapped (1 mapped to :line-to and
2 to :move-to). This is inconsistent with from-plain, read-segment,
to-string-segment*, and the Rust RawSegmentData enum which all use
1=move-to and 2=line-to. The swap caused incorrect command types to
be reported to callers like get-handlers and get-points.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:14:18 +02:00
Andrey Antukh cb33fe417e 🐛 Fix non-integer row/column values in grid cell position inputs (#8869)
* 🐛 Fix non-integer row/column values in grid cell position inputs

The numeric-input component allows Alt+arrow key increments of 0.1x the
step value, which could produce float values (e.g. 4.5, 0.5) when users
adjusted grid cell row/column/row-span/column-span positions. The schema
requires these fields to be integers, causing backend validation errors.

Round the input values to integers in the on-grid-coordinates callback
before passing them to update-grid-cell-position.

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

* 🐛 Enforce integer-only values in grid cell numeric inputs

Add an `integer` prop to the legacy `numeric-input*` component that
rounds parsed values in `parse-value`, ensuring all input paths (typed
text, arrow keys, Alt+arrow, mouse wheel, expressions) produce integers.
Use it for all six row/column inputs in the grid cell options panel.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:05:55 +02:00
Andrey Antukh c8675c5b7e ♻️ Normalize newsletter-updates checbox on different register flows (#8839)
*  Add newsletter opt-in checkbox to registration validation form

Add accept-newsletter-updates support through the full registration
token flow. The newsletter checkbox is now available on the
registration validation form, allowing users to opt-in during the
email verification step.

Backend changes:
- Refactor prepare-register to consolidate UTM params and newsletter
  preference into props at token creation time
- Add accept-newsletter-updates to prepare-register-profile and
  register-profile schemas
- Handle newsletter-updates in register-profile by updating token
  claims props on second step

Frontend changes:
- Add newsletter-options component to register-validate-form
- Add accept-newsletter-updates to validation schema
- Fix subscription finalize/error handling in register form

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

* ♻️ Refactor auth register components to modern style

Migrate all components in app.main.ui.auth.register and
app.main.ui.auth.login/demo-warning to use the modern * suffix
convention, removing deprecated ::mf/props :obj metadata and
updating all invocations from [:& name] to [:> name*] syntax.

Components updated:
- terms-and-privacy -> terms-and-privacy*
- register-form -> register-form*
- register-methods -> register-methods*
- register-page -> register-page*
- register-success-page -> register-success-page*
- terms-register -> terms-register*
- register-validate-form -> register-validate-form*
- register-validate-page -> register-validate-page*
- demo-warning -> demo-warning*

Also remove unused old context-notification import in login.cljs.

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

* 🔥 Remove unused onboarding-newsletter component

The newsletter opt-in is now handled directly in the registration
form via the newsletter-options* component, making the standalone
onboarding-newsletter modal obsolete.

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

* 🐛 Fix register test for UTM params to use prepare-register step

UTM params are now extracted and stored in token props during the
prepare-register step, not at register-profile time. Move utm_campaign
and mtm_campaign from the register-profile call to the
prepare-register-profile call in the test.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-08 17:00:52 +02:00
Alonso Torres 6ce2aadfae Improve message from schema errors in plugins (#8865) 2026-04-08 16:14:51 +02:00
Dream 5502fe8df3 📚 Add changelog entry for undo/redo selection state (#8896) 2026-04-08 16:05:53 +02:00
Xaviju 10cfd99525 🐛 Fix lint invalid CSS props (#8907)
* 🐛 Fix lint invalid CSS props

* 🐛 Fix named colors in favor of modern notation or custom properties

* 🐛 Removed multiple combined selectors

* 🐛 Convert alpha value to numeric
2026-04-08 15:44:09 +02:00
Elena Torró e8e7900911 Merge pull request #8904 from penpot/ladybenko-wasm-cleanup
 Explicitly call free_gpu_resources on RenderState drop
2026-04-08 12:13:08 +02:00
Belén Albeza f6b8117fe9 Explicitly call free_gpu_resources on RenderState drop 2026-04-08 12:03:12 +02:00
Elena Torro 6d5b97a7e9 🔧 Fix text bounds 2026-04-08 11:16:09 +02:00
Eva Marco b8be89f231 🐛 Update onboarding image (#8902) 2026-04-08 11:00:59 +02:00
alonso.torres 0b0e193b70 🐛 Fix problem with text auto grow in layouts 2026-04-08 10:21:32 +02:00
Aitor Moreno d190655e64 Merge pull request #8841 from penpot/ladybenko-13861-modal-webgl-not-available
🎉 Show modal when WebGL is not available
2026-04-08 10:12:47 +02:00
Belén Albeza 619bc5833d 🔧 Remove VS Code settings 2026-04-08 09:59:44 +02:00
Andrey Antukh 40dfeb169c Merge remote-tracking branch 'origin/staging' into develop 2026-04-07 21:37:21 +02:00
Andrey Antukh 61d319eaac ⬆️ Update dependencies (#8867)
* ⬆️ Update deps

* ⬆️ Update storybook dependencies

* ⬆️ Update dependencies

* 🐛 Fix invalid var() usage on SCSS variable in numeric_input

* ⬆️ Update deps
2026-04-07 21:35:00 +02:00
Andrey Antukh 0cc5f7c63e Merge remote-tracking branch 'origin/staging' into develop 2026-04-07 19:28:23 +02:00
Andrey Antukh a27ef26279 Merge remote-tracking branch 'origin/main' into staging 2026-04-07 19:23:37 +02:00
Andrey Antukh f8c04949e1 🐛 Fix nil path content crash by exposing safe public API (#8806)
* 🐛 Fix nil path content crash by exposing safe public API

Move nil-safety for path segment helpers to the public API layer
(app.common.types.path) rather than the low-level segment namespace.
Add nil-safe wrappers for get-handlers, opposite-index, get-handler-point,
get-handler, handler->node, point-indices, handler-indices, next-node,
append-segment, points->content, closest-point, make-corner-point,
make-curve-point, split-segments, remove-nodes, merge-nodes, join-nodes,
and separate-nodes. Update all frontend callers to use path/ instead of
path.segment/ for these functions, removing the path.segment require
from helpers, drawing, edition, tools, curve, editor and debug.

Replace ad-hoc nil checks with impl/path-data coercion in all public
wrapper functions in app.common.types.path. The path-data helper
already handles nil by returning an empty PathData instance, which
provides uniform nil-safety across all content-accepting functions.

Update the path-get-points-nil-safe test to expect empty collection
instead of nil, matching the new coercion behavior.

* ♻️ Clean up path segment dead code and add missing tests

Remove dead code from segment.cljc: opposite-handler (duplicate of
calculate-opposite-handler) and path-closest-point-accuracy (unused
constant). Make update-handler and calculate-extremities private as
they are only used internally within segment.cljc.

Add missing tests for path/handler-indices, path/closest-point,
path/make-curve-point and path/merge-nodes. Update extremities tests
to use the local reference implementation instead of the now-private
calculate-extremities. Remove tests for deleted/privatized functions.

Add empty-content guard in path/closest-point wrapper to prevent
ArityException when reducing over zero segments.
2026-04-07 18:54:14 +02:00
Andrey Antukh e10bd6a8d3 🐛 Fix infinite recursion in get-frame-ids for thumbnail extraction (#8807)
The get-frame-ids function could enter infinite recursion when:
1. There's a circular reference in the frame hierarchy
2. A shape's frame-id points to itself (corrupt data)

The fix uses the cached version (get-frame-ids-cached) in recursive calls
and adds a guard to prevent self-referencing.
2026-04-07 16:34:08 +02:00
Andrey Antukh 52f28a1eee 🐛 Fix stale-asset detector missing protocol-dispatch errors
The stale-asset-error? predicate only matched keyword-constant
cross-build mismatches ($cljs$cst$). Protocol dispatch failures
($cljs$core$I prefix, e.g. IFn/ISeq) and V8's 'Cannot read
properties of undefined' phrasing were not covered, so the handler
fell through to a generic toast instead of triggering a hard reload.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-07 16:33:40 +02:00
Andrey Antukh 9a0ae32488 ⬆️ Update opencode dependency on repo root 2026-04-07 16:33:40 +02:00
DreamandMihai 0c08dfb13d Add the ability for save and restore selection state in undo/redo (#8652)
*  Capture selection state before changes are applied

Save current selection IDs in commit-changes so undo entries
can track what was selected before each action.

*  Save and restore selection state in undo/redo

Extend undo entry with selected-before and selected-after fields.
On undo, restore selection to what it was before the action.
On redo, restore selection to what it was after the action.
Handles single entries, stacked entries, accumulated transactions,
and undo groups.

Fixes #6007

* ♻️ Wire selected-before through workspace undo stream

Pass the captured selection state from commit data into
the undo entry so it is stored alongside changes.

* 🐛 Fix unmatched delimiter in changes.cljs

* 🐛 Pass selected-before through commit event to undo entry

selected-before was captured in commit-changes but dropped by the
commit function since it was missing from the destructuring and the
commit map. This caused restore-selection to receive nil on undo.

---------

Signed-off-by: eureka928 <meobius123@gmail.com>
Co-authored-by: Mihai <noreply@github.com>
2026-04-07 16:30:47 +02:00
Andrey Antukh 1e4ff4aa47 🐛 Ignore Zone.js toString TypeError in uncaught error handler (#8804)
Zone.js (injected by browser extensions such as Angular DevTools) patches
addEventListener by wrapping it and assigning a custom .toString to the
wrapper via Object.defineProperty with writable:false.  When the same
element is processed a second time, the plain assignment in strict mode
(libs.js is built with a "use strict" banner) throws a native TypeError:
"Cannot assign to read only property 'toString' of function '...'".

This error escapes the React tree through the window error/unhandledrejection
events and was surfacing the exception page to users even though Penpot itself
is unaffected.

The fix:
- Extract the private ignorable-exception? helpers from the letfn block into
  top-level defn/defn- forms so the predicate can be reused elsewhere.
- Add the Zone.js toString TypeError to the ignorable-exception? predicate so
  the global uncaught-error handler silently suppresses it.
- The React error boundary is intentionally left unchanged: anything that
  reaches it has executed inside React's reconciler and must not be ignored.
2026-04-07 16:25:57 +02:00
Andrey Antukh b99157a246 🐛 Prevent thumbnail frame recursion overflow (#8763)
Cache in-progress frame traversals before following parent frame links so thumbnail updates stop recursing forever on cyclic or transiently inconsistent shape graphs.

Add a regression test that covers cyclic frame-id chains and keeps the expected frame/component extraction behavior intact.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-07 15:09:54 +02:00
Belén Albeza 0558bab092 🎉 Show modal when WebGL is not available 2026-04-07 14:55:57 +02:00
Eva Marco 48e8c0bc65 🐛 Fix show resolved value instead of value (#8883) 2026-04-07 14:27:26 +02:00
Pablo Alba 3c639f41c4 Add option to leave a nitrate organization 2026-04-07 11:26:57 +02:00
Eva Marco a5055af538 🐛 Fix hidden on multiple selection (#8854) 2026-04-07 10:58:34 +02:00
Luis de Dios e99b6ec213 🐛 Fix MCP active tab switching (#8856) 2026-04-07 10:58:04 +02:00
Eva Marco 67734c5835 🐛 Fix hover on layers (#8885) 2026-04-07 10:57:27 +02:00
Cheonji Kim d5855f355f 📎 Fix typo in README.md for MCP server description (#8884)
Edited line 7(perfom -> perform)

Signed-off-by: Cheonji Kim <76100119+CheonjiKim@users.noreply.github.com>
2026-04-07 10:45:20 +02:00
Alejandro Alonso 83833896c9 Merge remote-tracking branch 'origin/staging' into develop 2026-04-07 10:18:35 +02:00
Alonso Torres 11d9c09a2e 🐛 Fix problem with dashboard thumbnails (#8862) 2026-04-07 10:10:08 +02:00
Aitor Moreno 101b2fe9e6 🎉 Add style data from text editor v3 2026-04-06 13:13:53 +02:00
Aitor Moreno 12382cfbb9 🎉 Feat apply styles to selection 2026-04-06 13:13:53 +02:00
Aitor Moreno 0f389fe3ad Merge pull request #8881 from penpot/azazeln28-fix-text-editor-v2-tests
🐛 Fix text-editor v2 waitForIdle not having a timeout
2026-04-06 13:13:05 +02:00
Aitor Moreno 9aa2abff2e 🐛 Fix text-editor v2 waitForIdle not having a timeout 2026-04-06 12:37:57 +02:00
Aitor Moreno 4205e283ea Merge pull request #8835 from penpot/superalex-fix-text-editor-mixed-selection-styles
🐛 Fix spurious mixed text styles on multi-span selection
2026-04-06 09:33:29 +02:00
Elena Torro 68760c8e26 🎉 Improve text inner stroke rendering 2026-04-02 12:00:08 +02:00
alonso.torres cbe3a3f33e 🐛 Fix problem when changing grow-type 2026-04-02 11:44:41 +02:00
Andrey AntukhandLuis de Dios 2ca7acfca6 Add tests for app.common.geom and descendant namespaces (#8768)
* 🎉 Add tests for app.common.geom.bounds-map

* 🎉 Add tests for app.common.geom and descendant namespaces

* 📎 Fix linting issues

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-04-02 09:50:34 +02:00
Andrey Antukh d2a3b67053 🎉 Add additional tests for app.common.types.shape.interactions (#8765)
*  Expand interaction helper test coverage

Add coverage for interaction destination and flow helpers,
including nil handling and removal helpers. Document the
intent of the new assertions so future interaction changes
keep the helper contract explicit.

*  Cover interaction validation edge cases

Exercise the remaining interaction guards and overlay
positioning edge cases, including invalid state
transitions and nested manual offsets. Keep the test
comments focused on why each branch matters for editor
 behavior.
2026-04-02 09:50:08 +02:00
Andrey Antukh 3ff1acfb6a 🐛 Fix vector index out of bounds in viewer zoom-to-fit/fill (#8834)
Clamp the frame index to the valid range in zoom-to-fit and
zoom-to-fill events before accessing the frames vector. When the
URL query parameter :index exceeds the number of frames on the
page (e.g. index=1 with a single frame), nth would throw
"No item 1 in vector of length 1". Also adds unit tests covering
the boundary condition.
2026-04-02 09:49:33 +02:00
Andrey Antukh 81b1b253f1 Add unique email domains to telemetry report (#8819)
Extend the telemetry payload with a sorted list of unique email domains
extracted from all registered profile email addresses. The new
:email-domains field is populated via a single SQL query using
split_part and DISTINCT, and is included in the stats sent when
telemetry is enabled.

Also update the tasks-telemetry-test to assert the new field is present
and contains the expected domain values.
2026-04-01 11:49:50 +02:00
Andrey Antukh 0337607a1b 🐛 Guard delete undo against missing sibling order (#8858)
Return nil from get-prev-sibling when the shape is no longer present in
the parent ordering so delete undo generation falls back to index-based
restore instead of crashing on invalid vector access.
2026-04-01 11:49:17 +02:00
Andrey Antukhandalonso.torres f7e1bcf87f 🐛 Handle plugin errors gracefully without crashing the UI (#8810)
* 🐛 Handle plugin errors gracefully without crashing the UI

Plugin errors (like 'Set is not a constructor') were propagating to the
global error handler and showing the exception page. This fix:

- Uses a WeakMap to track plugin errors (works in SES hardened environment)
- Wraps setTimeout/setInterval handlers to mark errors and re-throw them
- Frontend global handler checks isPluginError and logs to console

Plugin errors are now logged to console with 'Plugin Error' prefix but
don't crash the main application or show the exception page.

Signed-off-by: AI Agent <agent@penpot.app>

*  Improved handling of plugin errors on initialization

*  Fix test and linter

---------

Signed-off-by: AI Agent <agent@penpot.app>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-04-01 11:37:27 +02:00
Andrey Antukh 650762556f Merge remote-tracking branch 'origin/staging' into develop 2026-04-01 11:30:39 +02:00
Andrey Antukh 8fcbfadd49 Merge remote-tracking branch 'origin/main' into staging 2026-04-01 11:30:21 +02:00
Belén Albeza 8c1cf3623b 🔧 Update action checkout to v6 (#8861) 2026-04-01 11:29:55 +02:00
Andrey Antukh d3ac824912 🐛 Fix ICounted error on numeric-input token dropdown keyboard nav (#8803)
The options stored in options-ref is a delay (lazy value). In
on-token-key-down, it was passed raw to next-focus-index without being
dereferenced first, causing count to be called on a JS object that does
not implement ICounted.

Fix: dereference the delay in on-token-key-down (matching the existing
pattern in on-key-down), and make next-focus-index itself also handle
delays defensively. Add unit tests covering the delay case.
2026-04-01 11:21:01 +02:00
Andrey Antukh 350cc01b72 🐛 Fix frontend test script 2026-04-01 11:10:28 +02:00
Andrey Antukh 8289120ea4 Replace pnpx with pnpm exec in render-wasm build script
The pnpx tries to fetch esbuild instead of using the already installed
version.
2026-04-01 11:10:28 +02:00
Andrey Antukh 103af0e31a 📎 Fix inconsistencies on error report context data 2026-04-01 10:26:04 +02:00
Andrey Antukh c097c4a6da Merge remote-tracking branch 'origin/staging' into develop 2026-04-01 09:26:05 +02:00
Andrey Antukh a04dd6cbfd Merge remote-tracking branch 'origin/main' into staging 2026-04-01 09:22:52 +02:00
Andrey Antukh 0ad5baa5d9 🐛 Fix mcp build script 2026-03-31 19:59:32 +02:00
Andrey Antukh d3c77130bc Merge pull request #8852 from penpot/niwinz-staging-handle-bad-token-sets
🐛 Allow read/decode token-sets with bad names
2026-03-31 18:11:14 +02:00
Andrey Antukh c200dc4040 🐛 Normalize token set name on creating token-set instance 2026-03-31 17:40:39 +02:00
Alonso Torres 04f98d7acd Change caddy config (#8849) 2026-03-31 17:24:02 +02:00
Dominik Jain ad1e598efe Add wait time in exportImage to account for async updates #8836
This is a temporary workaround for penpot/penpot-mcp#27.
It adds a wait time before exports via the export_shape tool to account
for asynchronous updates in Penpot, increasing the likelihood of exporting
the fully updated state.
2026-03-31 17:20:02 +02:00
Dominik Jain 2e24f1e2de 🐛 Fix lock file not being included in npm package
The root lock file not being present causes issues, because
the sub-project dependencies are managed by it.

The lack of inclusion of pnpm-lock.yaml was the root cause of #8829.
A dependency was updated incompatibly, breaking the release.

Since npm pack has a hard exclusion rule for pnpm-lock.yaml,
we include it under a different name and restore it at runtime.
2026-03-31 17:19:04 +02:00
Dominik Jain 94215447c9 🔥 Remove redundant lock file in server package
Lock file in mcp/ base package should be single source of truth.
2026-03-31 17:19:04 +02:00
alonso.torres 6e2dc0c3dc 🐛 Fix problem with token performance 2026-03-31 15:44:20 +02:00
Yamila Moreno 084ca401fd 📚 Improve recommended settings for self-host (#8846) 2026-03-31 15:11:58 +02:00
Andrey Antukh e6ab57f719 📎 Add minor cosmetic reoriganization on tokens-lib 2026-03-31 15:05:54 +02:00
Andrey Antukh 667a995e66 Make update-token- noop if token is not modified 2026-03-31 15:04:26 +02:00
Andrey Antukh 9d703439bd Add helper for define clock in millis precision 2026-03-31 15:03:27 +02:00
Andrey Antukh d6dc0fe1a7 🐛 Fix raw file data download on dbg pannel (#8847) 2026-03-31 14:36:43 +02:00
Eva Marco 28cefa9cba 🐛 Fix delay tokens on typography row (#8851) 2026-03-31 14:06:50 +02:00
Eva Marco 5f474f9536 🎉 Add typography token row (#8749)
* 🔧 Create flag

*  Add typography type on tokens by input

* 🎉 Add typography token row

* ♻️ Update sub-components to use new style

* 🎉 Add disabled option on radio-buttons* component

* 🎉 Add combobox search in a new component

* 🎉 Divide components

* 🐛 Fix placeholder
2026-03-31 13:48:49 +02:00
Xaviju 27313e6add 🐛 Fix error on path and review UI (#8844) 2026-03-31 13:04:47 +02:00
Dominik Jain 8ce860cf0c 📚 Update MCP git branch information 2026-03-31 12:15:12 +02:00
Dominik Jain f3cc6d0d72 🎉 Add MCP version mismatch detection
If the MCP version (as given in mcp/package.json) does not match
the Penpot version (as given by penpot.version), display a warning
message in the plugin UI.

This is important for users running the local MCP server, as it
is a common failure mode to combine the MCP server with an
incompatible Penpot version.
2026-03-31 12:15:12 +02:00
Dominik Jain 905f4fa5dd Provide root package version as PENPOT_MCP_VERSION in plugin
Update current root package version using set-version script
2026-03-31 12:15:12 +02:00
Alejandro Alonso 56b28b5440 Merge remote-tracking branch 'origin/staging' into develop 2026-03-31 11:29:44 +02:00
Alejandro Alonso 0122eaa391 Merge pull request #8843 from penpot/alotor-viewer-thumbnails
🐛 Fix problem with thumbnails
2026-03-31 11:29:32 +02:00
Belén Albeza 114639ca1e Update check-browser? helper 2026-03-31 10:15:57 +02:00
Belén Albeza e9d30bf2c1 🐛 Fix text selection on Safari 18/26 (wasm) 2026-03-31 10:15:57 +02:00
alonso.torres a75e0c3071 🐛 Fix problem with thumbnails 2026-03-31 10:15:35 +02:00
Alejandro Alonso 153277d152 Merge pull request #8823 from penpot/elenatorro-13350-add-components-preview-using-render
🔧 Use wasm render for components thumbnail
2026-03-31 09:45:53 +02:00
Elena Torro 784ad8ab75 🔧 Use wasm render for components thumbnail 2026-03-31 08:53:50 +02:00
Andrey Antukh c1044ac522 Add protection for stale cache of js assets loading issues (#8638)
*  Use update-when for update dashboard state

This make updates more consistent and reduces possible eventual
consistency issues in out of order events execution.

* 🐛 Detect stale JS modules at boot and force reload

When the browser serves cached JS files from a previous deployment
alongside a fresh index.html, code-split modules reference keyword
constants that do not exist in the stale shared.js, causing TypeError
crashes.

This adds a compile-time version tag (via goog-define / closure-defines)
that is baked into the JS bundle. At boot, it is compared against the
runtime version tag from index.html (which is always fresh due to
no-cache headers). If they differ, the app forces a hard page reload
before initializing, ensuring all JS modules come from the same build.

* 📎 Ensure consistent version across builds on github e2e test workflow

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-30 19:46:51 +02:00
Aitor Moreno 5ed949f2b7 Merge pull request #8830 from penpot/superalex-fix-text-selection-on-vertical-align-middle-bottom
🐛 Fix text selection on vertical align middle/bottom
2026-03-30 17:15:11 +02:00
ElenzakaleidosandLuis de Dios 7ecfe77338 Update README.md (#8833)
* 💄 Update README.md

I updated the two images and removed the fest announcement

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>

* ♻️ Improve Markdown

---------

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-03-30 16:17:39 +02:00
Eva Marco 04f6307c69 🐛 Fix radio-buttons component in the DS (#8820) 2026-03-30 13:35:24 +02:00
Andrey Antukh 04892dd688 🐛 Fix content attribute sync group resolution by shape type (#8724)
* 🐛 Fix content attribute sync group resolution by shape type

The :content attribute was mapped to a single sync group (:content-group)
but it is used by both path and text shapes with different synchronization
needs. This caused incorrect component synchronization when editing content
on path shapes, as they should sync under :geometry-group instead of
:content-group.

Changes:
- Make sync-attrs allow type-dependent group mapping via maps
- Add resolve-sync-group and resolve-sync-groups helper functions
- Update all sync-attr lookups to use shape type for proper resolution
- Fix touched checks to handle multiple possible sync groups

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

*  Make PR feedback changes

* 🔥 Remove unused function

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-30 13:11:01 +02:00
Andrey Antukh ef3143dcb8 📎 Update changelog 2026-03-30 12:35:39 +02:00
Andrey Antukh 87bb1b8e74 Merge remote-tracking branch 'origin/staging' into develop 2026-03-30 12:29:43 +02:00
Andrey Antukh 264cd0aaac Merge remote-tracking branch 'origin/main' into staging 2026-03-30 12:29:07 +02:00
Andrey Antukh 3767ee05bb Add retry mechanism for idenpotent get repo requests on frontend (#8792)
* ♻️ Handle fetch-error gracefully with toast instead of full-page error

Network-level failures (lost connectivity, DNS failure, etc.) on RPC
calls were propagating as :internal/:fetch-error to the global error
handler, which replaced the entire UI with a full-page error screen.

Now the :internal handler distinguishes :fetch-error from other internal
errors and shows a non-intrusive toast notification instead, allowing
the user to continue working.

*  Add automatic retry with backoff for idempotent RPC requests

Idempotent (GET) RPC requests are now automatically retried up to 3
times with exponential back-off (1s, 2s, 4s) when a transient error
occurs.  Retryable errors include: network-level failures
(:fetch-error), 502 Bad Gateway, 503 Service Unavailable, and browser
offline (status 0).

Mutation (POST) requests are never retried to avoid unintended
side-effects.  Non-transient errors (4xx client errors, auth errors,
validation errors) propagate immediately without retry.

* ♻️ Make retry helpers public with configurable parameters

Make retryable-error? and with-retry public functions, and replace
private constants with a default-retry-config map.  with-retry now
accepts an optional config map (:max-retries, :base-delay-ms) enabling
callers and tests to customize retry behavior.

*  Add tests for RPC retry mechanism

Comprehensive tests for the retry helpers in app.main.repo:
- retryable-error? predicate: covers all retryable types (fetch-error,
  bad-gateway, service-unavailable, offline) and non-retryable types
  (validation, authentication, authorization, plain errors)
- with-retry observable wrapper: verifies immediate success, recovery
  after transient failures, max-retries exhaustion, no retry for
  non-retryable errors, fetch-error retry, custom config, and mixed
  error scenarios

* ♻️ Introduce :network error type for fetch-level failures

Replace the awkward {:type :internal :code :fetch-error} combination
with a proper {:type :network} type in app.util.http/fetch.  This makes
the error taxonomy self-explanatory and removes the special-case branch
in the :internal handler.

Consequences:
- http.cljs: emit {:type :network} instead of {:type :internal :code :fetch-error}
- errors.cljs: add a dedicated ptk/handle-error :network method (toast);
  restore :internal handler to its original unconditional full-page error form
- repo.cljs: simplify retryable-types and retryable-error? — :network
  replaces the former :internal special-case, no code check needed
- repo_test.cljs: update tests to use {:type :network}

* 📚 Add comment explaining the use of bit-shift-left
2026-03-30 12:20:02 +02:00
Alejandro Alonso 62cc555084 🐛 Fix spurious mixed text styles on multi-span selection 2026-03-30 12:19:16 +02:00
Andrey Antukh e7e98255d9 Add scroll and zoom raf throttling (#8812)
* ⬆️ Update opencode and copilot deps

* 🐛 Decouple workspace-content from workspace-local to reduce scroll re-renders

Move workspace-local subscription from workspace-content* (parent) into
viewport* and viewport-classic* (children). workspace-content* now only
subscribes to the new workspace-vport derived atom, which changes only on
window resize — not on every scroll event. This prevents the sidebar,
palette and other workspace-content children from re-rendering on scroll.

* 🐛 Throttle wheel events to one state update per animation frame

Accumulate wheel event deltas in a mutable ref and flush them via
requestAnimationFrame, so that multiple wheel events between frames
produce a single state mutation instead of one per event. This prevents
the cascade of synchronous React re-renders (via useSyncExternalStore)
that can exceed the maximum update depth on rapid scrolling.

Both panning (scroll) and zoom (ctrl/mod+wheel) are throttled. Scroll
deltas are summed additively; zoom scales are compounded multiplicatively
with the latest cursor point used as the zoom center.

* ♻️ Extract schedule-zoom! and schedule-scroll! from on-mouse-wheel

* ♻️ Avoid zoom dep on on-mouse-wheel by using a ref
2026-03-30 12:06:56 +02:00
Alejandro Alonso 36c23faae0 🐛 Fix sync WASM viewport outlines with live modifiers 2026-03-30 11:21:24 +02:00
Andrey Antukh 6264c0c217 Add scroll and zoom raf throttling (#8812)
* ⬆️ Update opencode and copilot deps

* 🐛 Decouple workspace-content from workspace-local to reduce scroll re-renders

Move workspace-local subscription from workspace-content* (parent) into
viewport* and viewport-classic* (children). workspace-content* now only
subscribes to the new workspace-vport derived atom, which changes only on
window resize — not on every scroll event. This prevents the sidebar,
palette and other workspace-content children from re-rendering on scroll.

* 🐛 Throttle wheel events to one state update per animation frame

Accumulate wheel event deltas in a mutable ref and flush them via
requestAnimationFrame, so that multiple wheel events between frames
produce a single state mutation instead of one per event. This prevents
the cascade of synchronous React re-renders (via useSyncExternalStore)
that can exceed the maximum update depth on rapid scrolling.

Both panning (scroll) and zoom (ctrl/mod+wheel) are throttled. Scroll
deltas are summed additively; zoom scales are compounded multiplicatively
with the latest cursor point used as the zoom center.

* ♻️ Extract schedule-zoom! and schedule-scroll! from on-mouse-wheel

* ♻️ Avoid zoom dep on on-mouse-wheel by using a ref
2026-03-30 11:08:33 +02:00
Andrey Antukh d7e0b0cf9f 📎 Add check-fmt script to root package.json 2026-03-30 11:06:13 +02:00
Andrey Antukh b6524881e0 🐛 Fix crash in apply-text-modifier with nil selrect or modifier (#8762)
* 🐛 Fix crash in apply-text-modifier with nil selrect or modifier

Guard apply-text-modifier against nil text-modifier and nil selrect
to prevent the 'invalid arguments (on pointer constructor)' error
thrown by gpt/point when called with an invalid map.

- In text-wrapper: only call apply-text-modifier when text-modifier is
  not nil (avoids unnecessary processing)
- In apply-text-modifier: handle nil text-modifier by returning shape
  unchanged; guard selrect access before calling gpt/point

* 📚 Add tests for apply-text-modifier in workspace texts

Add exhaustive unit tests covering all paths of apply-text-modifier:
- nil modifier returns shape unchanged (identity)
- modifier with no recognised keys leaves shape unchanged
- :width / :height modifiers resize shape correctly
- nil :width / :height keys are skipped
- both dimensions applied simultaneously
- :position-data is set and nil-guarded
- position-data coordinates translated by delta on resize
- shape with nil selrect + nil modifier does not throw
- position-data-only modifier on shape without selrect is safe
- selrect origin preserved when no dimension changes
- result always carries required shape keys

* 🐛 Fix zero-dimension selrect crash in change-dimensions-modifiers

When a text shape is decoded from the server via map->Rect (which
bypasses make-rect's 0.01 minimum enforcement), its selrect can have
width or height of exactly 0.  change-dimensions-modifiers and
change-size were dividing by these values, producing Infinity scale
factors that propagated through the transform pipeline until
calculate-selrect / center->rect returned nil, causing gpt/point to
throw 'invalid arguments (on pointer constructor)'.

Fix: before computing scale factors, guard sr-width / sr-height (and
old-width / old-height in change-size) against zero/negative and
non-finite values.  When degenerate, fall back to the shape's own
top-level :width/:height so the denominator and proportion-lock base
remain consistent.

Also simplify apply-text-modifier's delta calculation now that the
transform pipeline is guaranteed to produce a valid selrect, and
update the test suite to test the exact degenerate-selrect scenario
that triggered the original crash.

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

* ♻️ Simplify change-dimensions-modifiers internal logic

- Remove the intermediate 'size' map ({:width sr-width :height sr-height})
  that was built only to be assoc'd and immediately destructured back into
  width/height; compute both values directly instead.

- Replace the double-negated condition 'if-not (and (not ignore-lock?) …)'
  with a clear positive 'locked?' binding, and flatten the three-branch
  if-not/if tree into two independent if expressions keyed on 'attr'.

- Call safe-size-rect once and reuse its result for both the fallback
  sizes and the scale computation, eliminating a redundant call.

- Access :transform and :transform-inverse via direct map lookup rather
  than destructuring in the function signature, consistent with how the
  rest of the let-block reads shape keys.

- Clean up change-size to use the same destructuring style as the updated
  function ({sr-width :width sr-height :height}).

- Fix typo in comment: 'havig' -> 'having'.

*  Add tests for change-size and change-dimensions-modifiers

Cover the main behavioural contract of both functions:

change-size:
- Scales both axes to the requested target dimensions.
- Sets the resize origin to the shape's top-left point.
- Nil width/height each fall back to the current dimension (scale 1 on
  that axis); both nil produces an identity resize that is optimised away.
- Propagates the shape's transform and transform-inverse matrices into the
  resulting GeometricOperation.

change-dimensions-modifiers:
- Changing :width without proportion-lock only scales the x-axis (y
  scale stays 1), and vice-versa for :height.
- With proportion-lock enabled, changing :width adjusts height via the
  inverse proportion, and changing :height adjusts width via the
  proportion.
- ignore-lock? true bypasses proportion-lock regardless of shape state.
- Values below 0.01 are clamped to 0.01 before computing the scale.
- End-to-end: applying the returned modifiers via gsh/transform-shape
  yields the expected selrect dimensions.

*  Harden safe-size-rect with additional fallbacks

The previous implementation could still return an invalid rect in several
edge cases.  The new version tries four sources in order, accepting each
only if it passes a dedicated safe-size-rect? predicate:

1. :selrect           – used when width and height are finite, positive
                        and within [-max-safe-int, max-safe-int].
2. points->rect       – computed from the shape corner points; subject to
                        the same predicate.
3. Top-level shape fields (:x :y :width :height) – present on all rect,
                        frame, image, and component shape types.
4. grc/empty-rect     – a 0,0 0.01×0.01 unit rect used as last resort so
                        callers always receive a usable, non-crashing value.

The out-of-range check (> max-safe-int) is new: it rejects coordinates
that pass d/num? (finite) but exceed the platform integer boundary defined
in app.common.schema, which previously slipped through undetected.

Tests cover all four fallback paths, including the NaN, zero-dimension,
and max-safe-int overflow cases.

*  Optimise safe-size-rect for ClojureScript performance

- Replace (when (some? rect) ...) with (and ^boolean (some? rect) ...)
  to keep the entire predicate as a single boolean expression without
  introducing an implicit conditional branch.

- Replace keyword access (:width rect) / (:height rect) with
  dm/get-prop calls, consistent with the hot-path style used throughout
  the rest of the namespace.

- Add ^boolean type hints to every sub-expression of the and chain in
  safe-size-rect? (d/num?, pos?, <=) so the ClojureScript compiler emits
  raw JS boolean operations instead of boxing the results through
  cljs.core/truth_.

- Replace (when (safe-size-rect? ...) value) in safe-size-rect with
  (and ^boolean (safe-size-rect? ...) value), avoiding an extra
  conditional and keeping the or fallback chain free of allocated
  intermediate objects.

*  Use safe-size-rect in apply-text-modifier delta-move computation

safe-size-rect was already used inside change-dimensions-modifiers to
guard the resize scale computation. However, apply-text-modifier in
texts.cljs was still reading (:selrect shape) and (:selrect new-shape)
directly to build the delta-move vector via gpt/point.

gpt/point raises "invalid arguments (on pointer constructor)" when
given a nil value or a map with non-finite :x/:y, which can happen when
a shape's selrect is missing or degenerate (e.g. decoded from the server
via map->Rect, bypassing make-rect's 0.01 floor).

Changes:
- Promote safe-size-rect from defn- to defn in app.common.types.modifiers
  so it can be reused by consumers outside the namespace.
- Replace the two raw (:selrect …) accesses in the delta-move computation
  with (ctm/safe-size-rect …), which always returns a valid, finite rect
  through the established four-step fallback chain.
- Add two frontend tests covering the delta-move path with a fully
  degenerate (zero-dimension) selrect, ensuring neither a bare
  position-data modifier nor a combined width+position-data modifier
  throws.

* ♻️ Ensure all test shapes are proper Shape records in modifiers-test

All shapes in safe-size-rect-fallbacks tests now start from a proper
Shape record built by cts/setup-shape (via make-shape) instead of plain
hash-maps. Each test that mutates geometry fields (selrect, points,
width, height) does so via assoc on the already-initialised record,
which preserves the correct type while isolating the field under test.

A (cts/shape? shape) assertion is added to each fallback test to make
the type guarantee explicit and guard against regressions.

The unused shape-with-selrect helper (which built a bare map) is
removed.

* 🔥 Remove dead code and tighten visibility in app.common.types.modifiers

Dead functions removed (zero callers across the entire codebase):
- modifiers->transform-old: superseded by modifiers->transform; only
  ever appeared in a commented-out dev/bench.cljs entry.
- change-recursive-property: no callers anywhere.
- move-parent-modifiers, resize-parent-modifiers: convenience wrappers
  for the parent-geometry builder functions; never called.
- remove-children-modifiers, add-children-modifiers,
  scale-content-modifiers: single-op convenience builders; never called.
- select-structure: projection helper; only referenced by
  select-child-geometry-modifiers which is itself dead.
- select-child-geometry-modifiers: no callers anywhere.

Functions narrowed from defn to defn- (used only within this namespace):
- valid-vector?: assertion helper called only by move/resize builders.
- increase-order: called only by add-modifiers.
- transform-move!, transform-resize!, transform-rotate!, transform!:
  steps of the modifiers->transform pipeline.
- modifiers->transform1: immediate helper for modifiers->transform; the
  doc-string describing it as 'multiplatform' was also removed since it
  is an implementation detail.
- transform-text-node, transform-paragraph-node: leaf helpers for
  scale-text-content.
- update-text-content, scale-text-content, apply-scale-content: internal
  scale-content pipeline; all called only by apply-modifier.
- remove-children-set: called only by apply-modifier.
- select-structure: demoted to defn- rather than deleted because it is
  still called by select-child-structre-modifiers, which has external
  callers.

*  Add more tests for modifiers

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-30 11:04:54 +02:00
Andrey Antukh a149f31d56 Add comprehensive tests for shape layout namespace (#8759)
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-30 11:03:53 +02:00
Andrey Antukh e4cc7d72da 🐛 Fix incorrect attrs references on generate-sync-shape (#8776)
For :component
2026-03-30 11:03:34 +02:00
Juan de la Cruz 932305cbd8 Merge pull request #8832 from penpot/alotor-fix-mcp-plugin-themne
🐛 Fix problem with mcp plugin theme
2026-03-30 10:30:39 +02:00
alonso.torres 623608799a 🐛 Fix problem with mcp plugin theme 2026-03-30 10:02:11 +02:00
Pablo Alba 06aec4b3a3 Add is-default to nitrate summary (#8814) 2026-03-30 09:39:35 +02:00
Xaviju 1b68318c6b 🐛 Token tree must be expanded by default (#8799) 2026-03-30 08:14:17 +02:00
Alejandro Alonso 45b25c23ab 🐛 Fix text selection on vertical align middle/bottom 2026-03-30 07:37:07 +02:00
alonso.torres 6ca34908d8 🐛 Fix problem with grid edition in Safari 2026-03-27 14:21:32 +01:00
Alejandro Alonso dff381c4fe Merge remote-tracking branch 'origin/staging' into develop 2026-03-27 13:54:38 +01:00
Alejandro Alonso 2f4a655523 Merge pull request #8800 from penpot/superalex-fix-render-wasm-clipped-frame-inner-stroke-artifact
🐛 Fix inset child clip for frames with inner stroke
2026-03-27 13:54:19 +01:00
Alejandro Alonso 508c67c930 Merge remote-tracking branch 'origin/staging' into develop 2026-03-27 12:21:30 +01:00
Elena Torró 486a08189e Merge pull request #8811 from penpot/alotor-fix-position-data
🐛 Fix problem with position data in new render
2026-03-27 12:06:07 +01:00
María Valderrama 7f228e58c6 Update delete team modal when in org 2026-03-27 11:36:42 +01:00
Alejandro Alonso 943757a36c Merge pull request #8817 from penpot/niwinz-unhandled-exception-text
🐛 Guard against null focusNode/anchorNode in text-editor
2026-03-27 11:14:15 +01:00
Andrey Antukh d67c7f1c8e Add retry mechanism for idenpotent get repo requests on frontend (#8792)
* ♻️ Handle fetch-error gracefully with toast instead of full-page error

Network-level failures (lost connectivity, DNS failure, etc.) on RPC
calls were propagating as :internal/:fetch-error to the global error
handler, which replaced the entire UI with a full-page error screen.

Now the :internal handler distinguishes :fetch-error from other internal
errors and shows a non-intrusive toast notification instead, allowing
the user to continue working.

*  Add automatic retry with backoff for idempotent RPC requests

Idempotent (GET) RPC requests are now automatically retried up to 3
times with exponential back-off (1s, 2s, 4s) when a transient error
occurs.  Retryable errors include: network-level failures
(:fetch-error), 502 Bad Gateway, 503 Service Unavailable, and browser
offline (status 0).

Mutation (POST) requests are never retried to avoid unintended
side-effects.  Non-transient errors (4xx client errors, auth errors,
validation errors) propagate immediately without retry.

* ♻️ Make retry helpers public with configurable parameters

Make retryable-error? and with-retry public functions, and replace
private constants with a default-retry-config map.  with-retry now
accepts an optional config map (:max-retries, :base-delay-ms) enabling
callers and tests to customize retry behavior.

*  Add tests for RPC retry mechanism

Comprehensive tests for the retry helpers in app.main.repo:
- retryable-error? predicate: covers all retryable types (fetch-error,
  bad-gateway, service-unavailable, offline) and non-retryable types
  (validation, authentication, authorization, plain errors)
- with-retry observable wrapper: verifies immediate success, recovery
  after transient failures, max-retries exhaustion, no retry for
  non-retryable errors, fetch-error retry, custom config, and mixed
  error scenarios

* ♻️ Introduce :network error type for fetch-level failures

Replace the awkward {:type :internal :code :fetch-error} combination
with a proper {:type :network} type in app.util.http/fetch.  This makes
the error taxonomy self-explanatory and removes the special-case branch
in the :internal handler.

Consequences:
- http.cljs: emit {:type :network} instead of {:type :internal :code :fetch-error}
- errors.cljs: add a dedicated ptk/handle-error :network method (toast);
  restore :internal handler to its original unconditional full-page error form
- repo.cljs: simplify retryable-types and retryable-error? — :network
  replaces the former :internal special-case, no code check needed
- repo_test.cljs: update tests to use {:type :network}

* 📚 Add comment explaining the use of bit-shift-left
2026-03-27 11:10:26 +01:00
Pablo Alba 8cc6c40b87 Update nitrate organizations dropdown visibility 2026-03-27 10:47:47 +01:00
Marina López 1ecfbef6fb ♻️ Refactor forms file 2026-03-27 10:39:47 +01:00
Marina López abe328973c 💄 Fix focus radio button 2026-03-27 10:39:47 +01:00
Andrey Antukh 3be1ae2ac1 🐛 Guard against null focusNode/anchorNode in text-editor 2026-03-27 10:26:54 +01:00
Alejandro Alonso 19b1f508d3 Merge remote-tracking branch 'origin/staging' into develop 2026-03-27 10:18:01 +01:00
Alejandro Alonso 8db63c9770 Merge pull request #8785 from penpot/ladybenko-fix-repeatable-key
🐛 Fix repeateable keys triggering an infinite React loop in text editor v2
2026-03-27 10:17:44 +01:00
Alejandro Alonso 9c1f2e9af8 Merge remote-tracking branch 'origin/staging' into develop 2026-03-27 10:06:54 +01:00
Alejandro Alonso 0da6b87b5f 🎉 Allow get param to set antialias threshold 2026-03-27 10:00:54 +01:00
alonso.torres f3b762855b 🐛 Fix problem with position data in new render 2026-03-27 09:29:28 +01:00
Marius 342b07779d 🌐 Add translations for: German
Currently translated at 95.1% (1974 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-03-26 20:09:48 +01:00
Andrey Antukh 4174d6a05b 🎉 Add tests for undo-stack helper function on common (#8766) 2026-03-26 19:44:49 +01:00
Pablo Alba 51b9023640 Show nitrate org name on invitation to join team email (#8802) 2026-03-26 17:25:30 +01:00
Pablo Alba 4b4b99a949 Add response to nitrate request when nitrate is down (#8722) 2026-03-26 17:25:07 +01:00
Andrey Antukh 6db3c6cf89 🐛 Fix regression on subpath support (#8793) 2026-03-26 15:43:30 +01:00
Andrey Antukh 0dfa62a5b6 🐛 Improve error reporting on request parsing failures (#8805)
Include request URI and status in frontend handle-response error data,
and add request path/context to backend IOException handler logs and
response body. Previously these errors had no identifying information
about which endpoint or request caused the failure.
2026-03-26 15:42:49 +01:00
Penpot Dev 0ad3ae0620 📚 Add explicit commit guideline to builtin agents 2026-03-26 14:42:22 +01:00
Andrey AntukhandPenpot Dev 3eaf67a385 🐛 Fix fetch abort errors escaping the unhandled exception handler (#8801)
When AbortController.abort(reason) is called with a custom reason (a
ClojureScript ExceptionInfo), modern browsers (Chrome 98+, Firefox 97+)
reject the fetch promise with that reason object directly instead of with
the canonical DOMException{name:'AbortError'}.  The ExceptionInfo has
.name === 'Error', so both the p/catch guard and is-ignorable-exception?
failed to recognise it as an abort, letting it surface to users as an
error toast.

Fix by calling .abort() without a reason so the browser always produces
a native DOMException whose .name is 'AbortError', which is correctly
handled by all existing guards.

Also add a defense-in-depth check in is-ignorable-exception? that
filters errors whose message matches the 'fetch to \'' prefix, guarding
against any future re-introduction of a custom abort reason.

Co-authored-by: Penpot Dev <dev@penpot.app>
2026-03-26 14:13:38 +01:00
Andrey Antukh 1a4ca6d04b 📚 Update frontend/AGENTS.md file 2026-03-26 14:12:11 +01:00
Alejandro Alonso 6403c8deee 🐛 Fix inset child clip for frames with inner stroke 2026-03-26 13:33:15 +01:00
Belén Albeza 85425e2ccd 🐛 Fix repeateable keys triggering an infinite React loop in text editor v2 2026-03-26 13:17:09 +01:00
Pablo Alba 1af2521f64 Add create default team org for nitrate on adding an user to a team 2026-03-26 13:17:05 +01:00
Penpot Dev 945efdb0b4 🔥 Remove .opencode/skills
I think they make ai agent work worse.
2026-03-26 13:09:31 +01:00
Penpot Dev 2ba3605f11 ⬆️ Update root repo deps 2026-03-26 13:09:31 +01:00
Andrey Antukh 5fca9457cf ♻️ Extract use-portal-container hook to reduce duplication (#8798)
The dedicated-container portal pattern was repeated across 6 components.
Extract it into a reusable use-portal-container hook under app.main.ui.hooks.
2026-03-26 12:45:42 +01:00
Andrey Antukh 448d85febb 🐛 Fix regression on mcp server listen port 2026-03-26 12:28:27 +01:00
Elena Torró 5ae4b21046 Merge pull request #8791 from penpot/superalex-update-new-render-screenshots
🎉 Updating wasm render screenshots
2026-03-26 12:00:53 +01:00
Elena Torró 72cfd5d996 Merge pull request #8770 from penpot/superalex-fix-text-v2-firefox-word-selection-styles
🐛 Fix wrong typography font size in sidebar when selecting text in Firefox (editor v2)
2026-03-26 11:59:23 +01:00
Elena Torro 1641eec672 🎉 Add stroke to path 2026-03-26 11:43:06 +01:00
Alejandro Alonso 74af101462 Merge remote-tracking branch 'origin/staging' into develop 2026-03-26 11:42:35 +01:00
Alejandro Alonso ab404340f8 Merge remote-tracking branch 'origin/main' into staging 2026-03-26 11:36:42 +01:00
Marina López 6fa0c5ceaa Add organization avatar 2026-03-26 10:54:55 +01:00
Xaviju 713ff6190b 🔧 Add SCSS linter (stylelint) (#8592)
* 🔧 Add SCSS linter (stylelint)

*  Fix default standard scss errors with extends - WIP

*  Fix default standard scss errors

*  Update and cleanup

*  Update and cleanup

*  Update and cleanup

* 🐛 Fix broken visual regression tests

* 📎 Add to CHANGES

* ♻️ Remove unused class
2026-03-26 10:09:54 +01:00
alonso.torres 6e03a191a3 🐛 Fix return type for combineAsVariants methods 2026-03-26 09:37:31 +01:00
Luis de Dios a7e3d7963a 🐛 Fix do not manage tab notifications when MCP flag is disabled 2026-03-26 09:37:24 +01:00
Marina López cd67dc42c4 🐛 Fix dates to avoid show them in english when browser is in auto (#8775) 2026-03-26 09:33:13 +01:00
alonso.torres 52a576dc4d 🐛 Fix problem with fills in text range 2026-03-26 09:14:50 +01:00
andrés gonzález 1740d2e3d1 🌐 Differentiate MCP key copy from access token copy (#8786) 2026-03-26 08:21:44 +01:00
Alejandro Alonso b32a2d32d8 🎉 Updating wasm render screenshots 2026-03-26 08:06:36 +01:00
Alejandro Alonso 85cfb8161a 📎 Rename skills 2026-03-25 23:50:29 +01:00
Alejandro Alonso a34a668f94 📎 Add opencode skills 2026-03-25 23:48:18 +01:00
Alejandro Alonso 811d53be12 Merge remote-tracking branch 'origin/main' into staging 2026-03-25 18:27:22 +01:00
andrés gonzález a60020ea98 💄 Change link from Integrations to MCP docs (#8784) 2026-03-25 18:07:37 +01:00
Alejandro Alonso d2c609f8a4 Merge pull request #8783 from penpot/alotor-fix-shadows
🐛 Fix problem with shadows
2026-03-25 17:51:08 +01:00
alonso.torres 7c5aec4274 🐛 Fix problem with shadows 2026-03-25 17:16:41 +01:00
andrés gonzález f01bfb7a26 🐛 Adding missing images to mcp doc (#8782) 2026-03-25 16:30:47 +01:00
Alejandro Alonso efd6b95ff6 🐛 Clear cache canvas on zoom. Teep textures-only invalidation on pan 2026-03-25 16:18:47 +01:00
Alejandro Alonso 3c2430b16c Merge pull request #8778 from penpot/alotor-bugfix-z-index
🐛 Fix problem with z-index
2026-03-25 15:59:15 +01:00
alonso.torres a5d908629b 🐛 Fix problems with z-index 2026-03-25 15:56:56 +01:00
Andrey Antukh 737e04fe2c 🐛 Fix nil deref on missing bounds in layout modifier propagation (#8735)
* 🐛 Fix nil deref on missing bounds in layout modifier propagation

When a parent shape has a child ID in its shapes vector that does
not exist in the objects map, the layout modifier code crashes
because it derefs nil from the bounds map.

The root cause is that children from the parent shapes list are
not validated against the objects map before being passed to the
layout modifier pipeline. Children with missing IDs pass through
unchecked and reach apply-modifiers where bounds lookup fails.

Fix by adding nil guards in apply-modifiers to skip children
without bounds, and changing map to keep to filter them out.

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

* 📎 Add tests for nil bounds in layout modifier propagation

Tests cover flex and grid layout scenarios where a parent
frame has child IDs in its shapes vector that do not exist
in the objects map, verifying that set-objects-modifiers
handles these gracefully without crashing.

Tests:
- Flex layout with normal children (baseline)
- Flex layout with non-existent child in shapes
- Flex layout with only non-existent children
- Grid layout with non-existent child in shapes
- Flex layout resize propagation with ghost children
- Nested flex layout with non-existent child in outer frame

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-25 15:36:21 +01:00
andrés gonzález 38bf6c3603 📚 Add MCP docs (#8772) 2026-03-25 15:32:24 +01:00
alonso.torres 28b4c14b95 🐛 Fix problem when removing margin 2026-03-25 13:28:56 +01:00
Eva MarcoandLuis de Dios ba8b552df2 🐛 Fix shared button variant and title (#8696)
Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-03-25 13:08:41 +01:00
moktamd 4e3dc6532a 🐛 Default MCP listen addresses to localhost instead of 0.0.0.0
Signed-off-by: moktamd <moktamd@users.noreply.github.com>
2026-03-25 12:34:33 +01:00
Andrey Antukh a2672a598c 🐛 Fix TypeError when token error map lacks :error/fn key (#8767)
* 🐛 Fix TypeError when token error map lacks :error/fn key

Guard against missing :error/fn in token form control resolve streams.
When schema validation errors are produced they may not carry an
:error/fn key; calling nil as a function caused a TypeError crash.
Apply an if-let guard at all 7 affected sites across input.cljs,
color_input.cljs and fonts_combobox.cljs, falling back to :message
or returning the error map unchanged.

* ♻️ Extract token error helpers and add unit tests

Extract resolve-error-message and resolve-error-assoc-message helpers
into errors.cljs, replacing the seven duplicated inline lambdas in
input.cljs, color_input.cljs and fonts_combobox.cljs with named
function references.  Add frontend-tests.tokens.token-errors-test
covering both helpers for the normal path (:error/fn present) and the
fallback path (schema-validation errors that lack :error/fn).

Signed-off-by: Penpot Dev <dev@penpot.app>

---------

Signed-off-by: Penpot Dev <dev@penpot.app>
2026-03-25 12:12:18 +01:00
Andrey Antukh 0a98100536 Merge remote-tracking branch 'origin/staging' into develop 2026-03-25 12:07:27 +01:00
Andrey Antukh af4548a6ed Merge remote-tracking branch 'origin/main' into staging 2026-03-25 12:02:49 +01:00
Alejandro Alonso d361a2ca6e Merge remote-tracking branch 'origin/staging' into develop 2026-03-25 10:42:24 +01:00
Alejandro Alonso b5b51e21c2 Merge pull request #8741 from penpot/superalex-fix-text-align-empty-paragraph-v2
🐛 Fix text align empty paragraph v2
2026-03-25 10:42:05 +01:00
Alejandro Alonso 334039668d 🐛 Fix wrong typography font size in sidebar when selecting text in Firefox (editor v2) 2026-03-25 10:38:56 +01:00
Xaviju a59bd05c4f 🐛 Update visual regression tests (#8730) 2026-03-25 09:51:32 +01:00
Alejandro Alonso caa25c70fc Merge remote-tracking branch 'origin/staging' into develop 2026-03-25 09:38:06 +01:00
Alejandro Alonso 6268a8aaf1 Merge pull request #8764 from penpot/alotor-fix-issue-text-sizing
🐛 Fix resize text modifiers
2026-03-25 07:45:25 +01:00
alonso.torres 6b609566e1 🐛 Fix resize text modifiers 2026-03-25 07:29:20 +01:00
Andrey Antukh 0dfac801a4 Improve error handling and exception formatting (#8757)
*  Improve error handling and exception formatting

- Enhance exception formatting with visual separators and cause chaining
- Add new handler for :internal error type
- Refine error types: change assertion-related errors to :assertion type
- Improve error messages and hints consistency
- Clean up error handling in zip utilities and HTTP modules

* 🐛 Properly handle AbortError on fetch request unsubscription

When a fetch request in-flight is cancelled due to RxJS unsubscription
(e.g. navigating away from the workspace while thumbnail loads are
pending), the AbortController.abort() call triggers a catch handler
that previously relied solely on a @unsubscribed? flag to suppress the
error.

This was unreliable: nested observables spawned inside rx/mapcat (such
as datauri->blob-uri conversions within get-file-object-thumbnails)
could abort independently, with their own AbortController instances,
meaning the outer unsubscribed? flag was never set and the AbortError
propagated as an unhandled exception.

Add an explicit AbortError name check as a disjunctive condition so
that abort errors originating from any observable in the chain are
suppressed at the source, regardless of subscription state.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 19:55:23 +01:00
Andrey Antukh 01284e2a00 Improve error handling and exception formatting (#8757)
*  Improve error handling and exception formatting

- Enhance exception formatting with visual separators and cause chaining
- Add new handler for :internal error type
- Refine error types: change assertion-related errors to :assertion type
- Improve error messages and hints consistency
- Clean up error handling in zip utilities and HTTP modules

* 🐛 Properly handle AbortError on fetch request unsubscription

When a fetch request in-flight is cancelled due to RxJS unsubscription
(e.g. navigating away from the workspace while thumbnail loads are
pending), the AbortController.abort() call triggers a catch handler
that previously relied solely on a @unsubscribed? flag to suppress the
error.

This was unreliable: nested observables spawned inside rx/mapcat (such
as datauri->blob-uri conversions within get-file-object-thumbnails)
could abort independently, with their own AbortController instances,
meaning the outer unsubscribed? flag was never set and the AbortError
propagated as an unhandled exception.

Add an explicit AbortError name check as a disjunctive condition so
that abort errors originating from any observable in the chain are
suppressed at the source, regardless of subscription state.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 19:54:05 +01:00
Andrey Antukh cc73a768d5 Add comprehensive tests for path and descendant namespaces (#8755)
Add tests for app.common.types.path.subpath, helpers, segment,
bool operations (union/difference/intersection/exclude), top-level
path API, and shape-to-path conversion. Covers previously untested
functions across all path sub-namespaces. Tests pass on both JVM
and JS (ClojureScript/Node) platforms.
2026-03-24 19:53:22 +01:00
Andrey Antukh 3ef100427b 🎉 Add tests for app.common.data namespace (#8750)
*  Add tests for predicates and ordered data structures

Adds tests for boolean-or-nil?, in-range?, ordered-set/map creation
and ordering, oassoc/oassoc-in/oupdate-in/oassoc-before, and the
ordered collection index helpers (adds/inserts/addm/insertm-at-index).

*  Add tests for lazy and sequence helpers

Adds tests for concat-all, mapcat, zip, zip-all, enumerate,
interleave-all, add-at-index, take-until, safe-subvec and domap.

*  Add tests for collection lookup and map manipulation

Adds tests for group-by, seek, index-by, index-of-pred/of,
replace-by-id, getf, vec-without-nils, without-nils,
without-qualified, without-keys, deep-merge, dissoc-in, patch-object,
without-obj, update-vals, update-in-when, update-when, assoc-in-when,
assoc-when, merge, txt-merge, mapm, removev, filterm, removem,
map-perm, distinct-xf and deep-mapm.

*  Add tests for parsing, numeric and utility helpers

Adds tests for nan?, safe+, max, min, parse-integer, parse-double,
parse-uuid, coalesce-str, coalesce, read-string, name, prefix-keyword,
kebab-keys, regexp?, nilf, nilv, any-key?, tap, tap-r, map-diff,
unique-name, toggle-selection, invert-map, obfuscate-string,
unstable-sort, opacity-to-hex, format-precision, format-number
and append-class.

*  Add tests for remaining untested helpers in data ns

Cover percent?, parse-percent, num-string?, num?, not-empty?,
editable-collection?, oreorder-before, oassoc-in-before,
lazy-map and reorder.

Platform-specific assertions use reader conditionals where
CLJS and JVM behaviour differ (js/isFinite string coercion,
js/isNaN empty-string coercion).
2026-03-24 19:52:52 +01:00
Andrey Antukh 7461c5304c Add comprehensive tests for app.common.colors ns (#8758)
Cover all public functions: valid-hex-color?, parse-rgb,
valid-rgb-color?, rgb->str, hex->rgb, rgb->hex, rgb->hsv,
hsv->rgb, rgb->hsl, hsl->rgb, hex->hsl, hex->hsv, hex->rgba,
hex->hsla, hex->lum, hsl->hex, hsl->hsv, hsv->hex, hsv->hsl,
format-hsla, format-rgba, expand-hex, prepend-hash, remove-hash,
color-string?, parse, next-rgb, reduce-range, interpolate-color,
uniform-spread, uniform-spread? and interpolate-gradient.

Tests pass on both JVM and JS (ClojureScript) platforms.
Platform differences (NaN saturation for achromatic colors,
integer vs float return types) are handled with mth/close?.
2026-03-24 19:10:44 +01:00
Andrey Antukh 0f19bc02d7 📎 Add testing engineer agent (opencode) 2026-03-24 18:49:30 +01:00
Andrey Antukh 53f4c6fede Merge remote-tracking branch 'origin/main' into staging 2026-03-24 18:19:09 +01:00
Andrey Antukh edfa437ce7 📚 Improve CONTRIBUTING.md file 2026-03-24 18:18:38 +01:00
Andrey Antukh d4bc1d37f2 Merge remote-tracking branch 'origin/staging' into develop 2026-03-24 18:08:23 +01:00
Andrey Antukh 8928e274fc Merge remote-tracking branch 'origin/main' into staging 2026-03-24 18:01:38 +01:00
Andrey Antukh cc03f3f884 📚 Add minor improvements to ai agents documentation 2026-03-24 18:00:39 +01:00
alonso.torres b6e300a6c7 🐛 Fix plugins addToken schema validation 2026-03-24 16:27:59 +01:00
Belén Albeza 44689d3f9c 🐛 Fix internal error on invalid max-h/max-w values (wasm) 2026-03-24 16:02:40 +01:00
Dominik Jain ccaeb49354 📚 Add instructions on MCP usage via npx #8535 2026-03-24 15:57:04 +01:00
Dominik Jain 38f2ec1339 📎 Update Serena project file 2026-03-24 15:57:04 +01:00
Dominik Jain 7b5699b59f Improve instructions on Text elements 2026-03-24 15:57:04 +01:00
Dominik Jain 1f7afcebe3 Apply throwValidationErrors flag during MCP code executions #8682 2026-03-24 15:57:04 +01:00
Andrey Antukh 750e8a9d51 🐛 Fix dissoc error when detaching stroke color from library (#8738)
* 🐛 Fix dissoc error when detaching stroke color from library

The detach-value function in color-row was only passing index to
on-detach, but the stroke's on-color-detach handler expects both
index and color arguments. This caused a protocol error when trying
to dissoc from a number instead of a map.

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

* 🐛 Fix crash when detaching color asset from stroke

The color_row detach-value callback calls on-detach with (index, color),
but stroke_row's local on-color-detach wrapper only took a single argument
(fn [color] ...), so it received index as color and passed it to
stroke.cljs which then called (dissoc index :ref-id :ref-file), crashing
with 'No protocol method IMap.-dissoc defined for type number'.

Fix the wrapper to accept (fn [_ color] ...) so it correctly ignores the
index passed by color_row (it already has index in the closure) and
forwards the actual color map to the parent handler.

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 15:35:32 +01:00
Alejandro Alonso f88e287357 Merge pull request #8726 from penpot/niwinz-main-bugfix-1
🐛 Fix null text crash on paste in text editor
2026-03-24 15:19:41 +01:00
Andrey Antukh 56f1fcdb53 🐛 Fix crash when pasting image into text editor
When pasting an image (with no text content) into the text editor,
Draft.js calls handlePastedText with null/empty text. The previous fix
guarded splitTextIntoTextBlocks against null, but insertText still
attempted to build a fragment from an empty block array, causing
Modifier.replaceWithFragment to crash with 'Cannot read properties of
undefined (reading getLength)'.

Fix insertText to return the original state unchanged when there are no
text blocks to insert. Also guard handle-pasted-text in the ClojureScript
editor to skip the insert-text call entirely when text is nil or empty.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 13:00:28 +00:00
Andrey Antukh d863c7065f 🐛 Fix null text crash on paste in text editor
The splitTextIntoTextBlocks function in @penpot/draft-js called
.split() on the text parameter without a null check. When pasting
content without text data (e.g., images only), Draft.js passes null
to handlePastedText, causing a TypeError.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 13:49:28 +01:00
Alonso Torres 1539c074b4 🐛 Fix problem with margins in grid (#8748) 2026-03-24 13:48:24 +01:00
Alejandro Alonso ca427bcd4e Merge pull request #8728 from penpot/niwinz-staging-dom-remove-child-issue
🐛 Fix removeChild crash on all portal components
2026-03-24 13:16:32 +01:00
Andrey Antukh 8729fed724 📎 Add opencode and copilot deps on root package.json 2026-03-24 12:52:56 +01:00
Alejandro Alonso 5d6eb3b3d6 Merge pull request #8739 from penpot/niwinz-main-bugfix-5
🐛 Fix error when get-parent-with-data encounters non-Element nodes
2026-03-24 12:50:48 +01:00
Alejandro Alonso 3abd63c35a Merge pull request #8740 from penpot/niwinz-main-bugfix-7
🐛 Ensure path content is always PathData when saving
2026-03-24 12:44:51 +01:00
Aitor Moreno c3a0189af2 Merge pull request #8746 from penpot/superalex-fix-backspace-breaks-ctrl-z
🐛 Fix backspace breaks ctrl z
2026-03-24 12:27:19 +01:00
Luis de Dios 5f722d9183 🐛 Fix show red bullet in workspace menu if mcp key is expired (#8727) 2026-03-24 12:27:09 +01:00
Elena Torro 5a73003c7f 🐛 Fix fallback fonts and symbols 2 2026-03-24 12:06:28 +01:00
Eva Marco ccd28140bc 📎 Update changelog (#8744) 2026-03-24 12:03:56 +01:00
Alejandro Alonso 2ceb2c8d95 Merge pull request #8745 from penpot/azazeln28-fix-text-selection-misalignment
🐛 Fix text selection misalignment
2026-03-24 11:43:44 +01:00
Alejandro Alonso bd37096637 Merge pull request #8725 from penpot/elenatorro-13774-fix-missing-whitespace
🐛 Fix text transform on different spans
2026-03-24 11:41:36 +01:00
Alejandro Alonso 0c6736e676 Merge pull request #8737 from penpot/alotor-export-wasm
🐛 Fix problem with multiple export
2026-03-24 11:36:12 +01:00
alonso.torres 937032c790 Allow for reconnections to MCP server 2026-03-24 11:32:47 +01:00
Eva Marco dd6a3c291a 🐛 Fix tooltip shown on tab change (#8719) 2026-03-24 11:22:52 +01:00
Alejandro Alonso 55d763736f 🐛 Fix backspace breaks ctrl+z 2026-03-24 11:19:02 +01:00
Aitor Moreno c920c092cc 🐛 Fix text selection misalignment 2026-03-24 11:06:31 +01:00
Marina López be437fbfa1 💄 Fix styles from select organization 2026-03-24 10:22:09 +01:00
Alejandro Alonso 51fa5a5773 Merge remote-tracking branch 'origin/staging' into develop 2026-03-24 10:18:51 +01:00
Andrey Antukh 13b5c96a42 📎 Update changelog 2026-03-24 09:19:58 +01:00
Alejandro Alonso efd3efff00 🐛 Fix text-align before typing and sync attrs with v2 editor 2026-03-24 08:57:10 +01:00
Andrey Antukh d051a3ba45 🐛 Ensure path content is always PathData when saving
The save-path-content function only converted content to PathData when
there was a trailing :move-to command. When there was no trailing
:move-to, the content from get-path was stored as-is, which could be
a plain vector if the shape was already a :path type with non-PathData
content. This caused segment/get-points to fail with 'can't access
property "get", cache is undefined' when the with-cache macro tried
to access the cache field on a non-PathData object.

The fix ensures content is always converted to PathData via path/content
before being stored in the state.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-24 08:15:58 +01:00
Andrey Antukh 577f00dd24 🐛 Fix error when get-parent-with-data encounters non-Element nodes
The get-parent-with-data function traverses the DOM using parentElement
to find an ancestor with a specific data-* attribute. When the current
node is a non-Element DOM node (e.g. Document node reached from event
handlers on window), accessing .-dataset returns undefined, causing
obj/in? to throw "right-hand side of 'in' should be an object".

This adds a nodeType check to skip non-Element nodes during traversal
and continue up the parent chain.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-23 19:19:27 +00:00
Marina López 65ea27cbac 💄 Fix styles between grid layout inputs (#8673) 2026-03-23 20:05:13 +01:00
Andrey Antukh b484415a9f 🐛 Fix generic error shown on clipboard permission denial (#8666)
When the browser denies clipboard read permission (NotAllowedError),
the unhandled exception handler was showing a generic 'Something wrong
has happened' toast. This change adds proper error handling for
clipboard permission errors in paste operations and shows a
user-friendly warning message instead.

Changes:
- Add error handling in paste-from-clipboard for NotAllowedError
- Improve error handling in paste-selected-props to detect permission errors
- Mark clipboard NotAllowedError as ignorable in the uncaught error handler
  to prevent duplicate generic error toasts
- Add translation key for clipboard permission denied message

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-23 20:03:14 +01:00
alonso.torres 43be994920 🐛 Fix problem with multiple export 2026-03-23 19:51:20 +01:00
Andrey Antukh 1442e4c246 📎 Update changelog 2026-03-23 19:16:48 +01:00
Renzo 852f9ce07f 🎉 Add drag-to-change for numeric inputs (#8536)
Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
2026-03-23 19:01:32 +01:00
Elena Torró ee1c96f3a1 Merge pull request #8733 from penpot/ladybenko-13803-fix-grid-lines
🐛 Fix layout lines not disappearing on shape deletion (wasm)
2026-03-23 17:55:37 +01:00
Belén Albeza ce0553951f 🐛 Fix layout lines not disappearing on shape deletion (wasm) 2026-03-23 17:21:28 +01:00
Elena Torró 7afcd46e5c Merge pull request #8729 from penpot/ladybenko-13773-fix-exlusion
🐛 Fix exclusion being applied as union (wasm)
2026-03-23 16:31:52 +01:00
Elena Torro 84ac86af5b 🐛 Fix whitespace parsing and word capitalization 2026-03-23 16:30:23 +01:00
Eva Marco 7adac6df40 🐛 Fix review comments (#8708)
* 🐛 Fix focus option only on arrowdown not at open

* 🐛 Fix focus on input when visible focus should be on options

* ♻️ Improve nativation, adding tab control and moving throught options is now cyclic

*  Add selected option when inside cursor is inside option

* 🐛 Dropdown is positioned nex to the input alwais
2026-03-23 16:06:23 +01:00
Elena Torro 57be1428b3 🐛 Fix background-blur on wasm export 2026-03-23 15:44:36 +01:00
alonso.torres 13ee27b1ad 🐛 Fix problem with plugins export 2026-03-23 15:40:15 +01:00
Andrey Antukh 2905905a9f ♻️ Extract use-portal-container hook to reduce duplication
The dedicated-container portal pattern was repeated across 7 components.
Extract it into a reusable use-portal-container hook under app.main.ui.hooks.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-23 13:47:33 +00:00
Belén Albeza 405fd49d79 🐛 Fix exclusion being applied as union (wasm) 2026-03-23 14:21:41 +01:00
Andrey Antukh ff60503ce6 🐛 Fix removeChild crash on all portal components
The previous fix (80b64c440c) only addressed portal-on-document* but
there were 6 additional components that portaled directly to
document.body, causing the same race condition when React attempted
to remove a node that had already been detached during concurrent
state updates (e.g. navigating away while a context menu is open).

Apply the dedicated-container pattern consistently to all portal
sites: modal, context menus, combobox dropdown, theme selector, and
tooltip. Each component now creates a dedicated <div> container
appended to body on mount and removed on cleanup, giving React an
exclusive containerInfo for each portal instance.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-23 14:19:57 +01:00
Pablo Alba 11ed09f431 🐛 Fix link to nitrate create org 2026-03-23 12:32:50 +01:00
Andrey Antukh 43cdb91063 ♻️ Recycle frontend tests with wasm mocks (#8681) 2026-03-23 12:11:27 +01:00
Eva Marco 4345cfaec7 🎉 Add natural sort on token names (#8672) 2026-03-23 11:24:59 +01:00
Roland bfb331d230 🐛 Fix pluings API theme.addSet() crash caused by async state race in token-set proxy (#8700)
When `catalog.addSet()` creates a new token set, `st/emit!` is async —
the set is not yet in `@st/state` when the returned proxy is used.
Calling `theme.addSet(proxy)` immediately after reads `.name` from the
proxy, which calls `locate-token-set` on stale state → returns nil →
`enable-set` conjs nil into the theme's `:sets` → backend rejects with
400 (`:sets #{nil}`) → workspace reloads → plugin disconnects.

Fix: store `initial-name` in the proxy at construction time as a
fallback for the `:name` getter during the async propagation window.
Also add nil guards in `addSet`/`removeSet` as defense-in-depth.

Closes #8698

Signed-off-by: rodo <roland@dolltons.com>
2026-03-23 11:24:29 +01:00
Juan de la Cruz 884cdbbf8d Add new MCP plugin UI changes (#8699)
*  Add new MCP plugin UI changes

* 📎 Fix tool status misleading
2026-03-23 11:20:37 +01:00
Eva Marco 72fd637ec2 ♻️ Refactor small numeric inputs (#8660)
* ♻️ Refactor individual border radius inputs

* ♻️ Refactor layer opacity input

* ♻️ Refactor stroke width inputs and add icon only selects

* ♻️ Fix comments on PR
2026-03-23 11:00:29 +01:00
Andrey Antukh dc56da9662 Merge remote-tracking branch 'origin/staging' into develop 2026-03-23 10:15:30 +01:00
Abhishek Mittal 094ef3d6fe Add 'page' shapeId to MCP export_shape for full-page snapshot (#8693)
Add support for 'page' as a special shapeId value in the MCP export_shape
tool. It resolves to penpot.root, exporting the entire current page as a
PNG or SVG snapshot.

Previously only 'selection' and explicit shape IDs were supported. The new
'page' shortcut is useful for AI agents needing a bird's-eye view of the
design without having to know a specific shape ID.

Closes https://github.com/penpot/penpot/issues/8689

Signed-off-by: Abhishek Mittal <abhishekmittaloffice@gmail.com>
2026-03-23 10:03:32 +01:00
Pablo Alba 8406b5e9f8 Add nitrate api for notify org deletion (#8697) 2026-03-23 09:59:57 +01:00
Andres Gonzalez 9e4f4d5f7b 🐛 Remove wrong lines from staging changelog 2026-03-23 09:11:22 +01:00
Andres Gonzalez b637f0a917 🐛 Remove wrong lines from changelog 2026-03-23 09:11:09 +01:00
andrés gonzález 35125dfd79 Update changelog (#8703) 2026-03-23 08:48:22 +01:00
Alejandro Alonso 52496243ac Merge remote-tracking branch 'origin/staging' into develop 2026-03-20 17:00:47 +01:00
Alejandro Alonso 0c3b5895bf 🐛 Restore correct branches in finalize-editor-state for text 2026-03-20 17:00:34 +01:00
Alejandro Alonso c6f3aa4f66 Merge pull request #8710 from penpot/superalex-fix-text-finalize-classic-editor
🐛 Restore correct branches in finalize-editor-state for text
2026-03-20 16:59:53 +01:00
Alejandro Alonso 62b36f0153 🐛 Restore correct branches in finalize-editor-state for text 2026-03-20 16:48:37 +01:00
Juanfran e53ff6d20b Open create org modal in Nitrate 2026-03-20 16:19:29 +01:00
Alejandro Alonso 02afd805ca Merge remote-tracking branch 'origin/staging' into develop 2026-03-20 16:00:24 +01:00
María Valderrama 9c3fbc59b9 🐛 Fix visibility of go to nitrate cc option 2026-03-20 13:42:45 +01:00
Alejandro Alonso dd10be1fb4 Merge pull request #8611 from penpot/alotor-export-wasm
 Add support for export with wasm engine
2026-03-20 11:59:03 +01:00
Alejandro Alonso f068842a6c Merge remote-tracking branch 'origin/staging' into develop 2026-03-20 10:20:43 +01:00
Eva Marco 71b32b97f0 🔧 Activate flag on dev enviroment (#8706) 2026-03-20 10:13:05 +01:00
Aitor Moreno d8b1bd53f3 Merge pull request #8705 from penpot/superalex-fix-wasm-text-editor-finalize-nil
🐛 Coerce finalize? in WASM text updates for valid undo flags
2026-03-20 10:07:07 +01:00
alonso.torres 7a8824b826 Add support for export with wasm engine 2026-03-20 09:46:19 +01:00
Alejandro Alonso 1126ed37f1 🐛 Coerce finalize? in WASM text updates for valid undo flags 2026-03-20 09:43:00 +01:00
Aitor Moreno 0df6b30f79 Merge pull request #8704 from penpot/superalex-fix-text-disappearing
🐛 Fix WASM text auto-width geometry on finalize
2026-03-20 09:36:58 +01:00
Alejandro Alonso 353d8677b0 🐛 Fix WASM text auto-width geometry on finalize 2026-03-20 09:28:28 +01:00
Aitor Moreno d8f4d38ac2 Merge pull request #8701 from penpot/superalex-fix-line-breaks-not-rendering-in-text-shapes
🐛 Fix line breaks not rendering in text shapes
2026-03-20 09:17:10 +01:00
Eva Marco fb5ac5cd8b 🐛 Add box shadow to token dropdowns (#8685) 2026-03-20 09:02:27 +01:00
Aitor Moreno 58d959a37e Merge pull request #8684 from penpot/superalex-fix-embedded-editor-pasting-text-2
🐛 Fix embedded editor pasting text
2026-03-20 06:52:39 +01:00
Xaviju ee1dd80b6e Copy token name from contextual menu (#8566) 2026-03-19 23:22:44 +01:00
Xaviju 8ad62c6800 🐛 Add export menu to inspect styles tab (#8645)
* 🐛 Add export menu to inspect styles tab

* 📎 Add to CHANGES
2026-03-19 23:20:18 +01:00
Xaviju f8913c755d 🎉 Rename token group (#8275)
* 🎉 Rename token group

* 📎 Add to CHANGES
2026-03-19 22:54:21 +01:00
Alejandro Alonso e8ce2a43f2 🐛 Fix line breaks not rendering in text shapes 2026-03-19 17:45:58 +01:00
Eva Marco 8e7e6ffc2f ♻️ Design review for numeric inputs (#8630)
* ♻️ Update tooltip position on icon buttons

* ♻️ Sort token groups by priority not alphabetically

* ♻️ Add proper padding on text-icon-inputs

* ♻️ Hide detach button when dropdown is open

* 🐛 Fix detach stroke width

* 🐛 Fix strokes applied on all rows

* 🐛 Fix nillable inputs

* 🐛 Fix comments on PR
2026-03-19 16:46:18 +01:00
Luis de Dios e870497ae1 📎 PR changes 2026-03-19 16:39:29 +01:00
Luis de Dios 9e9c28fe3c 🐛 Fix MCP notifications when there is only one tab 2026-03-19 16:39:29 +01:00
alonso.torres 93de83c427 🐛 Fix problem with error message 2026-03-19 16:19:27 +01:00
alonso.torres 3270d65491 🐛 Fix problem with token retrieval 2026-03-19 16:19:27 +01:00
alonso.torres a1a469449e Add throwValidationErrors flag for plugins 2026-03-19 15:37:08 +01:00
Alejandro Alonso 0499cd6162 Merge pull request #8654 from penpot/elenatorro-13282-perf-tiles
🔧 Preserve cache canvas during tile rebuild for smooth zoom preview
2026-03-19 15:20:08 +01:00
Alejandro Alonso 64b5fd7fb9 Merge pull request #8674 from penpot/ladybenko-13720-flag-wasm-debug-info
🔧 Show / Hide wasm info label via config flag
2026-03-19 14:45:37 +01:00
Eva Marco 4abaae4f80 🐛 Fix open tooltip on tab change (#8680) 2026-03-19 13:41:33 +01:00
Elena Torro de04896266 🔧 Preserve cache canvas during tile rebuild for smooth zoom preview 2026-03-19 12:30:10 +01:00
Alejandro Alonso d59aa03924 Merge pull request #8593 from penpot/azazeln28-feat-text-editor-composition-update
🎉 Feat add text editor composition update
2026-03-19 12:27:26 +01:00
Alejandro Alonso a28d47f437 🐛 Fix embedded editor pasting text 2026-03-19 12:10:46 +01:00
Elena Torró 2adf79a5eb Merge pull request #8615 from penpot/ladybenko-13626-more-recoverable-errors
 Use new error types in other parts of the rust codebase
2026-03-18 18:13:22 +01:00
Elena Torro e630be1509 🎉 Add background blur for wasm render 2026-03-18 18:05:30 +01:00
Elena Torro 5ba53f7296 🎉 Add background blur for wasm render 2026-03-18 17:43:27 +01:00
BitTobyandbittoby b876417d5b Add copy and paste for grid layout rows and columns via co… (#8498)
*  Add copy and paste for grid layout rows and columns via context menu

* 🔧 Use grid-id instead of grid in context menu deps

---------

Co-authored-by: bittoby <bittoby@users.noreply.github.com>
2026-03-18 16:19:15 +01:00
Elena Torró 81d90be4c9 🐛 Fix rasterizer initialization to only run when render-wasm/v1 is active (#8669) 2026-03-18 16:04:04 +01:00
Alonso Torres a4ad940177 Add version property to plugins API (#8676) 2026-03-18 16:03:17 +01:00
Pablo Alba 2a09f30199 Add nitrate endpoint to delete teams keeping your-penpot projects 2026-03-18 15:59:38 +01:00
Dominik Jain 1b91bbe64d Update MCP server to account for API updates
Update instructions and API documentation to account for
* updated token property names; resolves #8512
* improved variant container creation; resolves #8564
2026-03-18 15:30:04 +01:00
Andrés Moya 8e2a52af50 💄 Change function names 2026-03-18 15:30:04 +01:00
alonso.torres 4e1b940e04 Remap token properties for usability 2026-03-18 15:30:04 +01:00
Andrey Antukh ca72dcdcbb Merge remote-tracking branch 'origin/staging' into develop 2026-03-18 15:00:40 +01:00
Andrey Antukh 46c2d41218 Merge remote-tracking branch 'origin/main' into staging 2026-03-18 15:00:11 +01:00
Andrey Antukh 2d616cf9c0 📚 Add better organization for AGENTS.md file (#8675) 2026-03-18 14:59:38 +01:00
Aitor Moreno 72f5ecfe56 🎉 Feat add text editor composition update 2026-03-18 14:41:54 +01:00
Elena Torró 10359d39df Merge pull request #8659 from penpot/superlalex-fix-test-halos-big-shadows
🐛 Fix visible halos in big shadows
2026-03-18 13:38:25 +01:00
Belén Albeza 66ba097ba2 🐛 Fix not being able to enable wasm text editor via config flag 2026-03-18 13:27:05 +01:00
Belén Albeza 619842152d ♻️ Refactor render options (wasm) 2026-03-18 13:16:13 +01:00
Eva Marco df8194acf5 🐛 Fix several bugs (#8604)
* 🐛 Fix console warning

* ♻️ Use DS buttons and remove deprecated CSS

* 🐛 Fix copy on update library message

* 🐛 Fix id prop on switch component

* 🐛 Fix tooltip shown after tab change
2026-03-18 12:52:58 +01:00
Belén Albeza 0597eef750 Show/hide wasm info label via config flag 2026-03-18 12:44:10 +01:00
Elena Torro d2422e3a21 Add background blur type support to common schema 2026-03-18 11:29:27 +01:00
Alejandro Alonso 0484d23b12 🐛 Fix clipped rounded corners artifacts 2026-03-18 11:04:02 +01:00
Pablo Alba 04a3e236fe Add a callback-url parameter to login (#8655) 2026-03-18 10:15:31 +01:00
Andrey Antukh 0d2ec687d2 🐛 Fix unexpected corner case between SES hardening and transit (#8663)
* Revert "🐛 Fix plugin sandbox freezing CLJS Proxy constructor breaking Transit encoding"

This reverts commit 27a934dcfd.

* 🐛 Fix unexpected corner case between SES hardening and transit

The cause of the issue is a race condition between plugin loading
and the first time js/Date objects are encoded using transit. Transit
encoder populates the prototype of the Date object the first time a
Date instance is encoded, but if SES freezes the Date prototype before
transit, an strange exception will be raised on encoding any object
that contains Date instances.

Example of the exception:

Cannot define property transit$guid$4a57baf3-8824-4930-915a-fa905479a036,
object is not extensible
2026-03-18 09:54:54 +01:00
Andrey Antukh 5482ee211e 🐛 Fix unexpected corner case between SES hardening and transit (#8663)
* Revert "🐛 Fix plugin sandbox freezing CLJS Proxy constructor breaking Transit encoding"

This reverts commit 27a934dcfd.

* 🐛 Fix unexpected corner case between SES hardening and transit

The cause of the issue is a race condition between plugin loading
and the first time js/Date objects are encoded using transit. Transit
encoder populates the prototype of the Date object the first time a
Date instance is encoded, but if SES freezes the Date prototype before
transit, an strange exception will be raised on encoding any object
that contains Date instances.

Example of the exception:

Cannot define property transit$guid$4a57baf3-8824-4930-915a-fa905479a036,
object is not extensible
2026-03-18 09:53:22 +01:00
Dr. Dominik Jain 757fb8e21d Reduce instructions transferred at MCP connection to a minimum (#8649)
*  Reduce instructions transferred at MCP connection to a minimum

Force on-demand loading of the 'Penpot High-Level Overview',
which was previously transferred in the MCP server's instructions.

This greatly reduces the number of tokens for users who will
not actually interact with Penpot, allowing the MCP server to
remain enabled for such users without wasting too many tokens.

Resolves #8647

* 📎 Update Serena project
2026-03-17 18:50:07 +01:00
Dr. Dominik Jain 0f24cf26f6 Reduce instructions transferred at MCP connection to a minimum (#8649)
*  Reduce instructions transferred at MCP connection to a minimum

Force on-demand loading of the 'Penpot High-Level Overview',
which was previously transferred in the MCP server's instructions.

This greatly reduces the number of tokens for users who will
not actually interact with Penpot, allowing the MCP server to
remain enabled for such users without wasting too many tokens.

Resolves #8647

* 📎 Update Serena project
2026-03-17 18:48:06 +01:00
Andrey Antukh 1a59017e1c 🐛 Ignore posthog exceptions in unhandled exception handler (#8629)
PostHog recorder throws errors like 'Cannot assign to read only property
'assert' of object' which are unrelated to the application and should be
ignored to prevent noise in error reporting.
2026-03-17 18:41:06 +01:00
Andrey Antukh 4da332a5e2 Merge remote-tracking branch 'origin/staging' into develop 2026-03-17 18:29:08 +01:00
Andrey Antukh de03f3883b Merge remote-tracking branch 'origin/main' into staging 2026-03-17 18:28:39 +01:00
Pablo Alba 5eecd52743 Add get-teams-summary to nitrate api (#8662) 2026-03-17 18:25:18 +01:00
Elena Torró bf872fa766 Merge pull request #8665 from penpot/ladybenko-show-text-editor-info
🔧 Show label if wasm text editor is enabled
2026-03-17 17:12:18 +01:00
Belén Albeza c8b3407acd 🔧 Show label if wasm text editor is enabled 2026-03-17 16:47:05 +01:00
Andrey Antukh 802cec1ee4 Revert several changes to mcp scripts introduced in previous commits 2026-03-17 15:31:17 +01:00
Andrey Antukh 3c92c98c94 Revert several changes to mcp scripts introduced in previous commits 2026-03-17 15:30:26 +01:00
Andrey Antukh 6079ef4e22 Make mcp plugin always ready to be in multiuser 2026-03-17 15:18:22 +01:00
girafic d6cc469027 🐛 Fix permission message and update ruler guide proxy name on plugins api (#8632)
- Updated the error message for missing content write permission in the removeRulerGuide function.
- Renamed the ruler guide proxy from "RuleGuideProxy" to "RulerGuideProxy" for consistency.
- Adjusted variable naming in the addRulerGuide function for clarity.

Signed-off-by: Stas Haas <stas@girafic.de>
2026-03-17 15:05:26 +01:00
Andrey Antukh ab4e195cca Add protection for stale cache of js assets loading issues (#8638)
*  Use update-when for update dashboard state

This make updates more consistent and reduces possible eventual
consistency issues in out of order events execution.

* 🐛 Detect stale JS modules at boot and force reload

When the browser serves cached JS files from a previous deployment
alongside a fresh index.html, code-split modules reference keyword
constants that do not exist in the stale shared.js, causing TypeError
crashes.

This adds a compile-time version tag (via goog-define / closure-defines)
that is baked into the JS bundle. At boot, it is compared against the
runtime version tag from index.html (which is always fresh due to
no-cache headers). If they differ, the app forces a hard page reload
before initializing, ensuring all JS modules come from the same build.

* 📎 Ensure consistent version across builds on github e2e test workflow

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-17 15:04:06 +01:00
Andrey Antukh 7480be0bda 🐛 Fix mcp bundle build issue introduced in previous commits 2026-03-17 14:51:51 +01:00
Andrey Antukh b86898eaf9 Revert "🐛 Fix "Cannot assign to read only property toString" error in plugins runtime"
This reverts commit f796f7ccb9.
2026-03-17 14:45:18 +01:00
Andrey Antukh e018253c6b Make mcp plugin always ready to be in multiuser 2026-03-17 14:45:18 +01:00
Elena Torró c6f8356847 📚 Improve docs on feature flags (#8641) 2026-03-17 11:49:14 +01:00
David Barragán Merino 1b223359d9 🔧 Remove staging-render bundle github workflow 2026-03-17 11:18:51 +01:00
Andrey Antukh 0535ef0e39 Remove duplicated code for browser detection 2026-03-17 10:58:43 +01:00
Andrey Antukh 2d5392327e 🐛 Add minor improvements on wasm-render error handling 2026-03-17 10:58:43 +01:00
Andrey Antukh 0d236110e9 🐛 Fix ts/asap helper on frontend utils 2026-03-17 10:58:43 +01:00
Andrey Antukh 997f0c0e40 Build render-wasm on runing pnpm run test on frontend 2026-03-17 10:58:43 +01:00
Alejandro Alonso c27449e4f0 🐛 Fix visible halos in big shadows 2026-03-17 10:54:39 +01:00
Andrey Antukh 2276456295 Add minor compatibility adjustments for audit archive task (#8491) 2026-03-17 10:39:26 +01:00
Luis de Dios a5f09e18a8 🎉 Make the mcp plugin switching between tabs work correctly (#8597)
*  Make the MCP plugin switching between tabs work correctly

* 🎉 Show notification when the plugin is loaded in another tab

* 📎 PR changes

*  Add events
2026-03-17 10:17:02 +01:00
Andrey Antukh f796f7ccb9 🐛 Fix "Cannot assign to read only property toString" error in plugins runtime
The error "Cannot assign to read only property 'toString' of function"
occurs during React's commit phase after a plugin is loaded. The root
cause is an initialization ordering issue in the SES (Secure EcmaScript)
lockdown sequence.

When loadPlugin() is called, ses.harden(context) runs first, which
transitively freezes everything reachable from the context object —
including Function.prototype and Object.prototype — via prototype chain
traversal of getter functions. Later, createSandbox() calls
ses.hardenIntrinsics(), which attempts to run enablePropertyOverrides()
to convert frozen data properties (like Function.prototype.toString)
into accessor pairs that work around JavaScript's "override mistake".
However, enablePropertyOverrides checks "if (configurable)" before
converting, and since Function.prototype is already frozen (all
properties have configurable: false), the override taming is silently
skipped. This leaves Function.prototype.toString as a frozen
non-writable data property, causing any subsequent code that assigns
.toString to a function instance in strict mode to throw a TypeError.

The fix calls ses.hardenIntrinsics() before ses.harden(context) in
loadPlugin(), ensuring override taming installs the accessor pairs on
prototype properties before they get frozen. The existing
hardenIntrinsics() call in createSandbox() becomes a harmless no-op
thanks to the idempotency guard.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-17 10:14:12 +01:00
Andrey Antukh e730e9ee64 🐛 Fix subscribe to undefined stream error in use-stream hook (#8633)
Add a nil guard before subscribing to the stream in the use-stream
hook. When a nil/undefined stream is passed (e.g., from a conditional
expression or timing edge case during React rendering), the subscribe
call on undefined causes a TypeError. The guard ensures we only
subscribe when the stream is defined.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-17 10:06:16 +01:00
Andrey Antukh 27a934dcfd 🐛 Fix plugin sandbox freezing CLJS Proxy constructor breaking Transit encoding
When the plugin sandbox calls harden() (SES lockdown) on any proxy object
returned from the penpot.* API, SES traverses the prototype chain up to
Proxy.prototype and freezes the CLJS Proxy constructor function. Transit's
typeTag helper later fails with "object is not extensible" when trying to
set its cache property on that frozen constructor.

Fix by deleting the constructor data property from Proxy.prototype so that
harden never traverses to the CLJS Proxy constructor function.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-17 10:01:45 +01:00
Pablo Alba acc383ba31 Improve nitrate module JSON handling and error management 2026-03-17 09:59:02 +01:00
Andrey Antukh 0779c9ca61 🐛 Fix TypeError in get-points when content is not PathData (#8634)
The with-cache macro in impl.cljc assumed the target was always a
PathData instance (which has a cache field). When content was a plain
vector, (.-cache content) returned undefined in JS, causing:

  TypeError: Cannot read properties of undefined (reading 'get')

Fix:
- path/get-points (app.common.types.path) is now the canonical safe
  entry point: converts non-PathData content via impl/path-data and
  handles nil safely before delegating to segment/get-points
- segment/get-points remains a low-level function that expects a
  PathData instance (no defensive logic at that level)
- streams.cljs: replace direct call to path.segm/get-points with
  path/get-points so the safe conversion path is always used
- with-cache macro: guards against nil/undefined cache, falling back
  to direct evaluation for non-PathData targets

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-17 09:31:10 +01:00
andrés gonzález efd6d19a12 📚 Remove link to sales form from the Help Center (#8643) 2026-03-16 16:58:08 +01:00
Andrey Antukh 46f50aab16 Merge remote-tracking branch 'origin/staging' into develop 2026-03-16 16:13:16 +01:00
Andrey Antukh 3bf145a749 Disable wasm-render on frontend tests (temporarily) 2026-03-16 16:12:53 +01:00
David Barragán Merino 31696de474 🔧 GitHub Actions worker tasks updated 2026-03-16 15:02:26 +01:00
Marina López 1b8871df8e Update image nitrate modal 2026-03-16 14:23:23 +01:00
Pablo Alba 8cb5c23a29 🐛 Fix nitrate url 2026-03-16 13:35:15 +01:00
Elena Torró ac69f28a0a Merge pull request #8646 from penpot/elenatorro-include-wasm-share-on-gitignore
🔧 Ignore render_wasm shared.js autogenerated file
2026-03-16 13:04:25 +01:00
Elena Torro ff1ba6b953 🔧 Ignore render_wasm shared.js autogenerated file 2026-03-16 12:09:54 +01:00
Dream ce04780b6c 🐛 Make collapsible sidebar titles clickable to toggle (#8547)
Fixes #5168
2026-03-16 11:03:49 +01:00
andrés gonzález 98e989d7f3 📚 Adjust MCP presence in changelog (#8642) 2026-03-16 10:51:10 +01:00
Dr. Dominik Jain 5e519c6b4b Account for changed interfaces of addToken and addSet (#8614)
Resolves #8613
2026-03-16 10:39:08 +01:00
Dr. Dominik Jain f566c1950f Account for changed interfaces of addToken and addSet (#8614)
Resolves #8613
2026-03-16 10:38:25 +01:00
Pablo Alba 8f35e451e6 Add notification for nitrate when creating a team inside an organization (#8639) 2026-03-16 10:36:32 +01:00
Andrey Antukh d763484554 📎 Enable render-wasm feature by default 2026-03-16 10:15:49 +01:00
Andrey Antukh 6e19548bac 📎 Update changelog 2026-03-16 09:38:23 +01:00
Andrey Antukh 4f08580ced 📎 Update changelog 2026-03-16 09:38:01 +01:00
Andrey Antukh c4333341b1 Merge remote-tracking branch 'origin/develop' into staging 2026-03-16 09:36:46 +01:00
Andrey Antukh 4c9775e182 Merge remote-tracking branch 'origin/staging-render' into develop 2026-03-16 09:35:12 +01:00
Andrey Antukh c7f63c4155 Merge remote-tracking branch 'origin/staging' into staging-render 2026-03-16 09:29:25 +01:00
Andrey Antukh 328b7739e0 📎 Prepare changes and flags for next release (#8624) 2026-03-13 13:07:24 +01:00
Andrey Antukh a528508751 📚 Update AGENTS.md 2026-03-13 12:57:22 +01:00
Elena Torró a68e06ffe9 Merge pull request #8587 from penpot/azazeln28-feat-word-boundary-cursor-navigation
🎉 Feat word boundary cursor navigation
2026-03-13 12:49:09 +01:00
alonso.torres 1ab1d4f6ca 🐛 Fix problem with snap pixel transforms 2026-03-13 12:47:34 +01:00
Aitor Moreno 39dcad8f54 Merge pull request #8623 from penpot/superalex-fix-embedded-editor-cursor-positioning
🐛 Fix embedded editor cursor positioning
2026-03-13 12:21:12 +01:00
Belén Albeza fc64dfe9d6 Revert " Add regression test for token highlight bug (13302) (#8573)"
This reverts commit d8249cc3db.
2026-03-13 12:12:15 +01:00
Elena Torro c4b4f8c63c 🔧 Keep shared.js file on CI 2026-03-13 12:08:55 +01:00
Alejandro Alonso fa5c853bca 🐛 Fix embedded editor cursor positioning 2026-03-13 12:06:45 +01:00
Andrey Antukh 6d30989a2d Merge remote-tracking branch 'origin/staging-render' into develop 2026-03-13 11:54:04 +01:00
Andrey Antukh 50ce8c4739 Merge remote-tracking branch 'origin/staging' into staging-render 2026-03-13 11:53:20 +01:00
Andrey Antukh cf94b56154 Merge remote-tracking branch 'origin/staging' into develop 2026-03-13 11:41:56 +01:00
Andrey Antukh 5d931d1614 Merge remote-tracking branch 'origin/main' into staging 2026-03-13 09:20:30 +01:00
Andrey Antukh 33c5f82c43 🐛 Fix penpot.openPage() to navigate in same tab by default
- Change the default for the newWindow param from true to false, so
  openPage() navigates in the same tab instead of opening a new one
- Accept a UUID string as the page argument in addition to a Page object,
  avoiding the need to call penpot.getPage(uuid) first
- Add validation error when an invalid page argument is passed

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-13 09:19:50 +01:00
Pablo Alba 08845ad2d4 Add organization selection for nitrate (#8619) 2026-03-13 09:16:12 +01:00
Alejandro Alonso fe9f1d63ad Merge pull request #8616 from penpot/alotor-fix-live-update-pvalues
🐛 Fix problem with update live sidebar values
2026-03-13 08:38:09 +01:00
Alejandro Alonso ebd17974a6 Merge pull request #8580 from penpot/niwinz-staging-fix-paste-issue
🐛 Fix crash when pasting non-map transit clipboard data
2026-03-13 08:33:19 +01:00
Andrey Antukh eecb51ecc1 🐛 Fix clipboard getType error when no allowed types found (#8609)
When clipboard items have types that don't match the allowed types
list, the filtering results in an empty array. Calling getType with
undefined throws a NotFoundError. This change adds a check for null/undefined
types and filters them from the result.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-13 08:24:36 +01:00
Andrey Antukh e7e6303184 🐛 Make ct/format-inst nil safe (#8612)
Prevent JS TypeError when date is nil in date formatting.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 19:55:51 +01:00
David Barragán Merino 3eabbffb0e 🔧 Change the assets dir to fix deployment with Wrangler 2026-03-12 19:50:46 +01:00
Andrey Antukh dbe8304f0c Include plugins on the frontend bundle 2026-03-12 19:07:44 +01:00
Andrey Antukh 87488f4a98 Make the rename-layers-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh f6259708ca Make the create-palette-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh 1229c2a5e5 Make the constrast-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh 4a6e6fce5b Make the lorem-ipsum-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh b8c319aa61 Make the poc-tokens-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh 2d0058ef3b Make the poc-state-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh d14e3a9914 Make the colors-to-tokens-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh eebe90b2cd Make the table-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh 9fb6a3ab0e Make the icons-plugin work correctly on subpath 2026-03-12 19:07:44 +01:00
Andrey Antukh 207bc795c0 Add minor changes on plugins examples-style 2026-03-12 19:07:44 +01:00
Andrey Antukh 4ccbc612cb Add the ability to accept plugin permission pressing enter 2026-03-12 19:07:44 +01:00
Andrey Antukh b56885b8be 💄 Add cosmetic changes to workspace plugins dialog components 2026-03-12 19:07:44 +01:00
Andrey Antukh a6e0113b25 Install plugin by pressing enter on plugins dialog 2026-03-12 19:07:44 +01:00
Andrey Antukh 24fc84054d Append manifest.json to plugin uri if it comes without it 2026-03-12 19:07:44 +01:00
Andrey Antukh e841dc60b7 Make penpot depend on local plugins runtime
This removes the need to publish versions to pnpn
2026-03-12 19:07:44 +01:00
Andrey Antukh 85ffadf8d7 Add better approach for handling plugin iframe url
Ensure params are passed correctly to plugins declared to be version
2 and are prepared to run in a subpath.
2026-03-12 19:07:44 +01:00
Andrey Antukh bb651e0c4e Update devenv nginx to serve locally builded plugins 2026-03-12 19:07:44 +01:00
alonso.torres 99151fe530 🐛 Fix problem with update live sidebar values 2026-03-12 17:30:57 +01:00
Andrey Antukh ec4f685aac 🐛 Fix penpot.openPage() to navigate in same tab by default
- Change the default for the newWindow param from true to false, so
  openPage() navigates in the same tab instead of opening a new one
- Accept a UUID string as the page argument in addition to a Page object,
  avoiding the need to call penpot.getPage(uuid) first
- Add validation error when an invalid page argument is passed

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 15:53:32 +01:00
Andrey Antukh 25df9f2f83 🐛 Fix DataCloneError in plugin postMessage communication
Fixes a crash where plugins sending messages via 'penpot.ui.sendMessage()'
could fail if their message payload contained non-serializable values like
functions or closures.

The fix adds validation using 'structuredClone()' to catch these messages
early with a helpful error message, and adds a defensive try/catch in the
modal's message handler as a safety net.

Fixes the error: 'Failed to execute postMessage on Window: ... could not
be cloned.'

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 15:26:00 +01:00
Andrey Antukh 8d5450391e 🐛 Fix crash when pasting non-map transit clipboard data
Guard against transit-decoded clipboard content that is not a map
before calling assoc, which caused a runtime crash ('No protocol
method IAssociative.-assoc defined for type number').

Also route :copied-props paste data to paste-transit-props instead
of incorrectly sending it to paste-transit-shapes.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 15:05:22 +01:00
Aitor Moreno c76985abee Merge pull request #8585 from penpot/superalex-fix-first-width-change-ignored-on-auto-width-text-in-wasm-render
🐛 Fix auto width text issues
2026-03-12 14:01:11 +01:00
Alejandro Alonso be9b1158ed Merge pull request #8588 from penpot/niwinz-staging-abort-signal-fix
🐛 Fix unhandled AbortError in HTTP fetch requests
2026-03-12 13:53:18 +01:00
Eva Marco 8f5c38d476 🐛 Fix scroll on colorpicker (#8595) 2026-03-12 13:36:38 +01:00
Andrey Antukh 80d165ed5b 🐛 Fix unhandled AbortError in HTTP fetch requests
Identify and silence "signal is aborted without reason" errors by:
- Providing an explicit reason to AbortController when subscriptions are disposed.
- Updating the global error handler to ignore AbortError exceptions.
- Ensuring unhandled rejections use the ignorable exception filter.

The root cause was RxJS disposal calling .abort() without a reason, combined
with the on-unhandled-rejection handler missing the ignorable error filter.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 13:31:58 +01:00
Alejandro Alonso 4b330e7b50 Merge pull request #8596 from penpot/niwinz-staging-fix-max-recursion
🐛 Fix RangeError (stack overflow) in find-component-main
2026-03-12 13:30:16 +01:00
Alejandro Alonso f5cabac5f3 Merge pull request #8583 from penpot/niwinz-staging-ignore-extensions-exceptions
🐛 Ignore browser extension errors in unhandled exception handler
2026-03-12 13:20:11 +01:00
Alejandro Alonso 1487386fbb Merge pull request #8582 from penpot/niwinz-staging-bugfix-path-plain-content
🐛 Fix plain vector leaking into shape :content from shape-to-path
2026-03-12 13:15:14 +01:00
Andrey Antukh b68e400cc1 🐛 Fix crash in select* when options vector is empty (#8578)
Guard get-option fallback with (when (seq options) ...) to avoid
"No item 0 in vector of length 0" when options is an empty vector.
Also guard the selected-option memo in select* to mirror the same
pattern already present in combobox*.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 13:06:25 +01:00
Alejandro Alonso 50c27aecc7 Merge pull request #8574 from penpot/niwinz-staging-unmount-fixes
🐛 Fix removeChild crash on portal-on-document* unmount
2026-03-12 13:06:00 +01:00
Andrey AntukhandAlonso Torres 37cf099126 🐛 Fix number token applying rotation when line-height attr is specified (#8557)
* 💄 Removed forgotten print (#8594)

* 🐛 Fix number token applying rotation when line-height attr is specified

toggle-token always used the on-update-shape from token-properties,
which for :number tokens is unconditionally update-rotation. So calling
applyToken(token, ["line-height"]) on a :number token would correctly
set the line-height text attribute but also invoke update-rotation with
the token value, silently rotating the shape.

Added an :on-update-shape-per-attr map to the :number token properties
entry mapping each valid attribute subset to its correct update function.
toggle-token now resolves the update function from that map when explicit
attrs are provided, falling back to the default on-update-shape otherwise.

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

* ♻️ Centralise attr->update-fn map and use it generically in toggle-token

The attributes->shape-update map was only defined in propagation.cljs.
Move it to application.cljs (where all the update functions live) and
have propagation.cljs reference it via dwta/attributes->shape-update,
eliminating the duplication.

Build a private flattened attr->shape-update map (one entry per
individual keyword) from that same source of truth. toggle-token now
uses it to resolve the correct on-update-shape when explicit attrs are
passed, instead of always taking the default from token-properties.
This fixes the :number token side-effect without any per-type special
casing: any token type whose explicit attrs map to a different update
function than the type default will now dispatch correctly.

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

*  Backport obj/reify changes from develop

*  Add missing error handler on shape proxy on plugins objects

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Alonso Torres <alonso.torres@kaleidos.net>
2026-03-12 12:51:26 +01:00
Aitor Moreno 5a2e926c6b 🎉 Add word boundary navigation 2026-03-12 12:50:26 +01:00
Alejandro Alonso c254f88367 Merge pull request #8575 from penpot/niwinz-staging-fetch-exception
🐛 Wrap fetch TypeError into proper ex-info with :unable-to-fetch code
2026-03-12 12:48:48 +01:00
Alejandro Alonso 8f7b12dfd8 Merge pull request #8569 from penpot/niwinz-staging-bugfix-2-not-iseqable-exception
🐛 Fix 'not ISeqable' error when entering float values in layout/opacity inputs
2026-03-12 12:37:35 +01:00
Andrey Antukh 82e3a5fa53 🐛 Fix 'not ISeqable' error when entering float values in layout/opacity inputs
Replace int? with number? in on-change handlers for layout item margins,
min/max sizes, and layer opacity. Using int? caused float values like 8.5
to fall into the design token branch, calling (first 8.5) and crashing.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 12:37:22 +01:00
alonso.torres 0e0029bd56 🐛 Fix MCP keep alive messages 2026-03-12 12:34:22 +01:00
Alejandro Alonso 1680be33ef Merge pull request #8568 from penpot/niwinz-staging-bugfix-1-path-get-points
🐛 Fix TypeError when path content is nil in get-points calls
2026-03-12 12:31:58 +01:00
Alejandro Alonso a079de1305 Merge pull request #8579 from penpot/elenatorro-13619-fix-outer-non-closing-stroke
🐛 Fix stroke closing on outer strokes on paths
2026-03-12 12:24:52 +01:00
Andrey Antukh 6ee8184821 🐛 Fix error when creating guides without frame (#8598)
* 🐛 Fix error when creating guides without frame

The error 'Cannot read properties of undefined (reading
$cljs$core$IFn$_invoke$arity$0$)' occurred when creating a new
guide. It is probably a race condition because it is not reproducible
from the user point of view.

The cause is mainly because of use incorrect jsx handler :& where :>
should be used. This caused that some props pased with incorrect casing
and the relevant callback props received as nil on the component and
on the use-guide hook.

The fix is simple: use correct jsx handler

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

* 💄 Add cosmetic changes to viewport guides components

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 12:23:09 +01:00
Alejandro Alonso 0c778d7278 🐛 Consolidate WASM text content update and resize into a single change 2026-03-12 12:21:06 +01:00
Andrey Antukh 86f2cbf45e Merge remote-tracking branch 'origin/staging' into develop 2026-03-12 12:19:54 +01:00
Dominik Jain 93896d2263 Remove workaround for FlexLayout.appendChild
Update instructions to no longer stress that FlexLayout.appendChild
does not work as expected (#8417 now being resolved)
2026-03-12 12:06:16 +01:00
Dominik Jain 6c7c584c9a Emphasise the importance of the 'auto' sizing option of layouts 2026-03-12 12:06:16 +01:00
Dominik Jain ac6541d74a Add instructions to avoid unnecessary annotations 2026-03-12 12:06:16 +01:00
Dominik Jain 683468fa97 Update instructions on sizing options for FlexLayout & GridLayout
With #39 implemented, update the instructions accordingly.
2026-03-12 12:06:16 +01:00
Dominik Jain d2c9911eb2 📎 Fix typo 2026-03-12 12:06:16 +01:00
Dominik Jain ba138de53e Make clear that layoutChild is only available after the child was added 2026-03-12 12:06:16 +01:00
Dominik Jain bf87af1928 Add instructions on how to reuse fills/strokes 2026-03-12 12:06:16 +01:00
Dominik Jain a928980d62 📎 Ignore .claude 2026-03-12 12:06:16 +01:00
Eva Marco c00ef7c128 🐛 Fix unnexpected warning (#8603) 2026-03-12 12:01:28 +01:00
Elena Torro 6ca8865e5b 🐛 Close the subpath when possible 2026-03-12 12:00:03 +01:00
Marina López 58d7e1de18 📎 Revert show version notes when navigate from cc (#8591) 2026-03-12 11:47:50 +01:00
Xaviju 9b3207b06c 🐛 Fix wrong value on property copy on inspect styles (#8605) 2026-03-12 11:45:16 +01:00
Eva MarcoandAndrey Antukh 5c989d00d0 🎉 Token form combobox (#8294)
* 🎉 Create token combobox

* ♻️ Extract floating position as hook

* ♻️ Extract mouse navigation as hook

* ♻️ Extract token parsing

* 🎉 Add test

* 🎉 Add flag

* 🐛 Fix comments

* 🐛 Fix some errors on navigation

* 🐛 FIx errors on dropdown selection in the middle of the string

* 🐛 Only select available options not headers or empty mesage

* ♻️ Change component name

* 🐛 Intro doesn't trigger dropdown

* 🐛 Fix differences between on-option-enter and on-option-click

* ♻️ Refactor scrollbar rule

* 🐛 Fix update proper option

* ♻️ Use tdd to resolve parsing token

* ♻️ Add more test

* ♻️ Use new fn for token parsing

* ♻️ Refactor new fns and add docstrings

* 🐛 Fix comments and warnings

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-03-12 09:34:29 +01:00
Elena Torró 1512d53e7c Merge pull request #8601 from penpot/superalex-fix-text-width-not-applied-on-first-change-for-auto-width-texts-in-wasm-viewport
🐛 Fix text width not applied on first change for auto-width texts in WASM viewport
2026-03-12 09:30:10 +01:00
Elena Torró c59df2e52d Merge pull request #8602 from penpot/superalex-fix-slash-problem-in-embedded-editor
🐛 Fix slash problem in embedded editor
2026-03-12 09:21:18 +01:00
Elena Torro e72e2bf176 🐛 Fix stroke closing on outer strokes on paths 2026-03-12 09:02:37 +01:00
Alejandro Alonso 0d1b8dc1d6 🐛 Fix slash problem in embedded editor 2026-03-12 08:45:14 +01:00
Alejandro Alonso 70ef763bfe 🐛 Fix text width not applied on first change for auto-width texts in WASM viewport 2026-03-12 08:25:36 +01:00
Alejandro Alonso ecf525e094 Merge pull request #8576 from penpot/elenatorro-13619-fix-svg-inner-stroke-artifact
🐛 Fix inner stroke intersection on paths
2026-03-12 08:20:21 +01:00
Andrey Antukh 3e60de9582 🐛 Backport merge issues fixes from develop. 2026-03-11 20:16:32 +01:00
Elena Torró af7a9b4589 Merge pull request #8584 from penpot/azazeln28-fix-13577-auto-width-fixed-width-regression
🐛 Fix auto-width/fixed-width regression
2026-03-11 16:32:23 +01:00
Andrey Antukh 11a1ac2a09 🐛 Fix RangeError (stack overflow) in find-component-main
Refactor find-component-main to use an iterative loop/recur pattern instead of direct recursion and added cycle detection for malformed data structures.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-11 16:15:47 +01:00
Andrey Antukh ade0b6b07b 🐛 Fix issues introduced on merge from staging 2026-03-11 16:07:35 +01:00
Alonso Torres 31a4a7f21f 💄 Removed forgotten print (#8594) 2026-03-11 16:04:33 +01:00
Andrey Antukh 2de3ead14f Merge remote-tracking branch 'origin/staging-render' into develop 2026-03-11 15:50:58 +01:00
Andrey Antukh 0708b0f334 Merge remote-tracking branch 'origin/staging' into staging-render 2026-03-11 15:45:55 +01:00
Andrey Antukh 7ec9261475 Add improvements to AGENTS.md (#8586) 2026-03-11 15:24:40 +01:00
Belén Albeza d8249cc3db Add regression test for token highlight bug (13302) (#8573)
*  Add aria role to token pill

*  Clean up unused vars, imports and unneeded intercepts in tokens tests

*  Add regression test for bug 13302 (highlight token)
2026-03-11 14:17:57 +01:00
Elena Torró 2ca264496c Merge pull request #8529 from penpot/azazeln28-feat-add-proper-ltr-rtl-navigation
🎉 Add LTR/RTL cursor navigation
2026-03-11 13:52:17 +01:00
Aitor Moreno 920e66fd24 🎉 Add LTR/RTL cursor navigation 2026-03-11 13:34:23 +01:00
Andrey Antukh 33da7f384a Merge remote-tracking branch 'origin/staging' 2026-03-11 12:04:28 +01:00
Aitor Moreno e380886f51 🐛 Fix auto-width/fixed-width regression 2026-03-10 20:51:40 +01:00
David Barragán Merino e6d15a5ac2 🔧 Disable search indexing of plugin docs for non-production envs 2026-03-10 19:36:52 +01:00
David Barragán Merino e855907b05 🔧 Disable search indexing of plugin docs for non-production envs 2026-03-10 19:36:28 +01:00
David Barragán Merino b314faa0e9 🔧 Disable search indexing of plugin docs for non-production envs 2026-03-10 19:35:19 +01:00
Andrey Antukh db9e9f4832 🐛 Ignore browser extension errors in unhandled exception handler
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 19:30:01 +01:00
Andrey Antukh 7939cb045b 🐛 Fix plain vector leaking into shape :content from shape-to-path conversions
group-to-path was storing a raw concatenated vector into :content after
flattening children's PathData instances via (map vec). bool-to-path
was storing the plain-vector result of bool/calculate-content directly.
Both now wrap through path.impl/path-data at the assignment site so the
:content invariant (always a PathData instance) is upheld.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 19:26:03 +01:00
Andrey Antukh f566a2adfd 🐛 Fix ITransformable error when path content is a plain vector
Coerce content to PathData in transform-content before dispatching
the ITransformable protocol, so shapes carrying a plain vector in
their :content field (legacy data, bool shapes, SVG imports) no
longer crash with 'No protocol method ITransformable.-transform
defined for type object'.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 18:06:44 +00:00
Andrey Antukh 31d8b35a2c 📎 Revert small changes related to browser pool on exporter 2026-03-10 18:51:04 +01:00
Elena Torro 70dd46f8ce 🐛 Fix inner stroke intersection on paths 2026-03-10 16:08:55 +01:00
Andrey Antukh fed01fba73 🐛 Wrap fetch TypeError into proper ex-info with :unable-to-fetch code
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 15:59:14 +01:00
Andrey Antukh 7248db28c8 🐛 Fix nil values being inserted into TokenTheme :sets field (#8560)
* 🐛 Fix nil values being inserted into TokenTheme :sets field
* 📎 Use transducer form for filter in make-token-theme

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

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 15:44:06 +01:00
Andrey Antukh 4f0bceddae 🐛 Fix stale deferred DOM ops in dashboard navigation
Two related issues that could cause crashes during fast navigation
in the dashboard:

1. grid.cljs: On drag-start, a temporary counter element is appended
   to the file card node for the drag ghost image, then scheduled for
   removal via requestAnimationFrame. If the user navigates away before
   the RAF fires, React unmounts the section and removes the card node
   from the DOM. When the RAF fires, item-el.removeChild(counter-el)
   throws because counter-el is no longer a child. Fixed by guarding
   the removal with dom/child?.

2. sidebar.cljs: Keyboard navigation handlers used ts/schedule-on-idle
   (requestIdleCallback with a 30s timeout) to focus the newly rendered
   section title after navigation. This left a very wide window for the
   callback to fire against a stale DOM after a subsequent navigation.
   Additionally, the idle callbacks were incorrectly passed as arguments
   to st/emit! (which ignores non-event values), making the scheduling
   an accidental side effect. Fixed by replacing all occurrences with
   ts/schedule (setTimeout 0), which is sufficient to defer past the
   current render cycle, and moving the calls outside st/emit!.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 14:22:45 +00:00
Andrey Antukh 80b64c440c 🐛 Fix removeChild crash on portal-on-document* unmount
The previous implementation passed document.body directly as the
React portal containerInfo. During unmount, React's commit phase
(commitUnmountFiberChildrenRecursively, case 4) sets the current
container to containerInfo and then calls container.removeChild()
for every DOM node inside the portal tree.

When two concurrent state updates are processed — e.g. navigating
away from a dashboard section while a file-menu portal is open —
React could attempt document.body.removeChild(node) twice for the
same node, the second time throwing:

  NotFoundError: Failed to execute 'removeChild' on 'Node':
  The node to be removed is not a child of this node.

The fix allocates a dedicated <div> container per portal instance
via mf/use-memo. The container is appended to body on mount and
removed in the effect cleanup. React then owns an exclusive
containerInfo and its unmount path never races with another
portal or the modal container (which also targets document.body).

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 14:22:34 +00:00
Elena Torró 5a1461a910 Merge pull request #8563 from penpot/superalex-fix-negative-insets
🐛 Fix negative insets
2026-03-10 15:08:39 +01:00
Andrey Antukh 98c1503bca Backport serveral plugin types documentation 2026-03-10 15:05:08 +01:00
Andrey Antukh 9f66220caa 🐛 Fix flex layout container horizontalSizing/verticalSizing via plugin API (#8555)
Setting horizontalSizing/verticalSizing on a FlexLayoutProxy was
dispatching update-layout-child instead of update-layout, so the
frame's auto-sizing (hug content) was never triggered even though
the getter read back the value correctly.

Also restricts accepted values to #{:fix :auto} (matching shape.cljs)
since frames cannot use :fill, and fixes a copy-paste error that
reported :horizontalPadding instead of :horizontalSizing in error messages.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 15:01:23 +01:00
Aitor Moreno 3112b0d8cf 🐛 Fix grow options not verifying text-editor/v2 (#8571) 2026-03-10 15:01:23 +01:00
Andrey Antukh ab90500ec8 🐛 Fix download-image to properly handle network errors and non-2xx responses (#8554)
The download-image function in app.media silently succeeded when the
remote image URL was unreachable or returned an error status code,
causing create-file-media-object-from-url to report success with no
actual image stored.

Add exception handling for connection refused, timeouts, and I/O errors
around the HTTP request, and validate the HTTP status code in
parse-and-validate before processing the response body.

Fixes #8499

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 15:01:23 +01:00
Pablo Alba 3141f67cd7 Add subscription info for nitrate 2026-03-10 14:55:12 +01:00
Aitor Moreno 4bfd5194f6 🐛 Fix grow options not verifying text-editor/v2 (#8571) 2026-03-10 14:41:50 +01:00
Andrey Antukh 0f47c30349 Merge branch 'main' into staging 2026-03-10 14:39:16 +01:00
Andrey Antukh 68fbacf8b3 Merge tag '2.14.0-RC2' 2026-03-10 14:38:58 +01:00
Andrey Antukh 7ab5f241da 🐛 Fix TypeError when path content is nil in get-points calls
Use nil-safe path/get-points wrapper (some-> based) instead of
direct path.segment/get-points calls in edition.cljs to prevent
'Cannot read properties of undefined (reading get)' crash.

Add nil-safety test to verify path/get-points returns nil without
throwing when content is nil.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 12:21:13 +00:00
Andrey Antukh 32cf95265a 📚 Add GitHub Copilot instructions (#8548) 2026-03-10 13:12:15 +01:00
Elena Torró bd28131357 Merge pull request #8559 from penpot/superalex-fix-text-strokes-opacity
🐛 Fix text stroke opacity causing different colors on overlapping glyphs
2026-03-10 13:03:54 +01:00
Alejandro Alonso 0f34677ba7 🐛 Fix negative insets 2026-03-10 12:42:08 +01:00
Alejandro Alonso 024f779cab 🐛 Fix text stroke opacity causing different colors on overlapping glyphs 2026-03-10 12:36:53 +01:00
Andrey Antukh 70030fa9e3 🐛 Fix download-image to properly handle network errors and non-2xx responses (#8554)
The download-image function in app.media silently succeeded when the
remote image URL was unreachable or returned an error status code,
causing create-file-media-object-from-url to report success with no
actual image stored.

Add exception handling for connection refused, timeouts, and I/O errors
around the HTTP request, and validate the HTTP status code in
parse-and-validate before processing the response body.

Fixes #8499

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 10:04:07 +01:00
Andrey Antukh 0de482da9d ⬆️ Update pnpm to 10.31.0 across all submodules (#8549) 2026-03-10 10:03:05 +01:00
Andrey Antukh 8d342e9374 🐛 Fix flex layout container horizontalSizing/verticalSizing via plugin API (#8555)
Setting horizontalSizing/verticalSizing on a FlexLayoutProxy was
dispatching update-layout-child instead of update-layout, so the
frame's auto-sizing (hug content) was never triggered even though
the getter read back the value correctly.

Also restricts accepted values to #{:fix :auto} (matching shape.cljs)
since frames cannot use :fill, and fixes a copy-paste error that
reported :horizontalPadding instead of :horizontalSizing in error messages.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-03-10 09:59:54 +01:00
Aitor Moreno 5474b1890b Merge pull request #8558 from penpot/superalex-fix-embedded-editor-deselect-text-shape
🐛 Fix embedded editor deselect text shape
2026-03-10 09:54:01 +01:00
Alejandro Alonso 3e0cef4a3c 🐛 Fix embedded editor deselect text shape 2026-03-10 07:39:13 +01:00
Andrey Antukh e5f321c8f1 Merge remote-tracking branch 'origin/develop' into develop 2026-03-09 21:28:11 +01:00
Andrey Antukh 657546a993 Merge remote-tracking branch 'origin/staging' into develop 2026-03-09 21:27:50 +01:00
Denys Kisil 0719e4fa70 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.7% (2068 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-03-09 20:09:49 +00:00
Pablo Alba b0ad6d7fdb Mark the default team for an user in an org with the default flag (#8552) 2026-03-09 17:46:10 +01:00
Elena Torró 052417cd10 Merge pull request #8551 from penpot/ladybenko-13536-fix-position-absolute
🐛 Fix ordering of absolute shapes with no z-index
2026-03-09 17:09:15 +01:00
Elena Torro d948761090 🐛 Fix WebGL context lost error to raise an exception and show the exception page 2026-03-09 17:05:10 +01:00
Belén Albeza a2c89a816a 🐛 Fix ordering of absolute shapes with no z-index 2026-03-09 16:50:55 +01:00
Marina López ab20019e81 Add show version notes when navigate from CC 2026-03-09 16:32:07 +01:00
Elena Torró 6c20bfbc9b Merge pull request #8545 from penpot/superalex-fix-non-uniform-stroke-scaling-path-shapes-wasm
🐛 Fix non-uniform stroke scaling on path shapes in WASM renderer
2026-03-09 16:23:50 +01:00
Andrey Antukh 05c71f7b75 📚 Add GitHub Copilot instructions (#8548) 2026-03-09 16:23:28 +01:00
Luis de Dios adc3fa41e9 🎉 Add workspace menu for MCP server 2026-03-09 13:02:14 +01:00
Alejandro Alonso bdfa176b2f Merge pull request #8526 from penpot/azazeln28-feat-double-click-word-boundary-selection
🎉 Add word boundary selection
2026-03-09 12:53:30 +01:00
Alejandro Alonso 84539dac1f 🐛 Fix non-uniform stroke scaling on path shapes in WASM renderer 2026-03-09 12:42:09 +01:00
Pablo Alba 34d29328e6 🐛 Fix bad size on switching a layout with fixed sizing (#8504) 2026-03-09 12:12:03 +01:00
Eva Marco c59cc4dff4 🐛 Fix tooltip position on absolute positioned elements (#8509)
* 🐛 Fix tooltip position on absolute positioned elements

* 🐛 Fix tests
2026-03-09 12:11:39 +01:00
Eva Marco 0a5de10dff 🐛 Fix name on broken color token (#8527) 2026-03-09 12:10:26 +01:00
Pablo Alba b3a6468697 Add nitrate method for notify user when is added to organization (#8531) 2026-03-09 12:09:42 +01:00
Alonso Torres 40c9466718 🐛 Fix type in plugin attribute (#8543) 2026-03-09 12:06:56 +01:00
Alonso Torres 321b53e936 Add improvements on variants plugins (#8482) 2026-03-09 10:24:16 +01:00
Andrey Antukh 0ceadada35 🐛 Fix invalid data on layout flex dir shape property 2026-03-09 10:09:07 +01:00
Alejandro Alonso a059284a30 Merge pull request #8462 from penpot/ladybenko-13452-error-types
🎉 Improved wasm error handling
2026-03-09 10:08:43 +01:00
Andrey Antukh 77955d7f91 Add several redundant checks for library-id on file rpc methods 2026-03-09 10:01:29 +01:00
Andrey Antukh 151238e518 💄 Add cosmetic change to link-file-to-library rpc method impl 2026-03-09 10:01:29 +01:00
Andrey Antukh 591d63e470 Add better error report on wrong input on logging helpers 2026-03-09 10:01:09 +01:00
Belén Albeza 2ace44c9e5 Create wasm_error macro to handle Wasm errors differentiating critical vs recoverable 2026-03-09 07:22:32 +01:00
deveronica aca63802e1 🌐 Add translations for: Korean
Currently translated at 99.8% (2070 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-03-05 22:13:51 +01:00
deveronica 380d211b4c 🌐 Add translations for: Korean
Currently translated at 48.8% (1013 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-03-04 21:10:10 +01:00
Marina López 5102ae2a58 Add API get-penpot-version 2026-03-04 15:27:59 +01:00
andrés gonzález 5a6be141fd 📚 Add info about using math in tokens (#8510) 2026-03-04 14:59:04 +01:00
Aitor Moreno 208b3329fd Merge pull request #8532 from penpot/superalex-fix-cut-copy-paste
🐛 Fix cut copy paste
2026-03-04 13:38:11 +01:00
Alejandro Alonso da372099f7 🐛 Fix cut copy paste 2026-03-04 13:20:11 +01:00
María Valderrama de5276d638 💄 Add missing nitrate banner 2026-03-04 11:35:56 +01:00
Aitor Moreno 0b41a910bf 🎉 Add word boundary selection 2026-03-04 10:59:46 +01:00
Eva Marco cc3033735b 🐛 Fix showing warning when no shape is selected (#8515) 2026-03-04 10:58:36 +01:00
Xaviju e1d556f4aa 🐛 Sort tokens by name (#8488) 2026-03-04 10:33:29 +01:00
Andrey Antukh c3f5117757 🐛 Fix unhandled exception on using decimals on stroke row (#8405) 2026-03-04 09:47:14 +01:00
Elena Torró ffae6d4281 Merge pull request #8524 from penpot/azazeln28-feat-text-editor-theme-conf
🎉 Text Editor v3 theme conf
2026-03-04 09:37:56 +01:00
Andrey AntukhandEva Marco 86e851f408 🐛 Fix incorrect version visibility on workspace (#8463)
* 🐛 Add missing order by clause to snapshot query

This fixes the incorrect snapshot visibility when file
has a lot of versions.

*  Reduce allocation on milestone-group* component

* 🐛 Fix milestone group timestamp formatting

* 📎 Update changelog

* 🐛 Fix scroll on history panel

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
2026-03-04 09:27:51 +01:00
Marina López 4da9aa844b 💄 Align button with other elements 2026-03-04 09:24:57 +01:00
Andrey Antukh a4351d133b Add minor improvements to error reporting (#8402) 2026-03-04 09:12:19 +01:00
Andrey Antukh b704a7da0e 🐛 Fix inconsistency between plugins api doc and impl for shadows (#8454)
Related to offset-x and offset-y attributes.
2026-03-04 09:09:27 +01:00
Mihai 1ce295f5e5 🐛 Auto-focus search input when shortcuts panel opens
Fixes #8481
2026-03-04 09:09:24 +01:00
Andrey Antukh 478f631df5 🐛 Don't throw exception when picker is closed and image is still uploading (#8453)
*  Add notification tag to media uploading

This avoid hidding error messages once the upload
is finished.

* 🐛 Don't throw exception when picker is closed and image is still uploading
2026-03-04 09:07:15 +01:00
Dominik Jain c9d9e493e7 🎉 Prepare npm package for MCP server (#8473)
* 🎉 Prepare npm package for MCP server

* 🐛 Re-establish Windows compatibility of MCP server build script

Use node instead of cp to copy files

*  Set version for MCP npm tarball based on git tag

* Add scripts/set-version to set the version in package.json
  based on git describe information
* Add scripts/pack to perform the packaging
2026-03-04 08:41:28 +01:00
Belén Albeza 287b9d4597 🔧 Remove deleting node_modules on frontend watch script (#8525) 2026-03-04 08:28:58 +01:00
deveronica 0be5119b21 🌐 Add translations for: Korean
Currently translated at 14.1% (294 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-03-03 19:09:49 +00:00
Elena Torró 336095486e Merge pull request #8501 from penpot/superalex-fix-frame-clipping-artifact
🐛 Fix frame clipping artifact
2026-03-03 16:16:21 +01:00
Aitor Moreno ccb272784f 🎉 Add TextEditor theme customization 2026-03-03 16:04:41 +01:00
Elena Torró 52b4e803ff Merge pull request #8492 from penpot/azazeln28-fix-text-editor-initialization
♻️ Refactor Text Editor v3
2026-03-03 15:59:15 +01:00
Aitor Moreno 95aa63374c ♻️ Refactor Text Editor v3 2026-03-03 15:49:26 +01:00
Elena Torro 1800deddd5 🔧 Await promise correctly to fix tests flakyness 2026-03-03 13:01:32 +01:00
Marina López eb5b3a3fe5 Add link to see current plan 2026-03-03 12:56:40 +01:00
Elena Torro 9de591d9d7 🔧 Await promise correctly to fix tests flakyness 2026-03-03 12:34:18 +01:00
Andrey Antukh 57b9efbcd7 🐛 Fix redo operation on commenting on workspace (#8455) 2026-03-03 09:50:23 +01:00
Elena Torró ab40f3c888 Merge pull request #8518 from penpot/superalex-fix-blur-affecting-extra-shapes
🐛 Fix blur affecting extra shapes
2026-03-03 09:24:55 +01:00
andrés gonzález db0a8b65ca 📚 Add info about tokens remapping (#8503) 2026-03-03 09:02:31 +01:00
andrés gonzález 7c326e05e4 📚 Fix spanish text at docs (#8502) 2026-03-03 09:02:08 +01:00
andrés gonzález 58e86a545a 📚 Add info about grouping tokens (#8508) 2026-03-03 09:01:48 +01:00
Alejandro Alonso 9fa027c1df 🐛 Fix blur affecting extra shapes 2026-03-03 08:48:43 +01:00
deveronica 8e17f846c9 🌐 Add translations for: Korean
Currently translated at 12.0% (250 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-03-02 19:09:49 +01:00
Denys Kisil 8262b7a3a2 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.7% (2068 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-03-02 19:09:47 +01:00
Andrés Moya 31478c6afc 🐛 Fix validation of shadow token with missing keys (#8507) 2026-03-02 16:17:12 +01:00
Julien Déramond cc2c104e16 📚 Move Design Tokens > Spacing image to the Spacing section (#8487)
Signed-off-by: Julien Déramond <julien.deramond@thalesgroup.com>
2026-03-02 15:50:32 +01:00
Andrey Antukh 0b8ac2508e 📎 Update changelog 2026-03-02 14:57:03 +01:00
Andrey Antukh c35f70edc5 📎 Add minor adjustments 2026-03-02 14:57:03 +01:00
bittoby c18375c66e Add Tab/Shift+Tab navigation to rename layers sequentially 2026-03-02 14:57:03 +01:00
Andrey Antukh 585a2d7523 🐛 Fix merge issues 2026-03-02 14:02:05 +01:00
Andrey Antukh 23e77b5f03 🐛 Fix merge issues 2026-03-02 13:35:36 +01:00
Andrey Antukh 7067cc2286 Merge remote-tracking branch 'origin/staging-render' into develop 2026-03-02 12:22:47 +01:00
Andrey Antukh 0644bd817e Merge remote-tracking branch 'origin/staging' into staging-render 2026-03-02 12:20:08 +01:00
Dominik Jain b587e2e8ec MCP: Improve Streamable HTTP session handling & logging (#8493)
*  Reintroduce proper session management for /mcp endpoint

Reuse transport and server instance based on session ID in header

*  Periodically clean up stale streamable HTTP sessions

Add class StreamableSession to improve type clarity

*  Avoid recreation of objects when instantiating McpServer instances

Precompute the initial instructions and all tool-related data

*  Improve logging of tool executions
2026-03-02 11:27:13 +01:00
Maks d61e57099e 🐛 Make boolean environment variable parsing case-insensitive (#8500)
Resolves configuration validation errors when boolean environment variables
are provided with mixed case (e.g., PENPOT_TELEMETRY_ENABLED=True). The
parse-boolean function now handles all string variations: true, True, TRUE,
false, False, FALSE.

opencode/Bug-Hunter @ ollama/GLM4.6 with Love

Signed-off-by: Max <60165+34x@users.noreply.github.com>
2026-03-02 11:26:44 +01:00
Alejandro Alonso cfe11a930c 🐛 Fix frame clipping artifact 2026-03-02 09:32:30 +01:00
Egor Filatov bce52c6da8 🌐 Add translations for: Russian
Currently translated at 80.0% (1660 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-03-01 10:09:48 +01:00
Alexis Morin cd3a1d6376 🌐 Add translations for: French (Canada)
Currently translated at 99.7% (2068 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-28 01:09:48 +01:00
Pablo Alba 97d3e31593 Add success popup for nitrate license subscription 2026-02-27 12:28:09 +01:00
Aitor Moreno 740e790585 🎉 Add active-features? helper function (#8490) 2026-02-27 12:12:27 +01:00
Dominik Jain 8882f18db4 🚑 Fix multi-user mode MCP connections
Previously, only the latest streamable HTTP connection was operational
2026-02-26 17:39:33 +01:00
Denys Kisil c769e782f0 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.7% (2068 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-02-26 15:10:03 +01:00
Alexis Morin ed97cdde66 🌐 Add translations for: French (Canada)
Currently translated at 96.4% (2001 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-26 15:09:51 +01:00
Alejandro Alonso a2f8fca6ea Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-26 14:05:32 +01:00
Elena Torró ed23c55550 Merge pull request #8483 from penpot/superalex-fix-opacity-for-dotted-strokes
🐛 Fix opacity for dotted strokes
2026-02-26 13:41:43 +01:00
Alejandro Alonso 5b5c868a87 🐛 Fix opacity for dotted strokes 2026-02-26 13:31:12 +01:00
Eva Marco 35c829a981 🐛 Add token name in broken token tooltip (#8480) 2026-02-26 13:29:08 +01:00
Luis de Dios b5874b365b Merge pull request #8414 from oraios/mcp-dev-latest
 Update MCP server to account for recent API changes & general improvements
2026-02-26 13:18:19 +01:00
Alejandro Alonso 1a3ac6bdf8 Merge pull request #8475 from penpot/elenatorro-13524-fix-token-highlight
🐛 Fix rotation token highlight and its application on the text-ed…
2026-02-26 13:00:45 +01:00
Elena Torró de5d4f4292 Merge pull request #8460 from penpot/azazeln28-refactor-text-cursor
♻️ Refactor TextCursor and TextPositionWithAffinity
2026-02-26 12:29:43 +01:00
Elena Torro 2bd7c10e09 🔧 Fix variable name from wrong merge 2026-02-26 12:19:20 +01:00
Juan de la Cruz 7066afa01a 🎉 Add new slides 2.14 content (#8478) 2026-02-26 12:19:15 +01:00
Elena Torro 495371c079 🐛 Fix rotation token highlight and its application on the text-editor-v2 2026-02-26 11:57:11 +01:00
Elena Torró 75b1c0c1b1 Merge pull request #8280 from penpot/niwinz-layers-sidebar-changes
 Add serveral performance optimization to layers sidebar
2026-02-26 11:37:57 +01:00
Dalai Felinto 0ff5574b12 Add the ability to import tokens from Linked Library
Add the option to import tokens from a linked library.

I know there are plans to link the tokens in together with the library.
Once this happens this patch can be reverted. Until then it helps a lot
to use a design system that relies on themes.

Before that someones would need to:
* Download the design system / add to their team.
* Open the file, download the tokens.

For every new file:
* Link the Design System library.
* Import the tokens file.

With this patch all you need to get started is to download the design
system and add to your team. From their importing the links is done on
the same pop-up that is used to import the tokens.

---

Technical considerations:

I try adding this as a dialog that is called once the library is
imported. I ran into a few issues though:

* To find whether the library has tokens (and thus show the dialog) I
  would need to extend library summary to include tokens.
* I couldn't find a reliable way to import the tokens after importing
  the library without resorting to a timer :/

I'm sure both of those hurdles are doable, I just wasted enough time
trying it to the point I decided on a different approach.

Signed-off-by: Dalai Felinto <dalai@blender.org>

📎 Fix minor issues and linter reports

📎 Reuse translations
2026-02-26 11:37:56 +01:00
Andrey Antukh 5ea4b03108 📎 Fix e2e tests 2026-02-26 11:13:31 +01:00
Andrey Antukh 0fef5b7e5d Memoize variant props on layer-item 2026-02-26 11:13:31 +01:00
Andrey Antukh 8a1fdd9dd1 Reduce watchers for layer-item rename mechanism 2026-02-26 11:13:31 +01:00
Andrey Antukh a080a9e646 Add micro optimizations to layer-item component 2026-02-26 11:13:31 +01:00
Andrey Antukh a728d5a5f2 💄 Add minor cosmetic changes to filters-tree component 2026-02-26 11:13:30 +01:00
Andrey Antukh 6072234230 Add more selective debouncing for layers-tree 2026-02-26 11:13:30 +01:00
Andrey Antukh 41f2877801 Reduce allocation on layers-tree component 2026-02-26 11:13:30 +01:00
Andrey Antukh e2576d049a 💄 Add minor cosmetic changes on event listening 2026-02-26 11:13:30 +01:00
Andrey Antukh 4db9c373e6 💄 Fix component naming style related to layer-item 2026-02-26 11:13:30 +01:00
Andrey Antukh 09a9407867 💄 Change props naming on layer-item and related components 2026-02-26 11:13:30 +01:00
Andrey Antukh 7be03e2ea6 Remove usage of use-var on layer-item
Focus on use more basic primitves on performance
sensitive components
2026-02-26 11:13:30 +01:00
Eva Marco 9345902a62 🐛 Fix cannot apply second token after creation while shape is selected (#8476) 2026-02-26 10:53:25 +01:00
Alonso Torres a4190df073 🐛 Fix problem with flex.appendChild with naturalOrdering on plugins API (#8470) 2026-02-26 10:47:44 +01:00
Alexis Morin 05521a84d4 🌐 Add Canadian French 2026-02-26 10:26:10 +01:00
Eva Marco 47dae090ed 🐛 Add notification to token applied during text edition (#8434) 2026-02-26 10:24:48 +01:00
MkDev11 e30c01db26 🎉 Allow duplicating color and typography styles (#8449)
Add duplicate functionality for colors and typographies in the Assets
panel, matching the existing duplicate feature for components.

Changes:
- Add duplicate-color and duplicate-typography events in libraries
- Add Duplicate context menu option for colors
- Add Duplicate context menu option for typographies
- Update CHANGES.md

Closes #2912

Signed-off-by: mkdev11 <98430825+MkDev11@users.noreply.github.com>
2026-02-26 10:13:34 +01:00
Aitor Moreno 05165ce014 🐛 Fix board title cropped using wrong side 2026-02-26 09:35:56 +01:00
Aitor Moreno 96677713fc 🐛 Fix 45 rotated board doesn't show title properly 2026-02-26 09:34:15 +01:00
Pablo Alba c27f874e74 Show subscription type nitrate 2026-02-26 09:09:48 +01:00
Alejandro Alonso 901aa9bf09 Merge pull request #8403 from penpot/azazeln28-issue-13306-45-degree-rotated-board
🐛 Fix 45 rotated board doesn't show title properly
2026-02-26 07:57:12 +01:00
Aitor Moreno 0aea699482 🐛 Fix board title cropped using wrong side 2026-02-25 16:14:40 +01:00
Aitor Moreno 48d2135cf3 🐛 Fix 45 rotated board doesn't show title properly 2026-02-25 16:14:24 +01:00
Andrey Antukh d680973c85 Merge remote-tracking branch 'origin/staging' into develop 2026-02-25 15:31:49 +01:00
Alejandro Alonso 0d194decbf Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-25 14:26:42 +01:00
alonso.torres f41eca12f4 🐛 Fix problem with frame title movement 2026-02-25 14:14:08 +01:00
Andrés Moya c72e9ee1a0 🐛 Convert token values for the plugins 2026-02-25 14:04:20 +01:00
Andrés Moya ba87ea1a44 🔧 Add tokenscript flag and more validations to token values 2026-02-25 14:04:20 +01:00
Andrés Moya 72a855d4ac 🐛 Fix activeSets in themes API 2026-02-25 14:04:20 +01:00
Eva Marco e2377e8fa8 🐛 Fix input width on composite token form 2026-02-25 14:04:20 +01:00
Eva Marco c08cff68d7 ♻️ Refactor token test to match new render (#8442)
* ♻️ Refactor apply token test to match new render

* ♻️ Refactor crud token test with new render

* ♻️ Refactor general token tes tto use new render

* ♻️ Refactor remapping token tests to use new render

* ♻️ Refactor token set tests to use new render

* ♻️ Refactor token theme tests to use new render

* ♻️ Refactor token tree tests to use new render
2026-02-25 14:03:31 +01:00
Luis de Dios a75de11e70 Improve MCP section in the dashboard 2026-02-25 13:17:10 +01:00
alonso.torres 701443c3d7 Add disconnect to MCP plugin 2026-02-25 13:16:56 +01:00
Alejandro Alonso baa44119f4 Merge pull request #8468 from penpot/azazeln28-issue-13315-fix-text-alignment-options
🐛 Fix text alignment options
2026-02-25 13:15:13 +01:00
Aitor Moreno 7d3e434167 Merge pull request #8457 from penpot/superalex-improve-cursor-blinking
🎉 Improve cursor blinking
2026-02-25 12:50:05 +01:00
Dominik Jain 0974bca2c0 Improve instructions on writable shape properties 2026-02-25 12:49:26 +01:00
Dominik Jain 927455926f 📎 Update Serena project file 2026-02-25 12:48:38 +01:00
Aitor Moreno 40233e3316 🐛 Fix text alignment options 2026-02-25 12:47:07 +01:00
Alejandro Alonso 7e287bacfd Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-25 12:17:38 +01:00
Andrey Antukh b4c279ad7b 💄 Add minor cosmetic refactor on how plugin flags are stored
The main idea behind this, is move all plugin related stuff from
app.main.data.plugins into app.plugins.* and make them more consistent.
Also the intention that put all plugins related state under specific
prefix on the state.
2026-02-25 11:35:03 +01:00
Alejandro Alonso e2b5f936f5 🐛 Fix stroke artifacts 2026-02-25 11:27:05 +01:00
Elena Torró 614c6ed300 Merge pull request #8461 from penpot/superalex-fix-auto-width-affects-text-selection
🐛 Fix auto width affects text selection
2026-02-25 11:22:38 +01:00
Alejandro Alonso 4975f28a3d 🐛 Fix auto width affects text selection 2026-02-25 11:11:45 +01:00
Alejandro Alonso f5109c7df2 🎉 Refactor caret blinking to reduce CPU usage 2026-02-25 11:08:56 +01:00
Elena Torró 12a1cb1d32 Merge pull request #8451 from penpot/superalex-update-skia-version
🎉 Update skia version
2026-02-25 11:07:00 +01:00
Alejandro Alonso 84ba6f0002 🎉 Update skia version 2026-02-25 10:30:29 +01:00
Aitor Moreno a12b59d101 ♻️ Refactor TextCursor and TextPositionWithAffinity 2026-02-25 09:59:02 +01:00
Pablo Alba e4b69426e9 Add subscribe to nitrate dialog 2026-02-24 14:45:31 +01:00
Alonso Torres c972c06142 🐛 Fix problem with export dialog on single board (#8426) 2026-02-24 14:41:35 +01:00
Elena Torró 32d4026641 Merge pull request #8338 from penpot/azazeln28-11826-compute-selection-rects-from-pointer-events
🎉 Add compute selection rects from pointer events
2026-02-24 12:53:08 +01:00
Aitor Moreno 4477b2b4a0 🎉 Compute selection rects from pointer events 2026-02-24 11:09:45 +01:00
Alexis Morin db2689efc9 🌐 Add translations for: French (Canada)
Currently translated at 91.1% (1891 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-24 03:09:48 +01:00
Andrey Antukh bcc755b0be Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-24 00:09:57 +01:00
Andrey Antukh 9e51fa198a Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-24 00:09:41 +01:00
Andrey Antukh d176da8012 Add jfr and jcmd to the backend docker image (#8446) 2026-02-24 00:08:14 +01:00
Andrey Antukh 20862c2da3 🐛 Fix incorrect plugin icon resolution 2026-02-24 00:07:30 +01:00
Dominik Jain 4e577d37b8 Add information on the usage of component variants 2026-02-23 21:37:55 +01:00
Dominik Jain 40fb4edc4a PenpotUtils: Update isContainedIn to use textBounds, adding getBounds
Follow-up to https://github.com/penpot/penpot-mcp/issues/30
2026-02-23 17:27:25 +01:00
Dominik Jain e305ad1fa8 Update MCP instructions to mention new textBounds property
Follow-up to https://github.com/penpot/penpot-mcp/issues/30
2026-02-23 17:25:27 +01:00
Andrey Antukh 1b8afccba2 Remove usage of multipart body size config on backend 2026-02-23 14:44:44 +01:00
Yamila Moreno dd856ecf50 ♻️ Deprecate PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE envvar 2026-02-23 13:48:01 +01:00
Pablo Alba d159244ea6 Nitrate send subscription type 2026-02-23 13:43:31 +01:00
Elena Torró f4e79af3cd Merge pull request #8438 from penpot/alotor-fix-flex-layout-issue
🐛 Fix problem with flex layout propagation
2026-02-23 13:09:04 +01:00
alonso.torres 3e758826fe 🐛 Fix problem with flex layout propagation 2026-02-23 12:49:27 +01:00
Aitor Moreno 2cf66c948d Merge pull request #8427 from penpot/superalex-fix-blur-0-artifacts-2
🐛 Fix blur 0 artifacts
2026-02-23 12:25:54 +01:00
Andrey Antukh 145198c148 📎 Use proper version tag on frontend index template 2026-02-23 12:17:58 +01:00
Dalai Felinto 27c4ddba10 📎 Use generic error when failing to download font
The font specific error string was never added to en.po (my own mistake).

Looking further into it, there is no need to add more work to
translators when a generic error goes a long way.

Specially since this is not expected to happen.
2026-02-23 09:42:51 +01:00
alonso.torres eddfc4c4b2 🐛 Fix problem with createText in plugins 2026-02-23 09:35:30 +01:00
alonso.torres e6e34af391 🐛 Show outline on hidden paths 2026-02-23 09:34:50 +01:00
Alejandro Alonso 4ee908fc89 Revert "🐛 Fix stroke artifacts"
This reverts commit bdcf448f3f.
2026-02-23 07:23:41 +01:00
Alejandro Alonso bdcf448f3f 🐛 Fix stroke artifacts 2026-02-23 07:23:12 +01:00
Alexis Morin 5770a1fdc9 🌐 Add translations for: French (Canada)
Currently translated at 86.5% (1796 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-22 16:09:49 +00:00
Alexis Morin 41003310f8 🌐 Add translations for: French (Canada)
Currently translated at 85.3% (1771 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-21 14:09:48 +00:00
Aitor Moreno c58054d19c Merge pull request #8408 from penpot/ladybenko-always-mock-config-playwright
🔧 Always mock config.js in Playwright
2026-02-20 14:26:45 +01:00
Alejandro Alonso a7ab506c5c 🐛 Fix blur 0 artifacts 2026-02-20 13:37:27 +01:00
Marina López 16a067c0ae Add nitrate subscription plan card 2026-02-20 13:15:26 +01:00
Alejandro Alonso c7f644ab2a Merge pull request #8420 from penpot/elenatorro-13426-improve-pan-and-zoom-for-blur
🔧 Improve performance on shapes with blur
2026-02-20 12:49:24 +01:00
Andrés Moya 3d41dc276e 🐛 Fix resolve tokens with tokenscript when type is font family 2026-02-20 12:41:17 +01:00
Pablo Alba 90288e32d5 Show different info on nitrate dialog by connectivity 2026-02-20 10:19:25 +01:00
Yaron Shahrabani 4c416b7c18 🌐 Add translations for: Hebrew
Currently translated at 99.7% (2068 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-02-20 04:09:52 +01:00
Alexis Morin 7df10e2238 🌐 Add translations for: French (Canada)
Currently translated at 84.2% (1748 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-20 04:09:50 +01:00
Elena Torró cb5cacbcee Merge pull request #8413 from penpot/alotor-fix-error-emtpy-text
🐛 Fix problem with empty text
2026-02-19 17:09:03 +01:00
Dominik Jain f43de05d3d Remove workaround for atob function being unavailable
Follow-up to https://github.com/penpot/penpot-mcp/issues/17
2026-02-19 17:06:36 +01:00
Dominik Jain d019972bca Account for Token.resolvedValue now being implemented
Update MCP instructions, removing workaround for #8341
2026-02-19 17:06:36 +01:00
Dominik Jain 7fceb92673 Apply naturalChildOrdering, removing workarounds
Set the flag to true during code execution, resetting it to the
original value afterwards.

If the flag is unavailable, issue an error message, which is passed
on to the user via the LLM.

Remove instructions that served to work around the corresponding
issues:
 * https://github.com/penpot/penpot-mcp/issues/28
 * https://github.com/penpot/penpot-mcp/issues/32
2026-02-19 17:06:36 +01:00
Elena Torro 337cfc2d3e 🔧 Improve performance on shapes with blur 2026-02-19 16:50:42 +01:00
Dominik Jain 426053ac17 Update API type information for the MCP server
This resolves https://github.com/penpot/penpot-mcp/issues/31
2026-02-19 16:22:10 +01:00
Dominik Jain a5da7ceb2f Update TokenProperty values in system prompt
Update based on changes to camelCase.
See https://github.com/penpot/penpot-mcp/issues/38
2026-02-19 16:21:58 +01:00
Dominik Jain a7e3e78e0c Update Serena overview memory and initial instructions 2026-02-19 16:21:49 +01:00
Luis de Dios a82cf34d35 Merge pull request #8415 from oraios/mcp-prod
 MCP changes to improve handling of use cases 2 & 3
2026-02-19 16:01:10 +01:00
Alejandro Alonso 3f277b7daf Merge pull request #8416 from penpot/luis-revert-mcp-changes
Revert " MCP changes to improve handling of use cases 2 & 3…
2026-02-19 15:54:56 +01:00
Belén Albeza c2ee31e791 Fix some flaky text editor v2 tests 2026-02-19 15:46:16 +01:00
Luis de Dios 21a1320f16 Revert " MCP changes to improve handling of use cases 2 & 3 (#8369)"
This reverts commit 0a54d25d5a.
2026-02-19 14:46:44 +01:00
Dominik Jain 0a54d25d5a MCP changes to improve handling of use cases 2 & 3 (#8369)
* 📎 Fix spelling errors

* 🚧 Temporary workaround for sizing options not working

Add instructions explaining that FlexLayout sizing options do not work.
Relates to https://github.com/penpot/penpot-mcp/issues/39

* 🚧 Temporary workaround for Token resolvedValue not working

Instruct LLM to not use this property.
To be reverted once #8341 is fixed.

*  Improve description of token values

*  Make clear that ExecuteCodeTool serialises automatically

LLMs sometimes decide to apply serialisation themselves, which is unnecessary,
and which this seeks to prevent.

* 🚧 Temporary workaround for fills/strokes being read-only

Add instructions to make the limintations.
Once #8357 is resolved, this can be reverted.

* ♻️ Move high-level instructions to the end

In this way, they can reasonably reference the more low-level concepts

* 📚 Add instructions on cloning and the branch to use

* 📚 Revise instructions on prerequisites

* Do not state that pnpm must be available after Node.js installation
  (it is installed by corepack)
* Do not state that caddy is required; it is required only when
  rebuilding the API documentation for the server, which is not
  a task relevant to regular users.
* Do not strongly suggest that MCP users should be using the devenv.
* Windows: Add pointer to use Git Bash

* 📚 Remove unnecessary details on what the boostrap script does

* 📚 Update information on repository structure

* 📚 Add section on 'Development' to README
2026-02-19 14:29:07 +01:00
Pablo Alba a19860a77b Add nitrate popup 2026-02-19 12:08:47 +01:00
alonso.torres 360937f613 🐛 Fix problem with empty text 2026-02-19 12:00:05 +01:00
alonso.torres 426c8ea714 🐛 Fix type annotation for layoutCell property in plugins 2026-02-19 10:26:51 +01:00
alonso.torres 75e8d226d9 Add textBounds property in plugins 2026-02-19 10:26:51 +01:00
alonso.torres d42f5db1f0 🐛 Fix problem with horizontalSizing/verticalSizing in plugins 2026-02-19 10:26:51 +01:00
alonso.torres 03d0c62de1 🐛 Send a keep alive message in websocket connection 2026-02-19 10:26:51 +01:00
alonso.torres 698852cbeb 🐛 Fix permissions for mcp plugin 2026-02-19 10:26:51 +01:00
Belén Albeza f6d0414449 🔧 Use config flag for variants tests 2026-02-19 09:56:28 +01:00
Belén Albeza 4d05827fa9 🔧 Use disable-onboarding config flag instead of mocking get-profile in playwright 2026-02-19 09:56:27 +01:00
Belén Albeza 48fb9fa6ea Fix broken playwright tests 2026-02-19 09:56:27 +01:00
Dominik Jain 7cf88359fa 📚 Add section on 'Development' to README 2026-02-18 20:22:34 +01:00
Dominik Jain ea4c6c3998 📚 Update information on repository structure 2026-02-18 20:17:05 +01:00
alonso.torres cee974a906 🐛 Fix problem with tokens in plugins 2026-02-18 17:20:46 +01:00
alonso.torres 5cc5e8771e 🐛 Fix problem with tokens in plugins 2026-02-18 17:01:47 +01:00
Belén Albeza c74cf3fa37 🔧 Make config.js automatically mocked in playwright tests 2026-02-18 16:54:03 +01:00
Yaron Shahrabani 0e182cff18 🌐 Add translations for: Hebrew
Currently translated at 99.3% (2060 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-02-18 14:10:00 +00:00
Egor Filatov b4db1df62f 🌐 Add translations for: Russian
Currently translated at 78.7% (1634 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-02-18 14:09:55 +00:00
Alexis Morin 010074df4b 🌐 Add translations for: French (Canada)
Currently translated at 73.8% (1532 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-18 14:09:52 +00:00
Edgars Andersons 89f0e282ec 🌐 Add translations for: Latvian
Currently translated at 90.9% (1887 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-02-18 14:09:47 +00:00
Dominik Jain f8dd02169c 📚 Remove unnecessary details on what the boostrap script does 2026-02-18 11:14:21 +01:00
Dominik Jain ebdae2cf65 📚 Revise instructions on prerequisites
* Do not state that pnpm must be available after Node.js installation
  (it is installed by corepack)
* Do not state that caddy is required; it is required only when
  rebuilding the API documentation for the server, which is not
  a task relevant to regular users.
* Do not strongly suggest that MCP users should be using the devenv.
* Windows: Add pointer to use Git Bash
2026-02-18 11:11:25 +01:00
Dominik Jain 79d3469f36 📚 Add instructions on cloning and the branch to use 2026-02-18 10:56:21 +01:00
Aitor Moreno 7c1ddd3d7d Merge pull request #8382 from penpot/alotor-fix-components-propagation
🐛 Fix problem with text change component propagation and undo
2026-02-18 10:37:06 +01:00
Alejandro Alonso 4965f6d859 Merge pull request #8394 from penpot/elenatorro-fix-watch
🔧 Fix watch script
2026-02-18 10:11:44 +01:00
Elena Torro a3cd90da7f 🔧 Fix watch script 2026-02-18 09:57:25 +01:00
Andrey Antukh 942da56e78 Merge branch 'staging-render' into develop 2026-02-17 21:56:54 +01:00
Andrey Antukh 2b130c7e52 Merge branch 'staging' into staging-render 2026-02-17 21:54:23 +01:00
Alejandro Alonso a1a7f643ec Merge pull request #8390 from penpot/niwinz-staging-exporter-bundle
🐛 Fix exporter bundle deps issue with pnpm
2026-02-17 18:38:50 +01:00
Andrey Antukh 70013fde74 🐛 Fix exporter bundle deps issue with pnpm 2026-02-17 17:46:09 +01:00
Andrey Antukh 916107ce04 📎 Update pnpm-lock.yaml file on plugins module 2026-02-17 17:42:39 +01:00
Andrey Antukh 8eb5bd3dd8 🔧 Add minor adjustments to mcp build-types script 2026-02-17 17:42:39 +01:00
Andrey Antukh 5718698bff Update mcp api_types.yml file with latest plugins doc updates 2026-02-17 17:42:39 +01:00
Elena Torró c41b9214c5 Merge pull request #8387 from penpot/ladybenko-13415-fix-layout-lines
🐛 Fix grid overlay persisting after deleting board (wasm)
2026-02-17 17:38:52 +01:00
Elena Torró fb80c8f45b Merge pull request #8383 from penpot/superalex-fix-text-stroke-bounds
🐛 Fix text stroke bounds
2026-02-17 17:35:38 +01:00
Elena Torró 009dc4485a Merge pull request #8375 from penpot/alotor-fix-flex-layout-problem
🐛 Fix problem with flex layout auto sizing
2026-02-17 17:25:02 +01:00
alonso.torres b8f3bee3ac 🐛 Fix problem with flex layout auto sizing 2026-02-17 16:40:59 +01:00
Andrey AntukhandLuis de Dios f00b222262 Revert "♻️ Replace some components with DS ones" (#8384)
* Revert "♻️ Replace some components with DS ones"

This reverts commit 6879f54e5d.

* 📎 Restore missing styles

* 📎 Fix tests

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-02-17 16:23:04 +01:00
Elena Torró b28457860c Merge pull request #8388 from penpot/ladybenko-fix-cargo-clean
🔧 Run cargo clean on devenv start
2026-02-17 15:34:13 +01:00
Belén Albeza 23b268b414 🔧 Run cargo clean on devenv start 2026-02-17 15:08:30 +01:00
Elena Torró 32706a1460 Merge pull request #8386 from penpot/alotor-fix-watch-script
🐛 Fix watch script in wasm
2026-02-17 15:08:24 +01:00
Belén Albeza cd4b9ddd47 Add regression test for bug 13415 2026-02-17 14:32:09 +01:00
alonso.torres f0e3f1a319 🐛 Fix watch script in wasm 2026-02-17 14:27:36 +01:00
Dominik Jain 6a49b5df8c ♻️ Move high-level instructions to the end
In this way, they can reasonably reference the more low-level concepts
2026-02-17 13:16:21 +01:00
Alejandro Alonso afb252f42e 🔧 Migrate text editor v2 tests to wasm viewport 2026-02-17 12:59:53 +01:00
Belén Albeza 4185a7a6f3 🐛 Fix grid layout lines persisted after board is deleted 2026-02-17 12:58:15 +01:00
Dominik Jain 141847585e 🚧 Temporary workaround for fills/strokes being read-only
Add instructions to make the limintations.
Once #8357 is resolved, this can be reverted.
2026-02-17 12:51:48 +01:00
Alejandro Alonso 0dda7bd9ee 🐛 Fix text stroke bounds 2026-02-17 12:25:32 +01:00
alonso.torres 30106f8524 🐛 Fix problem with text change component propagation and undo 2026-02-17 11:54:33 +01:00
Oğuz Ersen 06bb2b98a9 🌐 Add translations for: Turkish
Currently translated at 99.8% (2070 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-02-17 11:10:12 +01:00
Alexis Morin 836616a05b 🌐 Add translations for: French (Canada)
Currently translated at 71.7% (1489 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-17 11:10:07 +01:00
Yaron Shahrabani 53d5fcd8d0 🌐 Add translations for: Hebrew
Currently translated at 97.1% (2015 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-02-17 11:09:59 +01:00
Stephan Paternotte 1c062b4cd0 🌐 Add translations for: Dutch
Currently translated at 99.8% (2070 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-02-17 11:09:56 +01:00
nautilusx 627854fbba 🌐 Add translations for: German
Currently translated at 94.6% (1964 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-02-17 11:09:52 +01:00
Nicola Bortoletto 73b7c0ee5d 🌐 Add translations for: Italian
Currently translated at 99.5% (2065 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-02-17 11:09:49 +01:00
Serhii Shvets 2b34767b2b 🐛 Fix Alt/Option to draw shapes from center point
Use the Alt/Option key stream (mouse-position-alt) instead of
the Command/Meta stream (mouse-position-mod) so the modifier
is actually detected during shape drawing.

When Alt is held, mirror the mouse point around the initial
click so that the click becomes the center of the drawn shape.
This aligns drawing behavior with resizing (transforms.cljs)
and with other design tools (Figma, Sketch, Illustrator).

Closes #8360

Signed-of-by: Serhii Shvets <justone128@gmail.com>
2026-02-17 11:02:40 +01:00
Andrey Antukh 082c8adb1d 📎 Update changelog 2026-02-17 10:29:05 +01:00
Melvin Laplanche 6cfaeb8a44 🎉 Add woff2 support on user uploaded fonts
Signed-off-by: Melvin Laplanche <noreply@melvin.la>
2026-02-17 10:29:05 +01:00
Andrey Antukh d192cf8893 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-17 10:01:42 +01:00
Andrey Antukh 7ef16a2b69 Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-17 10:01:06 +01:00
Andrey Antukh 137febcbab 📎 Clean changelog 2026-02-17 10:00:42 +01:00
Andrey Antukh e6fde82609 📎 Add 2.15 to changelog 2026-02-17 10:00:07 +01:00
Andrey Antukh ecc633efbe Merge remote-tracking branch 'origin/staging' into develop 2026-02-17 09:59:09 +01:00
Andrey Antukh f98c0bbd16 📎 Update changelog 2026-02-17 09:58:40 +01:00
Andrey Antukh dafad0c124 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-17 09:57:51 +01:00
Andrey Antukh 71ec51919e Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-17 09:55:16 +01:00
Elena Torró 1cb113dfeb Merge pull request #8379 from penpot/superalex-fix-grouped-component-shadow
🐛 Fix grouped component shadow
2026-02-17 09:54:37 +01:00
Elena Torró b45aec13ab Merge pull request #8378 from penpot/superalex-fix-focus-mode-simple-component
🐛 Fix focus mode for simple component
2026-02-17 09:53:27 +01:00
Luis de Dios 7f3212d5a4 🐛 Fix changelog to remove MCP (#8380) 2026-02-17 09:53:12 +01:00
Elena Torró 19592fadd8 Merge pull request #8374 from penpot/ladybenko-13385-fix-restore-version
🐛 Fix restore version not updating the canvas (wasm)
2026-02-17 09:50:05 +01:00
Luis de Dios 11690e7428 🐛 Fix copies in mcp server (#8370) 2026-02-17 09:21:09 +01:00
Andrés Moya 643cd6f61f 🐛 Add resolved value to tokens in plugins API (#8372) 2026-02-17 09:20:04 +01:00
Alonso Torres c32a336c50 🎉 Add MCP plugin embedded execution (#8368)
*  Add core changes for mcp server

*  Changes to plugins-runtime to add mcp extensions

*  Changes to MCP plugin

*  Changes post-review and ci fixes
2026-02-17 09:18:46 +01:00
Alejandro Alonso 0b2dfe7297 🐛 Fix grouped component shadow 2026-02-17 08:19:37 +01:00
Alejandro Alonso fe6fb0534c 🐛 Fix focus mode for simple component 2026-02-17 07:23:09 +01:00
Pablo Alba b87d7e3de0 Add create org button for nitrate 2026-02-16 19:43:26 +01:00
Alejandro Alonso f2d09a6140 🐛 Preserving selection when applying styles to selected text range 2026-02-16 17:39:30 +01:00
Eva Marco d09c909788 🐛 Fix input width on composite token form (#8365) 2026-02-16 17:08:33 +01:00
Belén Albeza 5ae2351e5a Add playwright test for bug 13385 2026-02-16 16:58:05 +01:00
Belén Albeza b5f4ce0a71 🐛 Fix canvas not being re-rendered after restoring a file version 2026-02-16 16:58:05 +01:00
Luis de Dios 166dc05ff2 🐛 Fix incorrect icons in grid view (#8373) 2026-02-16 16:39:39 +01:00
Yamila Moreno 9fa77cd06c 🔧 Add workflow_dispatch to staging, render and tag builds 2026-02-16 15:38:38 +01:00
Andrés Moya 619e2387dc 🐛 Fix applied tokens property names 2026-02-16 15:16:14 +01:00
Andrés Moya 813c804d45 🔧 Enhance schema validation of token application 2026-02-16 15:16:14 +01:00
Andrey Antukh 63f0c68977 Merge remote-tracking branch 'origin/main' into staging 2026-02-16 14:35:28 +01:00
Andrey Antukh 1f2a234458 📚 Update changelog 2026-02-16 14:32:28 +01:00
Andrey Antukh b281870c50 📚 Update changelog 2026-02-16 14:27:33 +01:00
Andrey Antukh 3909bc0fc1 Merge remote-tracking branch 'origin/main' into staging 2026-02-16 14:17:46 +01:00
Andrey Antukh b6427ecaac 🐛 Revert yetti upgrade
Because of regression introduced on undertow-core 2.3.19
2026-02-16 14:16:29 +01:00
Andrey Antukh e82319c49e Merge tag '2.13.2' 2026-02-16 14:13:51 +01:00
Yamila Moreno 8c5ce4d318 🔧 Add workflow_dispatch to develop builds 2026-02-16 12:22:09 +01:00
Luis de Dios 3c0df27fe0 🎉 Add MCP server to integrations section in dashboard (#8169) 2026-02-16 11:17:52 +01:00
Andrey Antukh a278d54429 🎉 Add copy as image to clipboard menu option (#8364)
*  Copy as image

Function to copy a board directly to the clipboard.
This is exposed on the Copy/Paste as... context menu.

The image is always copied at 2x to work well with wireframes. I tried
with and without Retina display and it is better in both scenarios.

Signed-off-by: Dalai Felinto <dalai@blender.org>

*  Add minor adjustments on promise creation

* 🔥 Remove prn from obj/reify macros

---------

Signed-off-by: Dalai Felinto <dalai@blender.org>
2026-02-16 11:17:02 +01:00
Andrey Antukh ce63bae92d Add better approach for error handling to obj/reify 2026-02-16 11:07:40 +01:00
Andrey Antukh a1cc016727 🔥 Remove prn from obj/reify macros 2026-02-16 11:05:57 +01:00
Pablo Alba 3d38aeb089 Add nitrate banner 2026-02-16 10:52:59 +01:00
Pablo AlbaandAndrey Antukh 43725a4abe 🐛 Fix unable to finish the create account form using keyboard (#8273)
* 🐛 Fix unable to finish the create account form using keyboard

* 📎 Prefer dom/click over dom/click!

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-02-16 10:49:51 +01:00
Andrey Antukh a0236e8c7e Merge pull request #8335 from penpot/dfelinto-download-font
 Add option for download used custom fonts
2026-02-16 10:44:57 +01:00
Andrey Antukh caccf72c7f Add better approach for error handling to obj/reify 2026-02-16 10:44:13 +01:00
Andrey Antukh 60ecb901b2 Make the obj/proxy object do not extend js/Object directly 2026-02-16 10:44:13 +01:00
Andrey Antukh fbf1240998 Add several optimizations for fonts zip download
Mainly prevent hold the whole zip in memory and uses an
unified response type, leavin frontend fetching the blob
data from the assets/storage subsystem.
2026-02-16 10:14:50 +01:00
Dalai Felinto c55c23c6dd Add option to download user uploaded custom fonts
Allow users download any of the manually installed fonts.
When there is more than one font in the family download as a .zip.

Signed-off-by: Dalai Felinto <dalai@blender.org>
2026-02-16 10:14:49 +01:00
Andrey Antukh d1d50138ed Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-16 10:00:46 +01:00
Andrey Antukh c63de58b7f Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-16 10:00:27 +01:00
Andrey Antukh 7301973655 🌐 Rehash and sync translation files 2026-02-16 09:41:49 +01:00
Sebastiaan Pasma e6990e996c 🌐 Add translations for: Dutch
Currently translated at 98.9% (2052 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-02-16 09:35:25 +01:00
Alejandro Alonso 82a6e73b0d 🌐 Add translations for: Yoruba
Currently translated at 56.5% (1172 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2026-02-16 09:35:24 +01:00
VKing9 a0fc14d4e8 🌐 Add translations for: Hindi
Currently translated at 96.3% (1998 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-02-16 09:35:24 +01:00
Anonymous a5dc3a4e2b 🌐 Add translations for: Croatian
Currently translated at 76.9% (1596 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2026-02-16 09:35:24 +01:00
Ņikita K. f60f259937 🌐 Add translations for: Latvian
Currently translated at 90.7% (1883 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-02-16 09:35:23 +01:00
Anonymous c669bd7327 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 86.8% (1801 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2026-02-16 09:35:23 +01:00
Anonymous c33a52ec51 🌐 Add translations for: Basque
Currently translated at 55.5% (1152 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/
2026-02-16 09:35:23 +01:00
Anonymous 9b2e847d99 🌐 Add translations for: Spanish
Currently translated at 97.1% (2015 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2026-02-16 09:35:22 +01:00
Anonymous d1e82c4d40 🌐 Add translations for: Hebrew
Currently translated at 95.6% (1984 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-02-16 09:35:22 +01:00
AlexTECPlayz 71ef2294e4 🌐 Add translations for: Romanian
Currently translated at 93.3% (1937 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2026-02-16 09:35:21 +01:00
Alejandro Alonso 3576c581a3 🌐 Add translations for: Arabic
Currently translated at 54.1% (1123 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2026-02-16 09:35:21 +01:00
Anonymous 13be3599b6 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 75.6% (1568 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2026-02-16 09:35:20 +01:00
Nicola Bortoletto 7b5e1f0196 🌐 Add translations for: Italian
Currently translated at 98.6% (2047 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-02-16 09:35:20 +01:00
Anonymous a483c99480 🌐 Add translations for: Polish
Currently translated at 54.3% (1127 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2026-02-16 09:35:20 +01:00
Radek Sawicki 60ebbd57c6 🌐 Add translations for: Polish
Currently translated at 54.3% (1127 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2026-02-16 09:35:19 +01:00
Црнобог e830689987 🌐 Add translations for: Serbian
Currently translated at 65.9% (1368 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2026-02-16 09:35:19 +01:00
Amerey.eu c8f9f49793 🌐 Add translations for: Czech
Currently translated at 76.7% (1591 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2026-02-16 09:35:19 +01:00
Anonymous f24f872c24 🌐 Add translations for: French
Currently translated at 95.2% (1976 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-02-16 09:35:19 +01:00
Renan Mayrinck 4a1a7e6e8f 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 67.0% (1391 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2026-02-16 09:35:18 +01:00
Denys Kisil 46ee8402fe 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 87.5% (1815 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-02-16 09:35:18 +01:00
The_BadUser b6d813dd51 🌐 Add translations for: Russian
Currently translated at 76.1% (1579 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-02-16 09:35:13 +01:00
Anonymous a9f674389a 🌐 Add translations for: German
Currently translated at 94.4% (1958 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-02-16 09:35:13 +01:00
william chen 78bdb13831 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 77.0% (1599 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-02-16 09:35:12 +01:00
im424 e548b2d863 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 77.0% (1599 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-02-16 09:35:12 +01:00
Alejandro Alonso 7543527fae 🌐 Add translations for: Hausa
Currently translated at 59.6% (1238 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2026-02-16 09:35:12 +01:00
Anonymous da488373e2 🌐 Add translations for: Turkish
Currently translated at 98.9% (2052 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-02-16 09:35:12 +01:00
Henrik Allberg 91e1c8299d 🌐 Add translations for: Swedish
Currently translated at 95.6% (1983 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-02-16 09:35:11 +01:00
Linerly adc661126a 🌐 Add translations for: Indonesian
Currently translated at 81.6% (1694 of 2074 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2026-02-16 09:35:11 +01:00
Hosted Weblate 3b07ff2613 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2026-02-16 09:15:59 +01:00
Alexis Morin 3aebff0799 🌐 Add translations for: French (Canada)
Currently translated at 61.2% (1266 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin 3b14acc93e 🌐 Add translations for: French (Canada)
Currently translated at 60.2% (1244 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin ad2db07123 🌐 Add translations for: French (Canada)
Currently translated at 55.8% (1154 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin 068ecc3847 🌐 Add translations for: French (Canada)
Currently translated at 55.4% (1146 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin c850fe2d4f 🌐 Add translations for: French (Canada)
Currently translated at 55.0% (1138 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin 59acf71255 🌐 Add translations for: French (Canada)
Currently translated at 52.4% (1083 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin d7c5e7798c 🌐 Add translations for: French (Canada)
Currently translated at 48.8% (1009 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin ee81459d87 🌐 Add translations for: French (Canada)
Currently translated at 44.6% (923 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin ea54ff4bd5 🌐 Add translations for: French (Canada)
Currently translated at 43.6% (902 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin 353998b989 🌐 Add translations for: French (Canada)
Currently translated at 42.3% (874 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin 6b1ae46116 🌐 Add translations for: French (Canada)
Currently translated at 40.8% (844 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Sandy S Kuo 727a0adf1f 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 77.5% (1603 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-02-16 09:15:49 +01:00
Alexis Morin b99e4e8954 🌐 Add translations for: French (Canada)
Currently translated at 37.5% (775 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Alexis Morin e8f893a668 🌐 Add translations for: French (Canada)
Currently translated at 36.6% (758 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Yaron Shahrabani 44964bc98b 🌐 Add translations for: Hebrew
Currently translated at 96.2% (1988 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-02-16 09:15:49 +01:00
Dogyeong 86f52a15a5 🌐 Add translations for: Korean
Currently translated at 12.1% (250 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-02-16 09:15:49 +01:00
Alexis Morin cc43426bb8 🌐 Add translations for: French (Canada)
Currently translated at 35.8% (740 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:49 +01:00
Dogyeong 5da2865158 🌐 Add translations for: Korean
Currently translated at 11.4% (237 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-02-16 09:15:49 +01:00
Alexis Morin eeb998409d 🌐 Add translations for: French (Canada)
Currently translated at 33.8% (699 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Alexis Morin b9ac8f7616 🌐 Add translations for: French (Canada)
Currently translated at 32.2% (667 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Nicola Bortoletto 626d6a285d 🌐 Add translations for: Italian
Currently translated at 99.5% (2057 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-02-16 09:15:48 +01:00
Alexis Morin 7159d00d75 🌐 Add translations for: French (Canada)
Currently translated at 29.3% (607 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Louis Chance 380c5382c7 🌐 Add translations for: French
Currently translated at 95.8% (1980 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-02-16 09:15:48 +01:00
Alexis Morin 28c244f8ee 🌐 Add translations for: French (Canada)
Currently translated at 24.1% (499 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Stas Haas af149afd08 🌐 Add translations for: German
Currently translated at 95.0% (1963 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-02-16 09:15:48 +01:00
Alexis Morin afd8add839 🌐 Add translations for: French (Canada)
Currently translated at 22.6% (467 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Andrey Antukh d6aad1d79b 🌐 Add translations for: French
Currently translated at 95.1% (1966 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-02-16 09:15:48 +01:00
Nicola Bortoletto 621e5161dc 🌐 Add translations for: Italian
Currently translated at 99.1% (2049 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-02-16 09:15:48 +01:00
Alexis Morin 00df74d602 🌐 Add translations for: French (Canada)
Currently translated at 21.7% (449 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Alexis Morin d31501ddb6 🌐 Add translations for: French (Canada)
Currently translated at 18.9% (392 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Louis Chance b0fda4cb06 🌐 Add translations for: French
Currently translated at 95.1% (1966 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-02-16 09:15:48 +01:00
Stephan Paternotte ce80c049cd 🌐 Add translations for: Dutch
Currently translated at 99.8% (2062 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-02-16 09:15:48 +01:00
Marius 2873bffff1 🌐 Add translations for: German
Currently translated at 93.7% (1937 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-02-16 09:15:48 +01:00
Alexis Morin 56c7ef8e99 🌐 Add translations for: French (Canada)
Currently translated at 18.7% (388 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:48 +01:00
Edgars Andersons 594551c16a 🌐 Add translations for: Latvian
Currently translated at 91.3% (1887 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-02-16 09:15:47 +01:00
Stephan Paternotte 00c34ecf12 🌐 Add translations for: Dutch
Currently translated at 99.8% (2062 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-02-16 09:15:47 +01:00
Alexis Morin 033a1f39fa 🌐 Add translations for: French (Canada)
Currently translated at 17.5% (363 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-02-16 09:15:47 +01:00
Oğuz Ersen aaab3e6b3e 🌐 Add translations for: Turkish
Currently translated at 99.8% (2062 of 2066 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-02-16 09:15:47 +01:00
Dominik Jain 7a52550889 Make clear that ExecuteCodeTool serialises automatically
LLMs sometimes decide to apply serialisation themselves, which is unnecessary,
and which this seeks to prevent.
2026-02-15 22:20:38 +01:00
Andrey Antukh 4a7b89a1da Merge pull request #8327 from penpot/niwinz-develop-rlimit-notifications
 Add proper mattermost notifications for rlimit rejects
2026-02-13 17:11:54 +01:00
Elena Torró 51782551cc Merge pull request #8346 from penpot/alotor-fix-problem-text-autogrouw
🐛 Fix problem with autogrow change while editing text
2026-02-13 15:25:35 +01:00
alonso.torres f60a4cd111 🐛 Fix problem with autogrow change while editing text 2026-02-13 14:52:40 +01:00
David Barragán Merino 1349789a7b 🔧 Fix the plugin style documentation build command 2026-02-13 14:20:34 +01:00
David Barragán Merino 8dbb169061 🔧 Fix the plugin style documentation build command 2026-02-13 14:20:01 +01:00
David Barragán Merino cc28bd44f6 🔧 Fix the plugin style documentation build command 2026-02-13 14:18:35 +01:00
Alejandro Alonso d9d4a99e1d 🔧 Migrate variants tests to wasm viewport 2026-02-13 14:13:03 +01:00
David Barragán Merino fe833c9e34 🔧 Disable observability for plugin docs and packages
This reverts commit a4f2641cc9.
2026-02-13 13:55:13 +01:00
Elena Torró 38ad24ea07 Merge pull request #8349 from penpot/alotor-fix-editor-selrect
🐛 Fix problem with text editor outline
2026-02-13 13:25:13 +01:00
Alejandro Alonso 8d225af13a Merge pull request #8351 from penpot/alotor-fix-create-rect-click
🐛 Fix problem when create click
2026-02-13 13:21:27 +01:00
Juanfran 449aa65f8d 🐛 Fix e2e tests for plugins 2026-02-13 13:17:08 +01:00
Andrey Antukh bd7f4dca3a 🐛 Fix rpc methods on plugins e2e tests 2026-02-13 13:17:08 +01:00
Andrey Antukh 1e7bef081a Allow self-signed certs on plugins e2e browser setup 2026-02-13 13:17:08 +01:00
Andrey Antukh 12bc3ac9ed Update default cors headers 2026-02-13 13:17:08 +01:00
Elena Torró 80a3f4cd60 Merge pull request #8350 from penpot/superalex-migrate-tests-to-wasm-viewport-3
🔧 Migrate inspect tab tests to wasm viewport
2026-02-13 13:06:14 +01:00
alonso.torres 3ea0a781f1 🐛 Fix problem when create click 2026-02-13 12:38:33 +01:00
alonso.torres 35abf8a179 🐛 Fix problem with text editor outline 2026-02-13 12:23:05 +01:00
Sagar cfcebf59d5 🐛 Make S3Client and S3Presigner use identical credential resolution (#8316) 2026-02-13 12:21:05 +01:00
Andrey Antukh cf43ac23a1 Merge pull request #8340 from penpot/hiru-fix-plugins-api-tokens
🐛 Fix problems about applying tokens to shapes with plugins
2026-02-13 12:18:29 +01:00
Alejandro Alonso 7e9fb0742d 🔧 Migrate inspect tab tests to wasm viewport 2026-02-13 12:09:31 +01:00
Elena Torró 39b3767203 Merge pull request #8348 from penpot/superalex-fix-non-existent-google-font
🐛 Fix non existent google font
2026-02-13 12:08:18 +01:00
Alejandro Alonso 23333aa3c3 Merge pull request #8347 from penpot/elenatorro-13386-fix-flex-absolute-index
🐛 Fix absolute z-index values on layout children
2026-02-13 12:06:45 +01:00
Alejandro Alonso 684e2b6950 🐛 Fix non existent google font 2026-02-13 11:59:59 +01:00
Elena Torro fd6f70a740 🐛 Fix absolute z-index values on layout children 2026-02-13 11:41:16 +01:00
Elena Torro b892cc9b14 🔧 Refactor shape base props to use transmute 2026-02-13 11:28:18 +01:00
Belén Albeza 50ddf5e628 🔧 Add integration test for bug 13305 2026-02-13 10:57:27 +01:00
Belén Albeza 75a4102637 🐛 Fix resize board to fit (wasm) 2026-02-13 10:57:27 +01:00
David Barragán Merino d7203ef24c 🔧 Fix the plugin bundle build command 2026-02-13 09:39:12 +01:00
David Barragán Merino 61f3e090da 🔧 Fix the plugin bundle build command 2026-02-13 09:38:39 +01:00
David Barragán Merino fda09b02b9 🔧 Fix the plugin bundle build command 2026-02-13 09:37:22 +01:00
Elena Torró 8f478aa6e5 Merge pull request #8325 from penpot/superalex-fix-wasm-forcing-url-param
🐛 Fix forcing wasm via url param
2026-02-12 17:46:50 +01:00
Dominik Jain 08fc6fe917 Improve description of token values 2026-02-12 17:45:50 +01:00
Alejandro Alonso 95e1efa5ff 🐛 Fix forcing wasm via url param 2026-02-12 17:35:02 +01:00
Dominik Jain 926d573d3e 🚧 Temporary workaround for Token resolvedValue not working
Instruct LLM to not use this property.
To be reverted once #8341 is fixed.
2026-02-12 17:24:44 +01:00
Andrés Moya a23ca6a1cb 🐛 Fix applied tokens reading in shape proxy 2026-02-12 17:14:16 +01:00
Eva Marco d07f568ba2 🐛 Avoid modifying shape by apply negative tokens to border radius (#8336) 2026-02-12 17:01:36 +01:00
Andrés Moya c626634610 🐛 Detect empty font-family 2026-02-12 16:04:23 +01:00
Andrés Moya 11eedd0368 🐛 Patch alternative ways of applying tokens to shapes 2026-02-12 16:01:55 +01:00
Florian Schroedl 375608b44b ⬆️ Update tokenscript interpreter to 0.26.0 and add CSS color schemas
Regenerate schemas.js with preset:cssColors to support CSS color constants.
2026-02-12 14:14:45 +01:00
Elena Torró 97d24b190f Merge pull request #8334 from penpot/superalex-migrate-tests-to-wasm-viewport
🔧 Migrate straightforward tests to user the wasm viewport
2026-02-12 14:03:06 +01:00
Alejandro Alonso 434ac0556a 🔧 Migrate straightforward tests to user the wasm viewport 2026-02-12 13:29:13 +01:00
Dominik Jain bac04f8a73 🚧 Temporary workaround for sizing options not working
Add instructions explaining that FlexLayout sizing options do not work.
Relates to https://github.com/penpot/penpot-mcp/issues/39
2026-02-12 12:37:24 +01:00
Dominik Jain b4e815e787 📎 Fix spelling errors 2026-02-12 12:36:51 +01:00
Andrey Antukh 12e5d8d8c4 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-12 11:00:56 +01:00
Andrey Antukh 04a3126856 Merge remote-tracking branch 'origin/main' into staging-render 2026-02-12 11:00:38 +01:00
Elena Torró 2f71663470 Merge pull request #8245 from penpot/elenatorro-13047-setup-embedded-text-editor
🔧 Set up embedded editor
2026-02-12 10:05:39 +01:00
Andrey Antukh 43cb313cd7 Merge pull request #8310 from oraios/mcp-tokens
 MCP improvements to enable UC2, design token handling
2026-02-12 09:47:32 +01:00
Elena Torró 0b199c606a Merge pull request #8331 from penpot/ladybenko-add-wasm-config-playwright-helper
🔧 Add helper utils to mock config flags for WasmWorkspacePage (e2e)
2026-02-12 09:45:03 +01:00
Aitor Moreno 54f63c5dc5 ♻️ Refactor minor things 2026-02-12 09:34:21 +01:00
Elena Torro a14c36e996 📚 Add embedded text editor MVP documentation 2026-02-12 09:34:20 +01:00
Elena Torro 2b525f0f48 🔧 Set up embedded editor 2026-02-12 09:34:20 +01:00
Belén Albeza fd6ff04e90 🔧 Add helper utils to mock config flags for WasmWorkspacePage (e2e) 2026-02-12 09:25:08 +01:00
eps-epsiloneridani dbb0aa8ce2 📚 Update recommended-settings.md (#8330)
Got rid of a stray quotation mark

Signed-off-by: eps-epsiloneridani <162043859+eps-epsiloneridani@users.noreply.github.com>
2026-02-12 09:19:10 +01:00
Andrey Antukh 12822833f6 Merge pull request #8301 from eureka928/fix/4513-shift-arrow-color-inputs
🐛 Add Shift/Alt arrow key stepping to color picker inputs
2026-02-12 08:26:05 +01:00
eureka928 307ae374fe ♻️ Unify color picker input handlers by treating alpha as a property
Eliminate duplicated on-change-opacity and on-key-down-opacity handlers
by routing alpha through apply-property-change, and extract shared
stepping logic into on-key-down-step.

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-02-12 08:25:37 +01:00
eureka928 7d7dbd4662 🐛 Add Shift/Alt arrow key stepping to color picker inputs (#4513)
Color picker numeric inputs (R, G, B, H, S, V, Alpha) now support
Shift+Arrow for ×10 steps and Alt+Arrow for ×0.1 steps, matching
the behavior of numeric inputs elsewhere in the application.

Signed-off-by: eureka928 <meobius123@gmail.com>
2026-02-12 08:25:37 +01:00
Alejandro Alonso 139d4ba13c Merge pull request #8328 from penpot/elenatorro-13311-fix-multiple-strokes-blending
🐛 Fix stroke color aliasing when a shape has multiple strokes
2026-02-12 07:05:44 +01:00
Elena Torro 0cb5c16823 🐛 Fix fallback font 2026-02-12 06:43:52 +01:00
Elena Torro 4ed1a544f8 🐛 Fix stroke color aliasing when a shape has multiple strokes 2026-02-12 06:43:52 +01:00
Elena Torró 566ac67fc9 Merge pull request #8324 from penpot/azazeln28-fix-editor-fills
🐛 Fix text editor issues
2026-02-11 16:37:20 +01:00
Juanfran 394d597736 Add enhacements to plugins build mechanism (#8326)
* 🐛 Fix plugin code build

* 🔧 Update editor.defaultFormatter to new Prettier

* 🐛 Fix lint issues in create-palette-plugin

* 🐛 Add missing run in pnpm init script for plugins
2026-02-11 15:28:33 +01:00
Aitor Moreno b2231e520c 📚 Add best practices to text editor README.md 2026-02-11 13:09:56 +01:00
Aitor Moreno e722e17b10 🐛 Fix paragraph styles not being applied 2026-02-11 12:49:20 +01:00
Aitor Moreno 755d720b34 🐛 Fix text editor fills not being updated 2026-02-11 12:29:03 +01:00
Alejandro Alonso d991d59852 Merge pull request #8318 from penpot/elenatorro-13311-fix-multiple-fills-blending
🐛 Fix fill aliasing when a shape has multiple fills
2026-02-11 11:37:43 +01:00
Dominik Jain 7eb9a207f5 Change PenpotUtils.findShapes to search on all pages by default
This matches the behaviour of findShape, more closely aligning with
the LLM's expectations (given the lack of concrete information in
the instructions)
2026-02-11 11:35:10 +01:00
Dominik Jain 8ac17604fd Improve information on component instances
* Add information on detachment
* Add information on remove behaviour in component instances
2026-02-11 11:35:10 +01:00
Elena Torro eede023d6b 🐛 Fix fill aliasing when a shape has multiple fills 2026-02-11 11:21:08 +01:00
Belén Albeza ccd42852b7 🐛 Fix token not being highlighted (wasm) 2026-02-11 11:17:27 +01:00
Alejandro Alonso a2f7ae549e Merge pull request #8312 from penpot/elenatorro-13256-sync-text-selection
🔧 Hide text color from selected text
2026-02-11 11:02:35 +01:00
Alejandro Alonso 6f74d458a8 🐛 Adding lost file for render e2e testing get-file-stroke-styles.json 2026-02-11 10:47:50 +01:00
Alejandro Alonso 8d033de145 Merge pull request #8299 from penpot/elenatorro-13242-review-performance
🔧 Improve layout performance
2026-02-11 10:45:40 +01:00
Dominik Jain f5afcde0de Improve shapeStructure
* Add information on component instance (component id, name; main instance id)
* Improve JSON result order (children should come last)
2026-02-11 10:45:22 +01:00
Dominik Jain b6dfdc23cd Update information on TokenProperty 2026-02-11 10:45:22 +01:00
Dominik Jain a5a084cf0f Update API type information based on current repo state 2026-02-11 10:45:22 +01:00
Dominik Jain 1546025814 Avoid certain <ul> elements with single <li> generating bullets 2026-02-11 10:45:22 +01:00
Dominik Jain 8de510d1c6 🐛 Fix PenpotAPIDocsProcessor not requiring the url parameter
* Add additional constant for the PROD url
* Adapt the debug function to use a URL
* Improve logging
2026-02-11 10:45:10 +01:00
Elena Torró 2e77c09ca5 Merge pull request #8309 from penpot/superalex-fix-stroke-dot-dash-mix
🐛 Fix dot strokes
2026-02-11 10:37:46 +01:00
Elena Torró 47346e478e Merge pull request #8303 from penpot/superalex-fix-stroke-opacity-for-boards
🐛 Fix stroke opacity for boards
2026-02-11 10:05:47 +01:00
Andrey Antukh 89cd4d820c 🔥 Remove mcp from compose 2026-02-11 09:13:24 +01:00
Alejandro Alonso f32c377f17 🐛 Fix stroke opacity for boards 2026-02-11 09:08:03 +01:00
Andrey Antukh 8693623b13 📎 Update SECURITY.md file 2026-02-11 08:11:04 +01:00
Alejandro Alonso 97f01c646d 🎉 Improve multiple emoji E2E test 2026-02-11 07:36:22 +01:00
Alejandro Alonso eea1d3c0a5 🎉 Improve updating canvas background E2E test 2026-02-11 07:19:22 +01:00
Alejandro Alonso 9eef4de87d 🐛 Fix dot/dahs/mixed strokes 2026-02-11 07:08:28 +01:00
Andrey Antukh f4d07a3c36 ⬆️ Update pnpm on frontend and plugins modules 2026-02-10 19:02:32 +01:00
Francis Santiago 15fa6206e2 Merge pull request #8286 from penpot/fc-fix-ipv6-fronted
🎉 Enable IPv6 support for docker images
2026-02-10 16:01:06 +01:00
Francis Santiago 3281819283 🎉 Enable IPv6 support for docker images 2026-02-10 15:52:33 +01:00
Andrés MoyaandAndrey Antukh 41f29767db 🐛 Fix configuration of poc-tokens-plugin (#8314)
* 🐛 Fix configuration of poc-tokens-plugin

* 📎 Add missing changes on poc-tokens-pligin tsconfig

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-02-10 15:18:11 +01:00
Dominik Jain 76289df32c Establish compatibility with new member anchors (h3 instead of a tag) 2026-02-10 14:03:40 +01:00
Andrey Antukh 920fbe34ad 🐛 Fix invalid deps passed to http management routes service 2026-02-10 13:46:29 +01:00
Elena Torro 187d1118c0 🔧 Hide text color from selected text 2026-02-10 13:15:55 +01:00
Dominik Jain a674b5f914 📚 Add instructions on running only the docs server 2026-02-10 12:53:20 +01:00
Dominik Jain 71507fb9b7 ♻️ Adjust ConfigurationLoader to use markdown file instead of yml 2026-02-10 12:35:44 +01:00
Dominik Jain 024aedc3ca ♻️ Convert prompt content to markdown format 2026-02-10 12:35:44 +01:00
Dominik Jain 44657c95df ♻️ Rename prompts.yml -> initial_instructions.md 2026-02-10 12:35:44 +01:00
Dominik Jain d4d5009a3d Improve prompts on token application 2026-02-10 12:35:44 +01:00
Dominik Jain bb4d0322d8 🚧 Temporarily add ts-ignore statements
This shall be reverted once the new API types are published
2026-02-10 12:35:44 +01:00
Dominik Jain 56e369a1c0 Add helper functions for token exploration
Extend PenpotUtils with helper functions for token exploration/discovery
and describe them in the system prompt
2026-02-10 12:35:44 +01:00
Dominik Jain 6b277956b9 Add information on clone() method 2026-02-10 12:35:44 +01:00
Dominik Jain e9a56c9d9f Shorten design token instructions 2026-02-10 12:35:44 +01:00
Dominik Jain 8d90edcc2f Add instructions on design tokens 2026-02-10 12:35:44 +01:00
Dominik Jain 8186f3c87c 📚 Remove misleading information from README
The types build is not part of the bootstrap, and it is not
relevant to regular users (only to developers).

Information on how to apply it is now in types-generator/README.md
2026-02-10 12:35:44 +01:00
Dominik Jain d7282518c4 📚 Improve usage documentation of API type generator script 2026-02-10 12:35:44 +01:00
Dominik Jain 467eb3c333 Update API docs to include token-related types 2026-02-10 12:35:44 +01:00
Dominik Jain d2299f83ec Apply bash in build scripts explicitly (Win compatibility) 2026-02-10 12:35:44 +01:00
Andrey Antukh 11a283916d Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-10 11:58:27 +01:00
Andrey Antukh f08700945a Merge remote-tracking branch 'origin/staging' into develop 2026-02-10 11:58:09 +01:00
Andrey Antukh 59711a1cf8 📎 Update changelog 2026-02-10 11:57:01 +01:00
Aitor Moreno e9b2e9e818 🚑 Hot fix for text editor internal error 2026-02-10 11:10:16 +01:00
Belén Albeza c4aa51bc01 🐛 Fix permanent blur when switching pages 2026-02-10 10:59:47 +01:00
Belén Albeza 1c270ac9c6 Remove leftover println in render code 2026-02-10 10:59:47 +01:00
Andrey Antukh 06e5825c8a 🐛 Add proper input checking to font related RCP method 2026-02-10 10:36:57 +01:00
Andrey Antukh e3dfa69011 Make plugins devserver to be able run inside devenv 2026-02-10 08:29:24 +01:00
Juanfran 96b682aa12 ♻️ Remove Nx and rely on pnpm monorepo features 2026-02-10 08:29:24 +01:00
Juanfran 45d04942cc Add example ui storybook 2026-02-10 08:29:24 +01:00
Juanfran 07055b53d1 ⬆️ Update plugins dependencies 2026-02-10 08:29:24 +01:00
Andrey Antukh d30387eb77 Backport docker images changes from develop 2026-02-09 19:21:30 +01:00
Andrey Antukh 33fd672c21 Backport MCP related changes from develop (#8306) 2026-02-09 18:00:43 +01:00
Andrey Antukh dd7038bdad 📎 Fix fmt issue on frontend code 2026-02-09 17:38:40 +01:00
Andrey Antukh 5ec345162a Add mcp plugin into the frontend bundle 2026-02-09 17:38:40 +01:00
Andrey Antukh 0027e9031a Make mcp env vars to use the same convention as penpot 2026-02-09 17:38:40 +01:00
Pablo Alba 5d3ccbc8b4 Add managed profiles endpoint to nitrate api (#8292) 2026-02-09 15:52:18 +01:00
Andrés Moya 1a1c351466 🐛 Fix dependency 2026-02-09 15:06:39 +01:00
Andrés MoyaandAndrey Antukh 5b5f22a8c6 🎉 Add tokens to Penpot Plugins API (#7756)
* 🎉 Add tokens to plugins API documentation

And add poc plugin example

* 📚 Document better the tokens value in plugins API

* 🔧 Refactor token validation schemas

* 🔧 Use automatic validation in token proxies

* 🔧 Use schemas to validate token creation

* 🔧 Use multi schema for token value

* 🔧 Use schema in token api methods

* 🐛 Fix review comments

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-02-09 14:18:31 +01:00
Andrey Antukh ac1c3ff184 Merge branch 'staging-render' into develop 2026-02-09 14:14:02 +01:00
Andrey Antukh 43cd92c76d Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-09 14:12:55 +01:00
Elena Torró cf2b40a097 Merge pull request #8302 from penpot/azazeln28-issue-13124-text-not-restored-undoing
🐛 Fix text not restored on ctrl+z
2026-02-09 13:41:43 +01:00
Aitor Moreno b72959544c 🐛 Fix text not restored on ctrl+z 2026-02-09 13:29:31 +01:00
Andrey Antukh a7b2e98b8e ⬆️ Use latest imagemagick version on docker images 2026-02-09 13:19:26 +01:00
Andrey Antukh d979894872 Add libxml2 dep to imagemagick dockerfile 2026-02-09 12:27:44 +01:00
Alejandro Alonso 3d20fc508d 🐛 Fix image magick info call (#8300) 2026-02-09 12:26:42 +01:00
Elena Torro 969666b39b 🔧 Simplify view interaction log message
Remove zoom_changed from log output as it's no longer needed
for debugging after the tile optimization changes.
2026-02-09 11:44:50 +01:00
Yamila Moreno d953222eb4 🔧 Add CI for MCP server (#8293) 2026-02-09 11:25:24 +01:00
Elena Torró b3faa985ce Merge pull request #8291 from penpot/superalex-fix-dashboard-navigation
🐛 Fix dashboard navigation from workspace
2026-02-09 09:59:11 +01:00
Elena Torro a8322215dd 🔧 Optimize pan/zoom tile handling
- Add incremental tile update that preserves cache during pan
- Only invalidate tile cache when zoom changes
- Force visible tiles to render synchronously (no yielding)
- Increase interest area threshold from 2 to 3 tiles
2026-02-09 09:38:01 +01:00
Elena Torro e1ce97a2b4 🔧 Prioritize visible tiles over interest-area tiles
Partition pending tiles into 4 groups by visibility and cache status.
Visible tiles are processed first to eliminate empty squares during
pan/zoom. Cached tiles within each group are processed before uncached.
2026-02-09 09:38:01 +01:00
Elena Torro 2ccd2a6679 🔧 Use HashSet for grid layout children lookup
HashSet provides O(1) contains() vs Vec's O(n), improving
child lookup performance in grid cell data creation.
2026-02-09 09:38:01 +01:00
Elena Torro 2d9a2e0d50 🔧 Use swap_remove in flex layout distribution
swap_remove is O(1) vs remove's O(n) when order doesn't matter.
These loops iterate backwards, so swap_remove is safe.
2026-02-09 09:38:01 +01:00
Elena Torro 216d400262 🔧 Prevent duplicate layout calculations
Use HashSet for layout_reflows to avoid processing the same
layout multiple times. Also use std::mem::take instead of
creating a new Vec on each iteration.
2026-02-09 09:37:58 +01:00
Elena Torro c87ffdcd30 🔧 Add forward children iterator for flex layout
Avoid Vec allocation + reverse for reversed flex layouts.
The new children_ids_iter_forward returns children in original order,
eliminating the need to collect and reverse.
2026-02-09 09:35:04 +01:00
Elena Torro 8ef6600cdc 🔧 Return HashSet from update_shape_tiles
Avoid final collect() allocation by returning HashSet directly.
Callers already use extend() which works with both types.
2026-02-09 09:35:04 +01:00
Elena Torro a3764b9713 🔧 Avoid clone in rebuild_touched_tiles
Use std::mem::take instead of clone to avoid HashSet allocation.
The set was cleared anyway by clean_touched(), so take() is safe.
2026-02-09 09:35:03 +01:00
Alejandro Alonso e5cdb5b163 Merge pull request #8290 from penpot/alotor-fix-alt-duplicate
🐛 Fix problem with alt+move for duplicate shapes
2026-02-09 06:33:13 +01:00
David Barragán Merino a4f2641cc9 🔧 Enable observability for plugin docs and packages 2026-02-06 18:01:11 +01:00
Alejandro Alonso a164a1bab3 🐛 Fix dashboard navigation from workspace 2026-02-06 12:58:56 +01:00
alonso.torres a0cbb392af 🐛 Fix problem with alt+move for duplicate shapes 2026-02-06 12:20:43 +01:00
Alejandro Alonso ccfee34e76 Merge pull request #8289 from penpot/niwinz-staging-exporter-fix
🐛 Fix issue with pdf render on exporter
2026-02-06 11:40:18 +01:00
Andrey Antukh 989eb12139 🔥 Remove merge conflict from plugins api ns 2026-02-06 11:38:36 +01:00
Eva Marco a5e36dbb3d 🐛 Fix broken attribute on numeric input (#8250)
* 🐛 Fix broken attribute on numeric input

* 🐛 Fix tooltip position
2026-02-06 11:32:16 +01:00
Alejandro Alonso 8acd031ab2 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-06 11:23:50 +01:00
Andrey Antukh 6f3f2f9a71 🐛 Fix issue with pdf render on exporter
When paired with release build penpot app
2026-02-06 11:19:56 +01:00
Elena Torro a7c1de6478 🐛 Fix lazy load intersection on dragging at the beginning 2026-02-06 10:59:05 +01:00
Elena Torro 184487f568 🐛 Fix lazy load intersection on dragging at the beginning 2026-02-06 10:53:11 +01:00
Andrey Antukh c00d512193 Add the concept of version to plugins
And make mcp plugin version 2
2026-02-06 09:42:59 +01:00
alonso.torres af5dbf2fbc 🐛 Set objects modified instead of modif-tree 2026-02-06 09:34:58 +01:00
Alejandro Alonso 7c7e32d85f 🐛 Fix grid lines 2026-02-06 09:34:58 +01:00
Andrey Antukh 2ccb33ba89 📎 Add missing for-update for the migration 145 2026-02-05 18:12:11 +01:00
Andrey Antukh ee88ee63a2 Add data migration for fix plugins data on profiles 2026-02-05 18:08:28 +01:00
alonso.torres fd3d549f9c Batch text layout updates 2026-02-05 17:29:43 +01:00
alonso.torres 53c2acb3e6 🐛 Fix several problems with layouts and texts 2026-02-05 17:29:43 +01:00
Belén Albeza 8a72eb64c3 Add integration test for 13267 2026-02-05 16:37:21 +01:00
alonso.torres 1d45ca7019 🐛 Fix problem propagating geometry changes to instances 2026-02-05 16:37:21 +01:00
Eva Marco f961f9a123 🐛 Fix several bugs (#8267)
* ♻️ Remove rename warning

* 🐛 Fix opacity value
2026-02-05 11:34:14 +01:00
Eva Marco dda3377596 🐛 Allow detach broken token from input (#8242)
* 🐛 Allow detach broken token from input

* 🐛 Fix multiselection on multiple token applied

* ♻️ Remove detach-token new fn
2026-02-05 11:28:47 +01:00
Andrey Antukh 17935443df Move all tokenscript related adaptations to a separared package 2026-02-05 09:45:55 +01:00
Florian Schroedl 150d57b1eb Add tokenscript MVP 2026-02-05 09:45:55 +01:00
Alejandro Alonso ad5e8ccdb3 🐛 Fix pdf sizing issue on export (#8274) 2026-02-05 09:23:14 +01:00
Andrey Antukh 490619119e 🐛 Use correct listen address for mcp server 2026-02-04 18:57:53 +01:00
Belén Albeza 834b513562 🔧 Fix typo in workspace spec (#8272) 2026-02-04 17:05:49 +01:00
Andrey Antukh 1656fefdc9 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-04 16:23:46 +01:00
Andrey Antukh 7f318bb110 Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-04 16:22:13 +01:00
Andrey Antukh 44c7d3fbd6 Backport .github workflows from develop 2026-02-04 16:21:19 +01:00
Andrey Antukh 3d50aa6cb2 ⬆️ Update imagemagick version 2026-02-04 16:21:19 +01:00
Andrey Antukh 06afd94a74 ⬆️ Update backend dependencies (mainly bugfixes) 2026-02-04 16:21:19 +01:00
Andrey Antukh e7d9dca55e ⬆️ Update jdk and node on devenv and other images 2026-02-04 16:21:19 +01:00
Andrey Antukh c14ccc18b8 Import mcp from develop 2026-02-04 16:21:19 +01:00
Andrey Antukh ca4d00df69 🐛 Fix latest error report related migration 2026-02-04 15:36:07 +01:00
Andrey Antukh 9667477d6b 🐛 Add missing dep for rpc routes on backend 2026-02-04 15:26:02 +01:00
Alejandro Alonso 485005477e 🐛 Fix WasmWorkspacePage import 2026-02-04 14:02:38 +01:00
Alejandro Alonso 86ca260ea2 Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-04 13:50:13 +01:00
Andrey Antukh d80ba1856a Add several improvements to frontend error reporting
*  Add major improvement on error handling

*  Add the ability to store frontend reports

* 📎 Add PR feedback changes
2026-02-04 12:45:38 +01:00
Alejandro Alonso ebb7d01bc9 🐛 Fix entering decimal values in dimension fields causes internal server error (#8263) 2026-02-04 12:44:19 +01:00
Andrey Antukh a1bfb2781e 📎 Update mcp readme 2026-02-04 12:22:36 +01:00
Andrey Antukh 08e8787568 Add mcp types generator build script 2026-02-04 12:22:36 +01:00
Andrey Antukh da55653844 Add integration tests for mcp 2026-02-04 12:22:36 +01:00
Andrey Antukh 11f2323057 Add mcp to default compose template 2026-02-04 12:22:36 +01:00
Andrey Antukh ae0f5e2bb9 🐛 Fix subpath support on plugins 2026-02-04 12:22:36 +01:00
Andrey Antukh 1fff1f9506 Add mcp dockerfile 2026-02-04 12:22:36 +01:00
Andrey Antukh 61d7dd3167 Update devenv with mcp required dependencies 2026-02-04 12:22:36 +01:00
Dominik Jain 880b9b61c4 🎉 Integrate mcp repository
Original repository: https://github.com/penpot/penpot-mcp
Imported commit: fcfa67e908fc54e23a3a3543dee432472dc90c5d
2026-02-04 12:22:36 +01:00
Yamila Moreno 307dae9f61 💄 Remove access logs for /readyz 2026-02-04 10:54:07 +01:00
Xaviju 0f0ad4f161 🐛 Remove path from state when removing tokens (#8252)
* 🐛 Remove path from state when removing tokens

* ♻️ Improve path edition legibility

* ♻️ Fix path delete on change set
2026-02-04 10:15:46 +01:00
Alejandro Alonso 24c8fc484f 🐛 Fix Internal Error when adding a new text layer and trying to go back to Dashboard without saving 2026-02-04 10:01:10 +01:00
Eva Marco d6831e9b48 ♻️ Restore warning on name change in generic form (#8260) 2026-02-03 14:08:35 +01:00
Pablo Alba 138df7c958 🐛 Fix remove fill affects different element than selected (#8233) 2026-02-03 13:17:54 +01:00
alonso.torres ef2bdf86d8 Add event to create shape in plugins 2026-02-03 13:09:58 +01:00
alonso.torres 512a31d375 Add naturalChildOrdering flag to Plugin's API 2026-02-03 13:09:58 +01:00
Alejandro Alonso bc16b8ddc3 Merge pull request #8198 from penpot/ladybenko-13176-playwright-wasm
🔧 Migrate workspace tests to user the wasm viewport
2026-02-03 13:00:10 +01:00
Alejandro Alonso b07c98faa5 Merge pull request #8259 from penpot/superalex-improve-shadow-rendering
🎉 Improving shadow rendering performance
2026-02-03 12:59:49 +01:00
Alejandro Alonso 25aff100cf 🎉 Add shadows playground for render wasm 2026-02-03 12:44:43 +01:00
Alejandro Alonso 5be887f10b 🎉 Improve plain shape calculation 2026-02-03 12:44:43 +01:00
Alejandro Alonso f7403935c8 🎉 Improve shadows rendering performance 2026-02-03 12:33:05 +01:00
Andrey Antukh 7d09d930fe 📚 Update changelog 2026-02-03 11:13:46 +01:00
Belén Albeza 79be3ab7df 🔧 Fix text editor flaky tests 2026-02-03 10:39:38 +01:00
Andrey Antukh 717a048b73 📎 Add fmt fix on frontend 2026-02-03 09:37:19 +01:00
Andrey Antukh cbd90ff970 📎 Comment problematic code on frontend 2026-02-03 09:31:26 +01:00
Andrey Antukh c99fac000a Merge remote-tracking branch 'origin/staging-render' into develop 2026-02-03 09:30:16 +01:00
andrés gonzález 79e5d2f4cd 📚 Change link to post at SH guide (#8247) 2026-02-03 08:27:17 +01:00
Andrey Antukh 1325584e1a Merge remote-tracking branch 'origin/staging' into staging-render 2026-02-03 08:24:04 +01:00
Andrey Antukh 0d9b7ca696 Merge tag '2.13.0-RC11' 2026-02-03 08:23:27 +01:00
Andrey Antukh d215a5c402 Merge tag '2.13.0-RC10' 2026-02-03 08:22:50 +01:00
Belén Albeza 629649aca6 🔧 Fix config playwright syntax 2026-02-02 16:25:16 +01:00
Belén Albeza cc326f23cf 🔧 Adjust timeout of websocket readiness (playwright) 2026-02-02 16:16:59 +01:00
Belén Albeza 2c4efc6b53 🔧 Fix onboarding test 2026-02-02 16:16:58 +01:00
Belén Albeza 4d5c874b91 🔧 Fix typography token test 2026-02-02 16:16:58 +01:00
Belén Albeza e3b97638b4 🔧 Fix broken / flaky tests 2026-02-02 16:16:58 +01:00
Belén Albeza daedc660b9 🔧 Migrate workspace tests to user the wasm viewport 2026-02-02 16:16:58 +01:00
Elena Torró 7681231d8f Merge pull request #8246 from penpot/azazeln28-test-more-text-editor
🔧 Add more Text Editor v2 tests
2026-02-02 15:09:18 +01:00
Aitor Moreno 07b9ef0fd6 🔧 Add more v2 text editor tests 2026-02-02 09:35:28 +01:00
Alejandro Alonso 2ae68d5752 Merge pull request #8244 from penpot/alotor-fix-modifiers-propagation
🐛 Fix problem with modifiers propagation
2026-01-29 17:34:36 +01:00
alonso.torres 913672e5c5 🐛 Fix problem with modifiers propagation 2026-01-29 17:15:01 +01:00
Xaviju 91671afb7a 🐛 Update switch checked state on change prop (#8235) 2026-01-29 13:13:50 +01:00
Eva Marco 838194f9e5 ♻️ Refactor code to reduce duplicates (#8213) 2026-01-29 12:28:05 +01:00
Alejandro Alonso 8c25fb00ac 🐛 Fix auto width/height texts on variant swithching 2026-01-29 12:25:38 +01:00
Alejandro Alonso 6a84215911 🐛 Fix stroke weight visually different with different levels of zoom 2026-01-29 12:18:26 +01:00
Xaviju 68cf2ecc57 🐛 Break long token names in remapping modal (#8241) 2026-01-29 11:04:36 +01:00
Xaviju 3e4f70f37b 🐛 Bulk remove tokens with a single undo action (#8208) 2026-01-29 10:58:16 +01:00
Andrey Antukh f65292a13c 📎 Mark as skip two text editor v2 tests (flaky) 2026-01-29 10:43:29 +01:00
Andrey Antukh 94722fdec2 Ensure .hidePopover fn exist before call 2026-01-29 10:43:29 +01:00
Andrey Antukh 28509e0418 Ensure .stopPropagation fn exists before calling
Also for .stopImmediatePropagation and .preventDefault on
the event instances.
2026-01-29 10:43:29 +01:00
Eva Marco 9569fa2bcb 🐛 Fix error when creating a token with an invalid name (#8216) 2026-01-29 10:41:52 +01:00
Eva Marco 852b31c3a0 🐛 Fix allow spaces on token description (#8234) 2026-01-29 10:40:32 +01:00
Eva Marco b8fdbd1ef8 🐛 Fix opacity value when token is broken (#8239) 2026-01-29 10:27:25 +01:00
Andrés Moya 84b3f5d7c6 🐛 Fix import of shadow tokens 2026-01-29 10:25:22 +01:00
Andrey Antukh 32454f5959 Merge remote-tracking branch 'origin/staging-render' into develop 2026-01-29 10:23:46 +01:00
Andrey Antukh b881e36875 Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-29 10:23:31 +01:00
Pablo Alba 0bb74ed722 🐛 Fix viewer can update library (#8231) 2026-01-28 20:48:52 +01:00
Andrey Antukh b40e775a70 Add minor improvements to performance events (#8217)
*  Move devtools perf logging helpers to util.perf ns

* 💄 Move flag check to the entry point instead of initialize event

* ♻️ Make performance events consistent with other events
2026-01-28 20:47:14 +01:00
Eva Marco 2b4e315744 ♻️ Replace layout item numeric inputs. (#8163)
*  Replace opacity numeric input

*  Add test

* ♻️ Replace margin inputs

* 🎉 Add test
2026-01-28 14:30:18 +01:00
Pablo Alba 4ca82821c1 🐛 Fix shared keys init should be by keywords (2) (#8230) 2026-01-28 13:41:37 +01:00
David Barragán Merino 76bd31fe7d 🔧 Fix CORS error 2026-01-28 13:40:45 +01:00
David Barragán Merino cc81e56d82 🔧 Fix CORS error 2026-01-28 13:40:26 +01:00
David Barragán Merino a90f672a5e 🔧 Fix CORS error 2026-01-28 13:30:08 +01:00
Aitor Moreno 2b00e4eec9 Merge pull request #8207 from penpot/alotor-wasm-disable-thumbnail-generation
🐛 Disable thumbnails render in wasm
2026-01-28 13:28:07 +01:00
Aitor Moreno 3b86d7c1b1 🐛 Fix initializing rasterizer 2026-01-28 12:59:16 +01:00
alonso.torres 3cb716ec30 🐛 Disable thumbnails render in wasm 2026-01-28 12:59:16 +01:00
Andrey Antukh a9e2fc8d94 Backport linter fixes and config from develop 2026-01-28 12:58:54 +01:00
Pablo Alba f76598f638 🐛 Fix shared keys init should be by keywords (#8228) 2026-01-28 12:56:04 +01:00
Andrey Antukh 17ffd9a5d0 Backport linter fixes and config from develop 2026-01-28 12:54:18 +01:00
Xaviju eacc033567 🐛 Fix long token names overflow remap modal (#8224) 2026-01-28 12:44:07 +01:00
Andrey Antukh 71c349479f Merge pull request #8196 from penpot/niwinz-develop-management-auth-changes
♻️ Make several improvements to management API authentication
2026-01-28 10:52:26 +01:00
David Barragán Merino 77bbf30ae4 🔧 Fix file name 2026-01-27 21:16:44 +01:00
David Barragán Merino 693b52bf45 📚 Fix links related to penpot plugins 2026-01-27 21:16:44 +01:00
David Barragán Merino 0f51b23ce7 🔧 Deploy plugin styles documentation 2026-01-27 21:16:44 +01:00
David Barragán Merino ec61aa6b6d 🔧 Add custom domain 2026-01-27 21:16:41 +01:00
David Barragán Merino 18aca16f98 🔧 Fix file name 2026-01-27 21:12:32 +01:00
David Barragán Merino c6465e27e3 📚 Fix links related to penpot plugins 2026-01-27 21:12:32 +01:00
David Barragán Merino 1834a18263 🔧 Deploy plugin styles documentation 2026-01-27 21:12:32 +01:00
David Barragán Merino d220d07875 🔧 Add custom domain 2026-01-27 21:12:32 +01:00
David Barragán Merino fda31624c1 🔧 Fix file name 2026-01-27 21:04:25 +01:00
David Barragán Merino 7f640569bd 📚 Fix links related to penpot plugins 2026-01-27 20:59:54 +01:00
David Barragán Merino 91f1323802 🔧 Deploy plugin styles documentation 2026-01-27 20:59:54 +01:00
David Barragán Merino dbd4a2366f 🔧 Add custom domain 2026-01-27 20:59:54 +01:00
Pablo Alba cbb6d098a7 🐛 Fix boolean operators in menu for boards (#8177) 2026-01-27 17:58:07 +01:00
Andrey Antukh b6f5000d1c ⬆️ Update pnpm 2026-01-27 17:57:07 +01:00
Andrey Antukh 0527124f2f Merge remote-tracking branch 'origin/staging-render' into develop 2026-01-27 17:56:03 +01:00
Andrey Antukh faf91ac70d Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-27 17:53:16 +01:00
Eva Marco 9ca76c745f 🐛 Fix app freeze on token name change (#8214) 2026-01-27 17:31:50 +01:00
Andrey Antukh 89935e2174 Make nitrate module loading conditional to flag
This removes the flag checking on each rpc method
2026-01-27 15:16:36 +01:00
Andrey Antukh 7f27e0326d Reuse basic team and profile schemas on nitrate 2026-01-27 15:14:32 +01:00
Andrey Antukh 9c539dfb2f 🔥 Remove subscriptions related management module 2026-01-27 15:14:32 +01:00
Andrey Antukh 50a4cf8b99 📎 Adapt nitrate module to auth changes 2026-01-27 15:14:32 +01:00
Andrey Antukh f5996a7235 ♻️ Make several improvements to management API authentication 2026-01-27 15:14:32 +01:00
Andrey Antukh e8fd4698c9 🔧 Update caddy configuration 2026-01-27 15:10:53 +01:00
Andrey Antukh 0ab126748f 💄 Add format rule for code comments (#8211)
* 💄 Add format rule for code comments

* ⬆️ Update linter and formatter on devenv
2026-01-27 15:07:18 +01:00
Yamila Moreno 71a5ab9913 🔧 Delete unused workflow 2026-01-27 13:47:05 +01:00
Andrey Antukh 61969f3eb5 Improve unhandled exception handling 2026-01-27 13:46:51 +01:00
Andrey Antukh bd2ef8057e Add helper for proper print js exceptions 2026-01-27 13:46:51 +01:00
Elena Torró 9808b6ca57 Merge pull request #8205 from penpot/superalex-improve-huge-shapes-render
🎉 Improving huge shapes render
2026-01-27 13:08:25 +01:00
Eva Marco 2523096fdd 🐛 Fix css rule (#8206) 2026-01-27 12:30:14 +01:00
Aitor Moreno de41cb5488 🐛 Fix add/remove fills to text nodes 2026-01-27 12:17:10 +01:00
Xaviju 8e63c4e3e8 ♻️ Review remap interface and interaction (#8168)
* ♻️ Review remap interface and interaction
* ♻️ Fix remapping feature tests
2026-01-27 11:18:34 +01:00
Alejandro Alonso b40ccaf030 🎉 Improve zoom actions for huge shapes 2026-01-27 11:11:38 +01:00
Alejandro Alonso 7d3ac38749 🎉 Improve huge shapes rendering 2026-01-27 11:11:38 +01:00
d5abc52dac 🎉 Add first integration with nitrate (#7803)
* 🐛 Display missing selected tokens set info (#8098)

* 🐛 Display missing selected tokens set info

*  Add integration tests to verify current active set

* 🎉 Integration with nitrate platform

* 🐛 Fix nitrate get-teams returns deleted teams

*  Add nitrate to tmux devenv

*  Add retry and validation to nitrate module

*  Add photoUrl to profile on nitrate authenticate

*  Move nitrate url to an env variable

* ♻️ Change Nitrate organization-id schema to text

* ♻️ Cleanup unused imports

* 🔧 Add control-center to nginx

*  Add create org link

* 🔧 Fix nginx entrypoint

* 🐛 Fix control-center proxy pass

* 🎉 Add nitrate licence check

* Revert " Add nitrate to tmux devenv"

This reverts commit dc6f6c458995dac55cab7be365ced0972760a058.

*  Add feature flag check

* 🐛 Rename licences for licenses

*  MR changes

*  MR changes 2

* 📎 Add the ability to have local config on start backend

* 📎 Add FIXME comment

---------

Co-authored-by: Xaviju <xavier.julian@kaleidos.net>
Co-authored-by: Juanfran <juanfran.ag@gmail.com>
Co-authored-by: Yamila Moreno <yamila.moreno@kaleidos.net>
Co-authored-by: Marina López <marina.lopez.yap@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-01-27 10:04:53 +01:00
Elena Torro 8d1bc6c50c 🐛 Fix flex layout sorting on reverse order with no z-index 2026-01-27 09:34:36 +01:00
Andrey Antukh 3112b240a0 📎 Add missing entry on changelog 2026-01-27 09:28:41 +01:00
Andrey Antukh 56fd66b91a 🐛 Fix several issues related to path edition (#8187)
*  Improve save-path-content event consistency

Mainly removing possible race conditions from the event
implementation.

*  Ensure path content snapshot on start-path-edit event

*  Reuse already available shape-id on split-segments
2026-01-27 09:27:42 +01:00
Andrey Antukh 3b96eb5476 🐛 Fix incorrect handling of schema expression on obj/reify 2026-01-27 09:20:33 +01:00
Elena Torro 2a7c24f6fd 🐛 Fix shape operations on sidebar when using interaction observer 2026-01-27 09:03:41 +01:00
Andrey Antukh 7a842ce36a Restore back node based http server for e2e tests 2026-01-27 08:55:17 +01:00
Andrey Antukh ea25c5db99 🔥 Remove unused svg from frontend resources 2026-01-27 08:55:17 +01:00
Alejandro Alonso 947aa22dee Merge pull request #8173 from penpot/elenatorro-improve-surface-performance
🔧 Improve surface rendering performance
2026-01-27 07:21:23 +01:00
Alejandro Alonso ce1796eb02 Merge pull request #8199 from penpot/elenatorro-13192-fix-layer-items-operations
🐛 Fix shape operations on sidebar when using interaction observer
2026-01-27 07:14:55 +01:00
Andrey Antukh 6f0685ba8e 🐛 Fix several issues related to path edition (#8187)
*  Improve save-path-content event consistency

Mainly removing possible race conditions from the event
implementation.

*  Ensure path content snapshot on start-path-edit event

*  Reuse already available shape-id on split-segments
2026-01-26 22:48:57 +01:00
Andrey Antukh abc1773f65 Merge tag '2.13.0-RC9' 2026-01-26 18:12:16 +01:00
David Barragán Merino 93f5e74bb0 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:14:15 +01:00
David Barragán Merino 1ce0b60e3d 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:13:54 +01:00
David Barragán Merino d433fd25c1 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:12:58 +01:00
Elena Torro bb0e9b47cb 🐛 Fix shape operations on sidebar when using interaction observer 2026-01-26 16:14:18 +01:00
Elena Torro 5209a8b423 🔧 Improve surface rendering performance 2026-01-26 16:10:22 +01:00
Aitor Moreno f4f4f5bbb5 🐛 Fix multiple issues and tests 2026-01-26 14:14:06 +01:00
David Barragán Merino 38179ba11e 🔧 Enable secret inheritance 2026-01-26 14:01:22 +01:00
David Barragán Merino ef80901400 🔧 Enable secret inheritance 2026-01-26 14:00:55 +01:00
David Barragán Merino c5f03d711a 🔧 Enable secret inheritance 2026-01-26 14:00:09 +01:00
David Barragán Merino 719a95246a 🔧 Define deploy plugin packages workflows 2026-01-26 13:48:33 +01:00
David Barragán Merino e590cd852d 🔧 Rename wrangle to wrangler 2026-01-26 13:48:33 +01:00
David Barragán Merino a9741073e5 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-26 13:48:33 +01:00
David Barragán Merino 5306bed548 🔧 Define deploy plugin packages workflows 2026-01-26 13:47:57 +01:00
David Barragán Merino 92a319ddd1 🔧 Rename wrangle to wrangler 2026-01-26 13:47:57 +01:00
David Barragán Merino 68a6d4c9a8 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-26 13:47:57 +01:00
David Barragán Merino 72cc5ee349 🔧 Define deploy plugin packages workflows 2026-01-26 13:23:46 +01:00
Eva Marco 804695b48b ♻️ Replace stroke width numeric inputs (#8137)
*  Replace opacity numeric input

*  Add test

* ♻️ Replace stroke width numeric input

* 🎉 Add tests
2026-01-26 12:50:28 +01:00
Elena Torró 20c8fbf314 🔧 Make render-wasm visual regression tests use gpu (#8189) 2026-01-26 11:53:29 +01:00
Andrey Antukh e02536f8d4 Merge branch 'staging-render' into develop 2026-01-26 11:02:50 +01:00
Andrey Antukh 3eeaaab17e Merge branch 'staging' into staging-render 2026-01-26 11:02:26 +01:00
Andrey Antukh f07495ae95 🐛 Fix incorrect handling of numeric values layout padding and gap
Fixes https://github.com/penpot/penpot/issues/8113
2026-01-26 11:01:25 +01:00
Andrey Antukh 23d5fc7408 🐛 Prevent exception on open-new-window when no window is returned
Fixes https://github.com/penpot/penpot/issues/7787
2026-01-26 11:01:25 +01:00
Andrey Antukh 8632b18eec 🐛 Avoid json decoder liner limit exception by chunking
Happens only when we send large binary data serialized with transit
(mainly used for upload fonts data).
2026-01-26 11:01:25 +01:00
Andrey Antukh 33e650242c Add slugify to the filename on assets exportation
Fixes https://github.com/penpot/penpot/issues/8017
2026-01-26 11:01:25 +01:00
Alejandro Alonso 3dc9e28230 Merge pull request #8155 from penpot/elenatorro-13089-improve-page-load-render
🔧 Improve render UX on first load
2026-01-26 10:40:44 +01:00
Andrey Antukh e03ad25118 🔧 Backport CI workflow from develop 2026-01-26 10:18:24 +01:00
Andrey Antukh d9c56da705 ⬆️ Update playwright on frontend module 2026-01-26 10:08:53 +01:00
Andrey Antukh 75248aec4e 🔧 Add missing container on tests.yml 2026-01-26 09:52:47 +01:00
David Barragán Merino f0d9429775 🔧 Rename wrangle to wrangler 2026-01-26 09:37:33 +01:00
Eva Marco 62ecf48bdb 🐛 Fix glich when applying padding (#8099)
*  Replace opacity numeric input

*  Add test

* 🐛 Fix glich when applying padding
2026-01-26 08:40:32 +01:00
Alejandro Alonso 18de7f1db6 Merge pull request #8041 from penpot/niwinz-subpath-support
 Add several adjustments for make penpot run on subpath
2026-01-26 07:34:24 +01:00
Alejandro Alonso 2b2941bd25 Merge pull request #8049 from penpot/niwinz-virtual-clock-by-user
 Make the virtual clock by profile and not global
2026-01-26 07:22:08 +01:00
Alejandro Alonso f2d561eff7 Merge pull request #8063 from penpot/niwinz-develop-auditlog-logging
 Add the ability to log using logging subsystem the audit events
2026-01-26 07:17:13 +01:00
Alejandro Alonso 418b65a287 Merge pull request #8143 from penpot/niwinz-develop-bugfix-2
🐛 Fix incorrect handling of numeric values layout padding and gap
2026-01-26 07:11:09 +01:00
Alejandro Alonso d4e7810eba Merge pull request #8135 from penpot/niwinz-develop-chunking-on-font-upload
🐛 Avoid json decoder line limit exception by chunking
2026-01-26 07:08:23 +01:00
Andrey Antukh 1d1d32ad39 🐛 Avoid json decoder liner limit exception by chunking
Happens only when we send large binary data serialized with transit
(mainly used for upload fonts data).
2026-01-26 06:58:17 +01:00
Alejandro Alonso fb08dc65c8 Merge pull request #8133 from penpot/niwinz-develop-asset-export-whitespaces
 Add slugify to the filename on assets exportation
2026-01-26 06:55:52 +01:00
Alejandro Alonso 927ac93fa7 Merge pull request #8141 from penpot/niwinz-develop-bugfix-1
🐛 Prevent exception on open-new-window when no window is returned
2026-01-26 06:32:02 +01:00
Andrey Antukh e546a7c614 🐛 Prevent exception on open-new-window when no window is returned
Fixes https://github.com/penpot/penpot/issues/7787
2026-01-26 06:31:40 +01:00
David Barragán Merino 058c20c2e2 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-23 20:39:39 +01:00
David Barragán Merino 599656c31e 🔧 Fix a typo in an interpolation 2026-01-23 19:52:47 +01:00
David Barragán Merino 5d7e6afd76 🔧 Fix a typo in an interpolation 2026-01-23 19:52:20 +01:00
Elena Torró 68a77e9cc8 Merge pull request #8179 from penpot/superalex-adding-performance-logs-flag
🎉 Adding performance logs flag
2026-01-23 14:06:57 +01:00
Alejandro Alonso e3148ea20e 🎉 Adding performance logs flag 2026-01-23 13:34:19 +01:00
Elena Torró 5da9bbea62 Merge pull request #8174 from penpot/superalex-fix-blur-events-text-editor-v2
🐛 Fix blur events for text editor v2 in firefox
2026-01-23 13:08:01 +01:00
Alonso Torres 15d369493b 🐛 Fix problem with z-index modal in dashboard (#8178) 2026-01-23 12:48:01 +01:00
Andrey Antukh 5016b2a7bf Merge remote-tracking branch 'origin/staging-render' into develop 2026-01-23 11:18:33 +01:00
Andrey Antukh 089d1667b6 Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-23 11:08:07 +01:00
Alejandro Alonso 4ad5282063 🐛 Fix blur events for text editor v2 in firefox 2026-01-23 10:58:54 +01:00
Elena Torró d0e79c94b4 Merge pull request #8162 from penpot/superalex-fix-auto-height
🐛 Fix text boxes with auto-height don't update height when resized by dragging side handles
2026-01-23 10:57:54 +01:00
Eva Marco 9c9b672e3e 🐛 Fix spanish translations on import export token modal (#8172) 2026-01-23 10:05:20 +01:00
Andrey Antukh 43ae213659 Replace login illustration svg with a correct bitmap (#8170) 2026-01-23 09:59:29 +01:00
Eva Marco 5146221513 🐛 Fix allow negative spread values on shadow token creation (#8167)
* 🐛 Fix allow negative spread values on shadow token creation

* 🎉 Add test
2026-01-23 09:50:36 +01:00
Eva Marco e53f335204 🐛 Fix unhandled error on tokens modal (#8165) 2026-01-23 09:35:53 +01:00
Alejandro Alonso d112c0a33b 🐛 Fix text boxes with auto-height don't update height when resized by dragging side handles 2026-01-23 09:05:20 +01:00
Elena Torró 7b86518afa Merge pull request #8171 from penpot/ladybenko-13152-fix-blur
🐛 Fix blur when clicking on same page
2026-01-22 17:42:39 +01:00
Elena Torró 9991901ed8 Merge pull request #8161 from penpot/superalex-fix-editing-text-doesnt-update-layer-name
🐛 Bug: Editing the text inside a text object doesn’t update the text layer name.
2026-01-22 17:40:32 +01:00
Belén Albeza 3d0c6ad421 Blur board titles and outlines when switching pages 2026-01-22 16:00:24 +01:00
Andrey Antukh dc973dac36 🔧 Enable link workspace packages on plugins 2026-01-22 13:55:41 +01:00
Andrey Antukh 4467827218 ⬆️ Update react dependency on frontend 2026-01-22 13:55:41 +01:00
Andrey Antukh 6470db8d5f Remove mention of yarn on several files 2026-01-22 13:55:41 +01:00
Andrey Antukh dc44156b53 🐛 Fix text editor wasm playground 2026-01-22 13:55:41 +01:00
Andrey Antukh f0e53d70ae 🎉 Migrate to PNPM render-wasm module 2026-01-22 13:55:41 +01:00
Andrey Antukh ef73a263b2 🎉 Migrate to PNPM docs module 2026-01-22 13:55:41 +01:00
Andrey Antukh 9b1e007a49 Replace e2e node server with caddy
Which is already available in the devenv runtime image
2026-01-22 13:55:41 +01:00
Andrey Antukh ea8632e56a 🐛 Fix frontend test-components scripts 2026-01-22 13:55:41 +01:00
Andrey Antukh 2d00e64ede Fix e2e scripts related to pnpm change 2026-01-22 13:55:41 +01:00
Andrey Antukh 1246250198 🎉 Migrate to PNPM frontend module 2026-01-22 13:55:41 +01:00
Andrey Antukh 34f2943dcd 🎉 Migrate to PNPM library module 2026-01-22 13:55:41 +01:00
Andrey Antukh 3fb78116b8 🎉 Migrate to PNPM backend module 2026-01-22 13:55:41 +01:00
Andrey Antukh 072e415b9e 🎉 Migrate to PNPM common module 2026-01-22 13:55:41 +01:00
Andrey Antukh 67a904824c 🎉 Migrate to PNPM exporter module 2026-01-22 13:55:41 +01:00
Belén Albeza 835ea97be7 🐛 Fix blur applied when clicking in the active page 2026-01-22 13:27:05 +01:00
Andrey Antukh 68184209be Add the ability to make json/->clj non-recursive 2026-01-22 13:07:05 +01:00
Andrey Antukh d2295862b4 Add the ability to customize decoder and schema on obj/reify 2026-01-22 13:07:05 +01:00
Andrey Antukh 23cbf33d1b Merge remote-tracking branch 'origin/staging' into develop 2026-01-22 12:34:49 +01:00
David Barragán Merino 16f22a7b5c 🔧 Fixes to the API documentation deployer 2026-01-22 12:10:27 +01:00
David Barragán Merino a1460115e8 🔧 Deploy penpot api documentation 2026-01-22 12:10:27 +01:00
David Barragán Merino 2574ad3315 🔧 Fixes to the API documentation deployer 2026-01-22 12:09:38 +01:00
David Barragán Merino e6b5364a84 🔧 Deploy penpot api documentation 2026-01-22 12:09:38 +01:00
David Barragán Merino b4ff0ccf3a 🔧 Fixes to the API documentation deployer 2026-01-22 12:08:09 +01:00
Andrey Antukh 6c6666a39a Merge pull request #8158 from penpot/ladybenko-13058-hide-avatar
🐛 Do not hide active users avatar when there are 3 of them
2026-01-22 12:02:53 +01:00
Elena Torro f94c9cdb02 🐛 Fix objects sorting for thumbnail generation 2026-01-22 09:29:33 +01:00
Elena Torro 8637c46ba1 🐛 Fix empty pool state 2026-01-22 08:52:26 +01:00
Elena Torro 5d7d23a2c7 🔧 Keep clear cached canvas 2026-01-22 08:51:58 +01:00
Alejandro Alonso a1a3966d7b 🐛 Editing the text inside a text object doesn’t update the text layer name 2026-01-22 08:24:13 +01:00
David Barragán Merino c1335961b4 🔧 Deploy penpot api documentation 2026-01-21 18:52:07 +01:00
Alonso Torres 656f81f89f ⬆️ Update plugins to 1.4.2 (#8157) 2026-01-21 17:36:58 +01:00
Belén Albeza eaf64b6e16 ♻️ Make the CSS of presence widgets to adhere to our guidelines 2026-01-21 17:33:18 +01:00
Belén Albeza 560a0d09d5 🐛 Fix hiding avatar when we have 3 active users 2026-01-21 16:01:20 +01:00
Elena Torro aab1d97c4c 🔧 Clean up and use proper imports 2026-01-21 16:01:06 +01:00
Elena Torro 499aac31a4 🔧 Improve tile invalidation to prevent visual flickering
When tiles are invalidated (during shape updates or page loading), the old tile
content is now kept visible until new content is rendered to replace it. This
provides a smoother visual experience during updates.
2026-01-21 15:42:52 +01:00
Alonso Torres 01a4ffeb8b ⬆️ Updated plugins release to 1.4.0 (#8148) 2026-01-21 15:41:00 +01:00
Elena Torro 962d7839a2 🔧 Add progressive rendering support for improved page load experience
When loading large pages with many shapes, the UI now remains responsive by
processing shapes in chunks (100 shapes at a time) and yielding to the browser
between chunks. Preview renders are triggered at 25%, 50%, and 75% progress to
give users visual feedback during loading.
2026-01-21 14:55:53 +01:00
Elena Torro 83387701a0 🔧 Add batched shape base properties serialization for improved WASM performance 2026-01-21 14:55:07 +01:00
Elena Torro 5775fa61ba 🔧 Refactor ShapesPool to use index-based storage instead of unsafe lifetime references
Replace `HashMap<&'a Uuid, ...>` with `HashMap<usize, ...>` for all auxiliary maps
(modifiers, structure, scale_content, modified_shape_cache)
2026-01-21 14:53:56 +01:00
Andrey Antukh b70eb768e0 Merge remote-tracking branch 'origin/staging' into develop 2026-01-21 13:51:41 +01:00
Andrey Antukh b8c70be9a2 Make frontend build and watch process more resilent to errors 2026-01-21 13:44:35 +01:00
Andrey Antukh 525adcfcbe Add wasm build on watch app script (devenv) 2026-01-21 13:44:35 +01:00
Eva Marco 7cce4c6532 🐛 Fix unhandled exception tokens creation dialog (#8136) 2026-01-21 13:09:22 +01:00
Alejandro Alonso a3fdd8b691 Merge pull request #8147 from penpot/niwinz-staging-file-menu-issue
 Use correct team-id on file-menu on dashboard
2026-01-21 12:54:28 +01:00
Andrey Antukh b6a9579c98 Use correct team-id on file-menu on dashboard
Before the changes on this commit, the team object is used for
retrieve the id, where we already have team-id. Additionally, the
team object resolution is async operation and is not available on
the first render which causes strange issues on automated flows
(playwright) where an option is clicked when the async flow is
still pending and we have no team object loaded.
2026-01-21 12:23:44 +01:00
Belén Albeza 5b1766835f 🐛 Fix broken selection on duplicated shapes on new pages 2026-01-21 10:32:13 +01:00
Andrey Antukh 4397ede5c1 Merge branch 'staging-render' into develop 2026-01-21 10:18:15 +01:00
Andrey Antukh ff25df0457 Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-21 10:17:22 +01:00
Andrey Antukh e0910db99e 🐛 Fix incorrect handling of numeric values layout padding and gap
Fixes https://github.com/penpot/penpot/issues/8113
2026-01-21 09:38:44 +01:00
Eva Marco 8c7fd0af4b 🐛 Fix shadow reference validation (#8132) 2026-01-21 09:17:03 +01:00
Andrey Antukh cf46051f56 🔥 Remove .traivis.yml file from the repository 2026-01-20 19:40:31 +01:00
Luis de Dios 079b3fbfad ♻️ Extract and create panel title component (#8090) 2026-01-20 18:56:25 +01:00
Andrey Antukh 299f628951 Merge pull request #8123 from penpot/GlobalStar117-fix/token-validation-crash
🐛 Fix Penpot crash when setting some name in Design tokens
2026-01-20 18:53:05 +01:00
David Barragán Merino 32d0fe6463 🔧 Use selfhosted runner 01 to generate the bundle 2026-01-20 18:09:18 +01:00
Andrey Antukh 6393330ee1 Merge remote-tracking branch 'origin/staging' 2026-01-20 16:25:10 +01:00
Andrey Antukh 8252bc485e 📚 Fix oidc callback related documentation issue 2026-01-20 16:24:12 +01:00
Andrey Antukh cecd3d4a90 📎 Update changelog 2026-01-20 16:00:57 +01:00
Eva Marco 1c2c0987f5 🐛 Fix schema validation for references from other sets 2026-01-20 15:51:43 +01:00
Globalstar117 0418147e74 🐛 Add error handler to token form validation to prevent crash
When creating a token with a name that conflicts with existing
hierarchical token names (e.g., 'accent-color' when 'accent-color.blue.dark'
exists), the validation throws an error via rx/throw. However, the
rx/subs! subscriber in generic_form.cljs had no error handler, causing
an unhandled exception that resulted in an 'Internal Error' crash.

This fix adds an error handler that:
1. Catches validation errors from the reactive stream
2. Uses humanize-errors to convert them to user-friendly messages
3. Displays the error in the form's extra-errors field

Before: Crash with 'Internal Error' dialog
After: Form shows validation error message

Fixes #8110

---
This is a Gittensor contribution.
gittensor:user:GlobalStar117
2026-01-20 15:51:25 +01:00
Andrey Antukh 9e0ba4429a Add slugify to the filename on assets exportation
Fixes https://github.com/penpot/penpot/issues/8017
2026-01-20 15:27:24 +01:00
Alonso Torres 47775a9e2c Merge pull request #8134 from penpot/alotor-fix-plugins-export
🐛 Fix problem with export in plugins
2026-01-20 15:03:04 +01:00
Eva Marco 7499a5bca6 ♻️ Replace opacity input (#8072)
*  Replace opacity numeric input

*  Add test
2026-01-20 14:30:35 +01:00
Xaviju 6cd5bc76d7 💄 Replace current themes switch with DS switch (#8131) 2026-01-20 14:17:25 +01:00
Andrey Antukh 8191d04114 Use non-legacy config example on docker compose file 2026-01-20 13:25:55 +01:00
David Barragán Merino bbe6ee2e19 🔧 Define a different temporary config file for each execution 2026-01-20 12:59:56 +01:00
David Barragán Merino fb6d8309b6 🔧 Prevent error 429 downloading docker images from dockerhub 2026-01-20 12:59:56 +01:00
Alejandro Alonso b7c2d9a079 Merge pull request #8130 from penpot/superalex-improve-zoom-pan-performance-7
🐛 Fix some tiles disappear after fast zoom and pan
2026-01-20 12:56:02 +01:00
Alejandro Alonso aeb34a6f64 Merge pull request #8109 from penpot/superalex-fix-text-selrect-calculation
🐛 Render wasm typography token issues
2026-01-20 12:54:45 +01:00
Alejandro Alonso 6fa0c3af0c 🐛 Fix some tiles disappear after fast zoom and pan 2026-01-20 12:40:01 +01:00
Alejandro Alonso 260b9fb040 🐛 Fix texts with auto size updated via tokens with render wasm
activated
2026-01-20 12:39:17 +01:00
Alejandro Alonso 884954f4ff 🐛 Fix text selrect calculation 2026-01-20 12:37:57 +01:00
Andrey Antukh 88f0f75174 Merge pull request #8129 from penpot/niwinz-staging-bugfix-1
 Several improvements for build process
2026-01-20 12:26:55 +01:00
Andrey Antukh 1ffa956251 Include timestamp on version tag 2026-01-20 12:26:39 +01:00
Andrey Antukh 31054099ff Use pseudo-names on release builds of frontend (#8105) 2026-01-20 12:26:39 +01:00
Andrey Antukh 689467bcf9 📎 Update changelog 2026-01-20 12:25:43 +01:00
Andrey Antukh 7724450037 Merge remote-tracking branch 'origin/staging-render' into develop 2026-01-20 12:18:04 +01:00
Xaviju 368fa954ce Remove tokens tree node (#8042) 2026-01-20 11:00:13 +01:00
Eva Marco 983487d73c 🐛 Fix shadow token reference validation (#8128) 2026-01-20 10:56:27 +01:00
Andrey Antukh 6fd0f5377c Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-20 10:08:58 +01:00
Elena Torró eb54bc485e Merge pull request #8120 from penpot/alotor-fix-flex-layout
🐛 Fix problems with layout
2026-01-20 10:00:24 +01:00
Elena Torró 12c24a36b4 Merge pull request #8122 from penpot/fix-thumbnail-generation
🐛 Fix problem with thumbnail generation
2026-01-20 09:59:34 +01:00
Alejandro Alonso 324d54ad28 🐛 Fix set all rounded corners to 0 2026-01-20 09:34:06 +01:00
alonso.torres f42ff27f3d 🐛 Fix problem with bools 2026-01-19 17:05:04 +01:00
Andrey Antukh 0ecb2bc838 Merge remote-tracking branch 'origin/staging' into develop 2026-01-19 13:42:05 +01:00
Andrey Antukh 3ecf509f3b 🔧 Add missing gif files handling on defaut nginx config 2026-01-19 13:32:01 +01:00
alonso.torres 2c1cc89f53 🐛 Fix problem with thumbnail generation 2026-01-19 12:54:15 +01:00
alonso.torres 498b0b30fe 🐛 Fix problems with layout 2026-01-19 12:17:58 +01:00
Elena Torró 89f40dcda2 🔧 Move WebGL context error message to 'errors' namespace (#8117) 2026-01-19 11:24:19 +01:00
Andrey Antukh e92f3fb3cb Use pseudo-names on release builds of frontend (#8105) 2026-01-19 11:23:35 +01:00
Andrey Antukh 5193cfd56e :paerclip: Update changelog 2026-01-19 11:15:39 +01:00
Dalai FelintoandDalai Felinto 7f395b2642 🐛 Fix import tokens dialog default option (#8051)
This was intended to be changed on 13fcf3a9bb. However only the menu
order changed, not the default option.

Signed-off-by: Dalai Felinto <dalai@blender.org>
Co-authored-by: Dalai Felinto <dalai@blender.org>
2026-01-19 11:14:34 +01:00
Dalai FelintoandDalai Felinto 813d5d8e69 🐛 Fix import tokens dialog default option (#8051)
This was intended to be changed on 13fcf3a9bb. However only the menu
order changed, not the default option.

Signed-off-by: Dalai Felinto <dalai@blender.org>
Co-authored-by: Dalai Felinto <dalai@blender.org>
2026-01-19 11:13:55 +01:00
Andrey Antukh 84f1ff092d Merge remote-tracking branch 'origin/staging' into develop 2026-01-19 11:10:37 +01:00
Juan de la Cruz 2a62bd2586 🎉 Add 2.13 release slides (#8116) 2026-01-19 11:09:58 +01:00
Elena Torró ccac7bd510 Merge pull request #8108 from penpot/ladybenko-13022-blur-page
🎉 Apply blur effect when switching pages
2026-01-19 11:04:31 +01:00
Andrey Antukh f2b082b93e Merge branch 'staging-render' into develop 2026-01-19 10:54:37 +01:00
Andrey Antukh d73197625d Merge remote-tracking branch 'origin/staging' into staging-render 2026-01-19 10:43:43 +01:00
Andrey Antukh 1f41bef4a9 Add several adjustments for make penpot run on subpath 2026-01-18 10:12:23 +01:00
Marina López fdf5bb250b 🐛 Fix profile not updating after deleting a team member (#8075) 2026-01-18 10:08:18 +01:00
Yamila Moreno 786736fadd Improve default nginx config (#8104) 2026-01-18 10:07:44 +01:00
Pablo Alba 1ff6e00398 🐛 Fix error message on components doesn't close automatically (#8081) 2026-01-18 10:07:11 +01:00
David Barragán Merino 25455523ad 🔧 Revert use of selfhosted runner 02 to generate the bundle 2026-01-16 17:51:02 +01:00
David Barragán Merino 8dfeb21978 🔧 Use selfhosted runner 02 to generate the bundle and the docker images 2026-01-16 17:44:15 +01:00
Belén Albeza 43d1d127dc 🎉 Apply blur effect to previous canvas pixels while setting wasm objects 2026-01-16 13:04:59 +01:00
Belén Albeza 8bd3ef717c 🎉 Apply blur to canvas when switching pages 2026-01-16 13:04:59 +01:00
Elena Torro 53bc647783 🔧 Fix shape selection from canvas to sidebar 2026-01-16 13:02:25 +01:00
Alejandro Alonso ad2833bb7a Merge pull request #8107 from penpot/elenatorro-13077-improve-shape-selection-performance
🔧 Fix shape selection from canvas to sidebar
2026-01-16 13:01:58 +01:00
Elena Torro 21911e898f 🔧 Fix shape selection from canvas to sidebar 2026-01-16 12:45:45 +01:00
Andrey Antukh 538073debf Merge remote-tracking branch 'origin/staging' into develop 2026-01-16 10:38:20 +01:00
Andrey Antukh 1ae7515189 🐛 Set correct defaults for storage configuration on compose (#8060)
* 🔥 Remove mention to legacy and never released admin on compose

* 🐛 Set correct defaults for storage configuration on compose
2026-01-16 08:11:34 +01:00
andrés gonzález 8c401f5346 📚 Add Files and Projects section to user guide (#8095) 2026-01-15 17:01:05 +01:00
Elena Torró 6029f9bb51 Merge pull request #8089 from penpot/superalex-improve-zoom-pan-performance-5
🎉 Performance improvements
2026-01-15 16:46:07 +01:00
Elena Torro e0fd8bac81 🔧 Optimize sidebar performance for deeply nested shapes
- Batch hover highlights using RAF to avoid long tasks from rapid events
- Run parent expansion asynchronously to not block selection
- Lazy-load children in layer items using IntersectionObserver
- Clarify expand-all-parents logic with explicit bindings
2026-01-15 13:41:54 +01:00
Elena Torro 34737ddfc9 🔧 Always lookup over a set 2026-01-15 13:41:10 +01:00
Elena Torro a8dfd19338 🔧 Add performance debugging logs 2026-01-15 13:40:58 +01:00
Elena Torro e33e8a8c3b 🔧 Lookup page objects only when value changes 2026-01-15 13:40:53 +01:00
Alejandro Alonso 214b0efa02 Merge pull request #8074 from penpot/elenatorro-13017-improve-sidebar-performance
🔧 Optimize sidebar performance for deeply nested shapes
2026-01-15 13:28:14 +01:00
David Barragán Merino 661436ecae 🔧 Use selfhosted runner 02 2026-01-15 13:26:49 +01:00
Alejandro Alonso c411aefc6c 🐛 Fix rotated shapes extrect calculation 2026-01-15 12:53:21 +01:00
Alejandro Alonso 311e124658 🎉 Reduce extrect work in tile traversal
Avoid repeated extrect calculations and simplify root ordering per tile.
2026-01-15 12:53:21 +01:00
Alejandro Alonso afc914f486 🎉 Render simple shapes directly on Current
Bypass intermediate surfaces for simple shapes without effects.
2026-01-15 12:53:21 +01:00
Pablo Alba 0d5fe6e527 🐛 Fix wrong register image 2026-01-15 11:25:12 +01:00
Pablo Alba e7230d9da4 🐛 Fix wrong image in the onboarding invitation block 2026-01-15 11:25:12 +01:00
Elena Torro 5054f6bc38 🔧 Optimize sidebar performance for deeply nested shapes
- Batch hover highlights using RAF to avoid long tasks from rapid events
- Run parent expansion asynchronously to not block selection
- Lazy-load children in layer items using IntersectionObserver
- Clarify expand-all-parents logic with explicit bindings
2026-01-15 09:15:32 +01:00
andrés gonzález b72de2dc8f 📚 Add shadow token documentation (#8082) 2026-01-15 09:09:08 +01:00
Alejandro Alonso 84f750da0d 🎉 Skip heavy effects in fast mode
Avoid blur and shadow passes for text and shapes when FAST_MODE is enabled.
2026-01-15 08:45:21 +01:00
Xaviju 38396ba299 ♻️ Divide token integrations tests into multiple files (#8076) 2026-01-14 16:12:36 +01:00
Pablo Alba b1997a83b3 🐛 Fix prototype connections lost when switching between variants 2026-01-14 15:30:54 +01:00
Elena Torro 68f5671eab 🔧 Always lookup over a set 2026-01-14 13:49:32 +01:00
Elena Torro 92976143bb 🔧 Add performance debugging logs 2026-01-14 13:49:32 +01:00
Eva Marco dd2d03e6a0 ♻️ Replace border radius inputs (#7953)
*  Replace border radius numeric input

*  Add border radius token inputs on multiple selection
2026-01-14 12:45:40 +01:00
Elena Torro fd675e0194 🔧 Lookup page objects only when value changes 2026-01-14 12:15:01 +01:00
Elena Torro a3119bef5e 🔧 Show message and button to reload the page when WebGL context is lost 2026-01-14 11:10:03 +01:00
Alejandro Alonso c60d74df62 🐛 Fix nested frames border clipping 2026-01-14 11:10:03 +01:00
Alejandro Alonso d593e299e3 🐛 Fix mask erros on save/restore optimizations 2026-01-14 11:10:03 +01:00
Alejandro Alonso 4a8e02987f 🐛 Fix mask erros on save/restore optimizations 2026-01-14 11:10:03 +01:00
Alejandro Alonso ee766e85a0 🎉 Wasm render dirty surfaces 2026-01-14 11:10:03 +01:00
Alejandro Alonso 35e3b7f19a 🎉 Root ids refactor 2026-01-14 11:10:03 +01:00
Alejandro Alonso 1810df232b 🎉 Ignore frames and groups when they have no visual extra information 2026-01-14 11:10:03 +01:00
Alejandro Alonso 3e99ad036c 🎉 Avoid unnecesary saves and restores 2026-01-14 11:10:03 +01:00
Alejandro Alonso 042a3a4080 🐛 Fix wasm playgrounds 2026-01-14 11:10:03 +01:00
Belén Albeza f0687fd1f7 🎉 Make workspace loader to wait for first render 2026-01-14 11:10:03 +01:00
Aitor Moreno 2c9159288f 🐛 Fix previous styles lost when changing selected text 2026-01-14 11:10:01 +01:00
Alejandro Alonso 51635770ce Merge pull request #8065 from penpot/eva-fix-lost-translation
🐛 Fix translation on rename token
2026-01-14 09:44:41 +01:00
Eva Marco 18a4e63da0 🐛 Fix translation on rename token 2026-01-13 15:57:09 +01:00
Andrey Antukh c98373658e Revert "🔧 Use selfhosted runners (#8057)"
This reverts commit 01e42b0458.
2026-01-13 15:45:28 +01:00
Andrey Antukh 2e400768b7 Add the ability to log using logging subsystem the audit events 2026-01-13 14:22:18 +01:00
David Barragán Merino 01e42b0458 🔧 Use selfhosted runners (#8057) 2026-01-13 14:20:33 +01:00
Madalena Melo 7529673812 📎 Create an issue template for reporting bugs on the new render engine (#8059)
This template will be part of the open beta test for the new render, so that users can report issues for us to review.
2026-01-13 13:27:27 +01:00
Andrey Antukh d2dad35d7a Merge branch 'staging' into develop 2026-01-13 10:36:22 +01:00
Andrey Antukh d71f811dbe Update help center build script 2026-01-13 10:35:15 +01:00
Eva Marco f7b5266304 🐛 Fix sticky-buttons-on-layers (#7962) 2026-01-12 14:28:19 +01:00
Eva Marco 09c23256b7 🐛 Fix multiselection with tokens on stroke color (#7977) 2026-01-12 12:54:24 +01:00
Andrey Antukh 1ae1c0460e 🐛 Fix translation related to dashboard deleted page (#8056)
* 🐛 Fix translation related to dashboard deleted page

*  Rehash and validate translation files
2026-01-12 12:42:51 +01:00
Andrey Antukh 291c7349db Merge remote-tracking branch 'origin/staging' into develop 2026-01-12 11:53:02 +01:00
Andrey Antukh 1beb3b86aa 🐛 Add missing msgid_plural on several translations 2026-01-12 11:52:07 +01:00
Andrey Antukh b605a3b53d Merge branch 'staging' into develop 2026-01-12 11:44:51 +01:00
Andrey Antukh fe20bdd00e 🐛 Fix multiple selection options on dashboard deleted page (#8055)
* 🐛 Fix multiple selection options on dashboard deleted page

* 📎 Fix translations
2026-01-12 11:41:37 +01:00
María Valderrama 5420897b92 🐛 Fix empty state message in trash page (#8045) 2026-01-12 11:17:01 +01:00
Andrey Antukh e430a4c9f3 Revert " Backport translations from develop"
This reverts commit ec6d72bd91.
2026-01-12 10:40:17 +01:00
Andrey Antukh ec6d72bd91 Backport translations from develop 2026-01-12 09:39:33 +01:00
Nicola Bortoletto cc2dab2756 🌐 Add translations for: Italian
Currently translated at 98.4% (2032 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-01-12 09:36:34 +01:00
Alexis Morin d0c0664338 🌐 Add translations for: French (Canada)
Currently translated at 16.5% (342 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-12 09:36:34 +01:00
Alexis Morin 2240f25143 🌐 Add translations for: French (Canada)
Currently translated at 15.3% (317 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-12 09:36:34 +01:00
Oğuz Ersen 93a5ec2f5d 🌐 Add translations for: Turkish
Currently translated at 99.8% (2061 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-01-12 09:36:33 +01:00
Alexis Morin d6784771a8 🌐 Add translations for: French (Canada)
Currently translated at 13.5% (279 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-12 09:36:33 +01:00
Edgars Andersons 930c814ded 🌐 Add translations for: Latvian
Currently translated at 91.1% (1883 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-01-12 09:36:33 +01:00
Stephan Paternotte 1a5a69bca2 🌐 Add translations for: Dutch
Currently translated at 99.8% (2061 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-01-12 09:36:33 +01:00
VKing9 9ad323a220 🌐 Add translations for: Hindi
Currently translated at 96.9% (2001 of 2065 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-01-12 09:36:33 +01:00
Andrey Antukh bcaf76d055 Make the virtual clock by profile and not global
And make it affect only RPC/HTTP requests and not worker
tasks.
2026-01-09 12:58:44 +01:00
Eva Marco 6ec451b46d 🐛 Fix resolved value on line height (#8047) 2026-01-09 12:54:24 +01:00
Andrés Moya 5fa4368d70 🔧 Refactor integration test to be cleaner (#8044) 2026-01-09 12:52:50 +01:00
Elena Torró b8efd2518d 🐛 Fix invite members UI modal (#8032) 2026-01-09 11:12:15 +01:00
Andrey Antukh 2b836f10cb 🐛 Do not show deleted files on search (#8036)
* 🐛 Do not show deleted files on search

* 💄 Add cosmetic changes to dashboard deleted files page
2026-01-09 11:11:29 +01:00
Andrés Moya 7b2271ec38 🐛 Fix remapping of tokens with the same name and update tests (#8043) 2026-01-09 10:53:19 +01:00
Xaviju 2240d93069 Save unfolded tokens path (#7949) 2026-01-09 09:56:18 +01:00
Edgars Andersons 3f4506284b 🌐 Add translations for: Latvian
Currently translated at 91.3% (1869 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-01-08 18:06:51 +01:00
Valentina Chapellu af1dfd91aa 🌐 Add translations for: Italian
Currently translated at 97.0% (1984 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-01-08 18:06:51 +01:00
Mikel Larreategi 24feebd73b 🌐 Add translations for: Basque
Currently translated at 56.4% (1155 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/
2026-01-08 18:06:51 +01:00
Aryiu 33e5a9a538 🌐 Add translations for: Catalan
Currently translated at 52.2% (1068 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ca/
2026-01-08 18:06:50 +01:00
Linerly 9c69b07a62 🌐 Add translations for: Indonesian
Currently translated at 82.9% (1697 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2026-01-08 18:06:50 +01:00
Црнобог 56f5be4f37 🌐 Add translations for: Serbian
Currently translated at 67.0% (1371 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2026-01-08 18:06:50 +01:00
ascarida 8a70204d41 🌐 Add translations for: Galician
Currently translated at 18.0% (370 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/gl/
2026-01-08 18:06:50 +01:00
Henrik Allberg 57a27f7e7f 🌐 Add translations for: Swedish
Currently translated at 97.1% (1986 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-01-08 18:06:50 +01:00
Eranot 3b0b2a78d6 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 68.1% (1394 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2026-01-08 18:06:50 +01:00
Alejandro Alonso 10bf4610df 🌐 Add translations for: Hausa
Currently translated at 60.6% (1241 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2026-01-08 18:06:50 +01:00
Andy Li 77e8414aea 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 78.1% (1599 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-01-08 18:06:50 +01:00
bingling_sama 20ecf3b066 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 88.2% (1804 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2026-01-08 18:06:50 +01:00
Amerey.eu 49b1032973 🌐 Add translations for: Czech
Currently translated at 77.9% (1594 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2026-01-08 18:06:49 +01:00
Radek Sawicki 5ba7dd8c56 🌐 Add translations for: Polish
Currently translated at 55.2% (1130 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2026-01-08 18:06:49 +01:00
Ingrid Pigueron 38b5125186 🌐 Add translations for: French
Currently translated at 94.5% (1934 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-01-08 18:06:49 +01:00
Vint Prox 6677ae83d4 🌐 Add translations for: Russian
Currently translated at 77.3% (1582 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-01-08 18:06:49 +01:00
Marius 0737c055f0 🌐 Add translations for: German
Currently translated at 93.2% (1906 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-01-08 18:06:49 +01:00
Dário 4b88748fe3 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 76.8% (1571 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2026-01-08 18:06:49 +01:00
Denys Kisil 92107e5b1e 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 88.8% (1818 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-01-08 18:06:49 +01:00
Shuaib Zahda ebc0e3a23c 🌐 Add translations for: Arabic
Currently translated at 55.0% (1126 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2026-01-08 18:06:49 +01:00
VKing9 ebe4f2da50 🌐 Add translations for: Hindi
Currently translated at 97.1% (1986 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-01-08 18:06:48 +01:00
Vincas Dundzys a07c1d6eaa 🌐 Add translations for: Lithuanian
Currently translated at 5.7% (118 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lt/
2026-01-08 18:06:48 +01:00
Ahmad HosseinBor 613bfda955 🌐 Add translations for: Persian
Currently translated at 38.2% (782 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2026-01-08 18:06:48 +01:00
AlexTECPlayz f7ef6618e5 🌐 Add translations for: Romanian
Currently translated at 94.8% (1940 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2026-01-08 18:06:48 +01:00
Sebastiaan Pasma fe334d9cbe 🌐 Add translations for: Dutch
Currently translated at 97.1% (1986 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-01-08 18:06:48 +01:00
Revenant 268b883c73 🌐 Add translations for: Malay
Currently translated at 32.8% (672 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ms/
2026-01-08 18:06:48 +01:00
Zvonimir Juranko f6a4effa29 🌐 Add translations for: Croatian
Currently translated at 78.1% (1599 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2026-01-08 18:06:48 +01:00
Yessenia Villarte Vaca ced848077e 🌐 Add translations for: Spanish (Latin America)
Currently translated at 6.4% (131 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es_419/
2026-01-08 18:06:48 +01:00
Alexis Morin 7d9d318539 🌐 Add translations for: French (Canada)
Currently translated at 12.5% (257 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 18:06:48 +01:00
Oğuz Ersen 9781fceadb 🌐 Add translations for: Turkish
Currently translated at 97.1% (1986 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-01-08 18:06:48 +01:00
Yaron Shahrabani 3178bd9a27 🌐 Add translations for: Hebrew
Currently translated at 97.0% (1984 of 2045 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-01-08 18:06:47 +01:00
Andrey Antukh e5d677f449 🌐 Validate and rehash translation files 2026-01-08 18:05:51 +01:00
Andrey Antukh 6bf928893c Merge pull request #8000 from penpot/luis-radio-buttons-ds
♻️ Replace some components with DS ones
2026-01-08 18:04:20 +01:00
Andrey Antukh 1ae0f3fc87 Merge pull request #8037 from penpot/niwinz-staging-project-name-fix
🐛 Fix long project name visual problem on dashboard
2026-01-08 17:41:37 +01:00
Andrés Moya 53dd90aa24 🔥 Remove unused css (#8039) 2026-01-08 16:37:27 +01:00
Eva Marco e13c203b8d ♻️ Refactor scss file 2026-01-08 16:35:56 +01:00
Andrey Antukh 9fd0f6a8f3 📎 Fix integration tests 2026-01-08 16:02:52 +01:00
Andrey Antukh 638c3356d3 📎 Use correct casing on translation strings 2026-01-08 14:58:17 +01:00
Luis de Dios 6879f54e5d ♻️ Replace some components with DS ones 2026-01-08 14:52:25 +01:00
Andrey Antukh a71baa5a78 🌐 Rehash and validate translation files 2026-01-08 14:46:18 +01:00
Andrey Antukh 8e4a89bd1c Merge branch 'staging' into develop 2026-01-08 14:43:43 +01:00
Andrey Antukh 90efb665b5 Add several additional renames for make translation string consistent 2026-01-08 14:37:58 +01:00
Pablo Alba 47ee490158 🐛 Fix typos on download modal 2026-01-08 14:37:58 +01:00
Andrey Antukh f0f89599bc 🌐 Backport translations from develop 2026-01-08 14:08:02 +01:00
Hosted Weblate 7aad9da285 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2026-01-08 14:04:56 +01:00
Alexis Morin ab57a4ae52 🌐 Add translations for: French (Canada)
Currently translated at 12.9% (259 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:48 +01:00
Alexis Morin 266ee29bb9 🌐 Add translations for: French (Canada)
Currently translated at 9.2% (184 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:48 +01:00
Alexis Morin 69ca86bb6c 🌐 Add translations for: French (Canada)
Currently translated at 7.3% (147 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:48 +01:00
Alexis Morin ee14a845fc 🌐 Add translations for: French (Canada)
Currently translated at 3.1% (62 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:48 +01:00
Yaron Shahrabani 73639f5d16 🌐 Add translations for: Hebrew
Currently translated at 99.7% (1992 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-01-08 14:04:48 +01:00
Yaron Shahrabani 9bd106b2bc 🌐 Add translations for: Hebrew
Currently translated at 99.4% (1986 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-01-08 14:04:47 +01:00
Alexis Morin 59c75afc7b 🌐 Add translations for: French (Canada)
Currently translated at 1.0% (21 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:47 +01:00
Nicola Bortoletto bbc81586e3 🌐 Add translations for: Italian
Currently translated at 99.7% (1992 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-01-08 14:04:47 +01:00
Anton Palmqvist c9c30eab75 🌐 Add translations for: Swedish
Currently translated at 99.8% (1994 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-01-08 14:04:47 +01:00
Alexis Morin 86ba9280db 🌐 Add translations for: French (Canada)
Currently translated at 0.3% (6 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-01-08 14:04:47 +01:00
Vin 5800cc4bb2 🌐 Add translations for: Russian
Currently translated at 79.2% (1583 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-01-08 14:04:47 +01:00
andy aa29a34c4c 🌐 Added translation for: French (Canada) 2026-01-08 14:04:47 +01:00
Edgars Andersons 3276129cc7 🌐 Add translations for: Latvian
Currently translated at 93.9% (1876 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-01-08 14:04:47 +01:00
VKing9 67a96de475 🌐 Add translations for: Hindi
Currently translated at 100.0% (1997 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-01-08 14:04:47 +01:00
Stephan Paternotte 48785b4846 🌐 Add translations for: Dutch
Currently translated at 99.8% (1994 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-01-08 14:04:46 +01:00
Oğuz Ersen 3f0573f95d 🌐 Add translations for: Turkish
Currently translated at 99.8% (1994 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-01-08 14:04:46 +01:00
Andrey Antukh d94a2a8881 Merge branch 'staging-render' into develop 2026-01-08 13:59:01 +01:00
Andrey Antukh 1c237a0968 Merge branch 'staging' into staging-render 2026-01-08 13:58:48 +01:00
Andrey Antukh b0dc7d6ffb 🔧 Change default jmx port on deps.edn 2026-01-08 13:56:22 +01:00
Elena Torró b7eaeffa88 Merge pull request #8024 from penpot/azazeln28-issue-12835-fix-previous-styles-lost
🐛 Fix previous styles lost when changing selected text
2026-01-08 13:49:06 +01:00
Andrey Antukh 722fcc1f82 Merge remote-tracking branch 'origin/staging' into develop 2026-01-08 13:48:21 +01:00
Andrey Antukh b7cd315872 🐛 Fix wasm-playground on devenv 2026-01-08 13:48:09 +01:00
Andrés MoyaandAkshay Gupta 2ad42cfd9b Add ability to remap tokens when renamed ones are referenced by other child tokens (#8035)
* 🎉 Add ability to remap tokens when renamed ones are referenced by other child tokens

Signed-off-by: Akshay Gupta <gravity.akshay@gmail.com>

* 🐛 Fix remap skipping tokens with same name in different sets

* 📚 Update CHANGES.md

* 🔧 Fix css styles

---------

Signed-off-by: Akshay Gupta <gravity.akshay@gmail.com>
Co-authored-by: Akshay Gupta <gravity.akshay@gmail.com>
2026-01-08 13:42:06 +01:00
Eva Marco 743d4e5c8d 🐛 Fix error on shadow token creation (#8029) 2026-01-08 13:26:01 +01:00
Andrey Antukh 97e4f4c424 🐛 Fix long project name visual problem on dashboard 2026-01-08 11:40:55 +01:00
Belén Albeza fb9560c315 🐛 Fix guides dropdown width (#8031)
* 🐛 Fix width of guides column dropdown

* ♻️ Remove deprecated tokens in css

* 🔧 Update changelog
2026-01-08 10:47:11 +01:00
Andrey Antukh 795f65632a 🐛 Fix wasm-playground on devenv 2026-01-08 10:42:37 +01:00
Alejandro Alonso d53c090900 Merge pull request #8028 from penpot/elenatorro-12956-fix-text-color-tokens
🐛 Fix missing text color token from selected shapes in selected colors list
2026-01-07 16:49:41 +01:00
Elena Torro 621e030095 🐛 Fix missing text color token from selected shapes in selected colors list 2026-01-07 16:41:25 +01:00
Alejandro Alonso 157e4aa2d0 Merge pull request #8025 from penpot/elenatorro-12951-fix-inner-text-shadow-token
🐛 Fix inner shadow selector on shadow token
2026-01-07 16:37:19 +01:00
Elena Torro 7cd2308f3b 🐛 Fix inner shadow selector on shadow token 2026-01-07 16:36:51 +01:00
Alejandro Alonso c315a15b48 Merge pull request #8026 from penpot/elenatorro-12997-fix-clojure-on-css-box-shadow
🐛 Fix CSS generated box-shadow property
2026-01-07 16:32:12 +01:00
Elena Torro 8a3e6d026e 🐛 Fix CSS generated box-shadow property 2026-01-07 16:28:05 +01:00
Florian Schrödl 0dd062d011 🐛 Fix line-height throwing for int (#7927) 2026-01-07 16:13:10 +01:00
Alejandro Alonso bfbb546699 Merge pull request #8027 from penpot/superalex-fix-colors-assets-from-shared-libraries
🐛 Fix color assets from shared libraries
2026-01-07 14:16:57 +01:00
Alejandro Alonso 083e77e9c5 🐛 Fix color assets from shared libraries 2026-01-07 14:02:28 +01:00
Aitor Moreno 7819e6c440 🐛 Fix previous styles lost when changing selected text 2026-01-07 12:41:39 +01:00
Andrey Antukh 952f622ce9 🔧 Add 'Reapply` prefix to valid commit checker prefixes 2026-01-07 11:56:38 +01:00
Andrey Antukh a6c6f97f47 Reapply "💄 Group tokens by name path (#7775)"
This reverts commit eff572d3bb.
2026-01-07 11:55:56 +01:00
Andrey Antukh 88424eb54a Merge branch 'staging' into develop 2026-01-07 11:55:40 +01:00
Alejandro Alonso 919f78daeb Merge pull request #7965 from penpot/eva-fix-styles-on-viewer
🐛 Fix inspect tab styles on viewer
2026-01-07 11:54:33 +01:00
Eva Marco b5c30f8c41 🐛 Fix inspect tab styles on viewer 2026-01-07 11:41:49 +01:00
Alejandro Alonso 60aa426753 Merge pull request #8022 from penpot/alotor-fix-drag-handlers
🐛 Fix problem with dragging handlers
2026-01-07 11:36:26 +01:00
Alejandro Alonso 86f7d6b26b Sanitizing error values (#8020) 2026-01-07 11:23:19 +01:00
Andrey Antukh 36732a4bd3 Make the devenv runtine initialization yarn independent (#8023) 2026-01-07 11:21:58 +01:00
Andrey Antukh eff572d3bb Revert "💄 Group tokens by name path (#7775)"
This reverts commit 0956b66281.
2026-01-07 11:20:44 +01:00
alonso.torres d470d96833 🐛 Fix problem with dragging handlers 2026-01-07 11:00:02 +01:00
Aitor Moreno cab70773d2 Merge pull request #7667 from penpot/azazeln28-doc-add-more-info-text-editor-v2-readme
📚 Add more info about text editor v2
2026-01-07 09:40:06 +01:00
Alejandro Alonso de9a21121a Merge remote-tracking branch 'origin/staging' into develop 2026-01-05 13:22:14 +01:00
Alejandro Alonso 32ca42a093 Merge remote-tracking branch 'origin/staging-render' into staging 2026-01-05 13:21:58 +01:00
Alejandro Alonso 523a97a4ec Merge pull request #8016 from penpot/alotor-fix-refresh-thumbnails
🐛 Fix problem with thumbnail regeneration
2026-01-05 13:21:34 +01:00
Alejandro Alonso 260f6861a3 Merge pull request #8015 from penpot/alotor-fix-grid-component-auto-sizing
🐛 Fix problem with grid layout components and auto sizing
2026-01-05 13:18:59 +01:00
alonso.torres edd53b419a 🐛 Fix problem with thumbnail regeneration 2026-01-05 13:09:40 +01:00
Alejandro Alonso cea10308b7 Merge remote-tracking branch 'origin/staging' into develop 2026-01-05 11:52:15 +01:00
alonso.torres 078a3d5a5c 🐛 Fix problem with grid layout components and auto sizing 2026-01-05 10:54:36 +01:00
Alejandro Alonso c4e57427ac Merge branch 'staging-render' into staging 2026-01-05 10:30:06 +01:00
David Barragán Merino 5223c9c881 🔧 Fix a typo in an interpolation 2026-01-05 09:13:14 +01:00
Alejandro Alonso be62fa10c4 📎 Bump new version on changelog 2026-01-05 08:42:57 +01:00
Alejandro Alonso 7a6405481c Merge remote-tracking branch 'origin/develop' into staging 2026-01-05 08:37:19 +01:00
Alejandro Alonso 218f34380a Merge pull request #8012 from penpot/azazeln28-refactor-minor-changes
♻️ Minor naming changes and event handling
2026-01-02 14:16:55 +01:00
Alejandro Alonso 47aaa2b5fa Merge pull request #8011 from penpot/alotor-fix-trash-bar
🐛 Fix problems with trash bar in dashboard
2026-01-02 13:46:53 +01:00
Aitor Moreno 6c6b3db87e ♻️ Minor naming changes and event handling 2026-01-02 13:41:48 +01:00
Alejandro Alonso 6eb32cfb79 Merge pull request #8008 from penpot/alotor-fix-path-editor
🐛 Fix problem with path editor and right click
2026-01-02 13:29:44 +01:00
alonso.torres dbba3496af 🐛 Fix problems with trash bar in dashboard 2026-01-02 12:00:16 +01:00
alonso.torres 55752d361f 🐛 Fix problem with path editor and right click 2026-01-02 10:37:52 +01:00
Alejandro Alonso fe94ee4526 Merge pull request #8009 from penpot/alotor-fix-style-font-input
🐛 Fix problem with style in fonts input
2026-01-02 10:23:02 +01:00
Aitor Moreno e39f292499 📚 Add more info about text editor v2 2026-01-02 10:13:34 +01:00
Andrey Antukh 5c71c57dd9 Merge tag '2.12.1' 2025-12-30 15:37:30 +01:00
Andrey Antukh 52b8560b70 Merge branch 'staging-render' into develop 2025-12-30 15:30:56 +01:00
Andrey Antukh 75860afe57 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-30 15:29:58 +01:00
Andrey Antukh 824ca1bbca 🔧 Make devenv init yarn indpendent 2025-12-30 15:28:19 +01:00
Andrey Antukh 5b6f9c1741 📎 Disable legacy cache on plugins nx config 2025-12-30 14:56:15 +01:00
Andrey Antukh 19853b832b 📚 Update documentation 2025-12-30 14:56:15 +01:00
Andrey Antukh d20c011db2 Migrate plugins to pnpm 2025-12-30 14:56:15 +01:00
Andrey Antukh 9431ae6858 📎 Update docs 2025-12-30 14:56:15 +01:00
Andrey Antukh 96356c1b89 ⬆️ Update storybook and fix compatibility issues 2025-12-30 14:56:15 +01:00
Andrey Antukh b7b68eeb47 🔥 Remove npx prefix on package.json scripts 2025-12-30 14:56:15 +01:00
Andrey Antukh 9bbeb657f8 🔧 Add plugins runtime ci job 2025-12-30 14:56:15 +01:00
Andrey Antukh ec1af4ad96 🎉 Import penpot-plugins repository
As commit 819a549e4928d2b1fa98e52bee82d59aec0f70d8
2025-12-30 14:56:15 +01:00
alonso.torres 23e7116b24 🐛 Fix problem with style in fonts input 2025-12-30 14:28:10 +01:00
Alejandro Alonso 48e3f35bb3 🐛 Fix setting a portion of text as bold or underline messes things up 2025-12-30 11:34:24 +01:00
Andrey Antukh 6b794c9d12 Merge branch 'staging' into staging-render 2025-12-30 11:13:15 +01:00
Yamila Moreno d3ee50daf5 🔧 Add ci for branch staging-render 2025-12-30 11:13:00 +01:00
Yamila Moreno 22a36d59d8 🔧 Add ci for branch staging-render 2025-12-30 10:57:51 +01:00
Alejandro Alonso a948e49e51 🐛 Fix using cache on first zoom after pan 2025-12-30 10:03:24 +01:00
Alejandro Alonso d635f5a8dc 🐛 Detecting situations where WebGL context is lost or no WebGL support 2025-12-30 10:03:24 +01:00
Alejandro Alonso ab3a3ef43b 🎉 Resize cache only when required 2025-12-30 10:03:24 +01:00
Alejandro Alonso 9c21fd3359 🐛 Fix resize cache memory leak 2025-12-30 10:03:24 +01:00
Andrey Antukh 7b5817f407 ♻️ Make several adjustments to the dashboard deleted page (#7999)
* ♻️ Make several sustantial adjustments to the dashboard deleted page

* 📎 Add PR feedback changes
2025-12-30 09:52:29 +01:00
Yamila Moreno e3405eacca 🔧 Improve mattermost notification 2025-12-29 19:06:26 +01:00
Alejandro Alonso 44b70cf1d4 Merge pull request #7998 from penpot/alotor-fix-problem-with-create-grid
🐛 Fix problem creating grid from elements
2025-12-29 14:31:15 +01:00
Alejandro Alonso a8bd74b392 Merge pull request #8001 from penpot/alotor-fix-gfonts-references
🐛 Fix problem with some fonts
2025-12-29 14:25:54 +01:00
alonso.torres 3d3e3582d6 🐛 Fix problem with some fonts 2025-12-29 12:35:19 +01:00
Andrey Antukh de052b5161 📎 Update changelog 2025-12-29 11:10:04 +01:00
Andrey Antukh e01654ba43 Merge branch 'staging-render' into develop 2025-12-29 10:43:00 +01:00
Andrey Antukh 6ebd48b94c Merge branch 'staging' into staging-render 2025-12-29 10:41:08 +01:00
Andrey Antukh 8a3b33797f 🐛 Fix error handling on password change form
Fixes https://github.com/penpot/penpot/issues/7978
2025-12-29 10:27:27 +01:00
Andrey Antukh 13fd20f76f Backport form error management improvements from develop 2025-12-29 10:27:27 +01:00
alonso.torres 417cd80564 🐛 Fix problem creating grid from elements 2025-12-23 14:49:21 +01:00
Alejandro Alonso a57011ec7b Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-23 13:35:27 +01:00
Andrey Antukh 69c880d00e 🐛 Fix importmap usage on firefox 2025-12-23 13:10:58 +01:00
Andrey Antukh 9eebc467ef Preload default translations 2025-12-23 13:10:58 +01:00
Andrey Antukh b77712ce73 Move frontend/vendor to frontend/packages 2025-12-23 13:10:58 +01:00
Andrey Antukh 3d3e81f314 Replace tubax with more modern tooling 2025-12-23 13:10:58 +01:00
Andrey Antukh fe6441bb24 Replace hightlight.js internal bundle with direct npm use 2025-12-23 13:10:58 +01:00
Andrey Antukh e15f0baf30 Replace direct draft-js usage with internal module 2025-12-23 13:10:58 +01:00
Andrey Antukh c040cbb784 🔥 Remove old gulp related dependencies 2025-12-23 13:10:58 +01:00
Andrey Antukh 7f674b78a9 📎 Move all deps to dev-dependencies on frontend package.json
All they only needed for build process.
2025-12-23 13:10:58 +01:00
Andrey Antukh 099b78affd 📎 Update frontend yarn.lock file 2025-12-23 13:10:58 +01:00
Andrey Antukh 78cc3f0aa4 📎 Add immutable dependency to vendor/draft-js 2025-12-23 13:10:58 +01:00
Andrey Antukh 76f5f12808 ⬆️ Update dependencies on exporter 2025-12-23 13:10:58 +01:00
Alejandro Alonso cb325282ec Merge pull request #7994 from penpot/alotor-fix-font-style
🐛 Fix problem when changing colors with multiple fonts
2025-12-23 07:34:41 +01:00
Andrey Antukh 01ecde3bfa Add the ability to add relations on penpot sdk (#7987)
*  Add the ability to add relations on penpot sdk

* 📎 Remove debug console log
2025-12-22 20:55:31 +01:00
Andrey Antukh 047483a70a 🐛 Fix deleted files thumbnails generation 2025-12-22 20:20:43 +01:00
Alonso Torres 4000ec8762 🐛 Fix problem resizing auto size layouts (#7995) 2025-12-22 20:17:11 +01:00
Andrey Antukh 8cb2f27de8 ♻️ Move file permissions to binfile common ns 2025-12-22 20:16:41 +01:00
Andrey Antukh 0433336fc9 📎 Use correct criterium version on frontend deps 2025-12-22 20:16:41 +01:00
Andrey Antukh ce234fbeda Allow get thumbnails for deleted files 2025-12-22 20:16:41 +01:00
Andrey Antukh fc4d31eed7 Add minor efficiency improvements to deleted dashboard page 2025-12-22 20:16:41 +01:00
María Valderrama c670aac339 🎉 Added deleted files to dashboard 2025-12-22 20:16:41 +01:00
Andrés Moya 1d3fb5434f Enable shadow tokens by default (#7996) 2025-12-22 18:17:29 +01:00
Andrey Antukh f478399ae0 Merge remote-tracking branch 'origin/staging-render' into develop 2025-12-22 17:28:18 +01:00
Andrey Antukh 6a1854f180 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 17:28:01 +01:00
Andrés Moya 0858e297e5 🎉 Add composite tokens to plugins API (#7992) 2025-12-22 17:14:54 +01:00
alonso.torres bd580ab159 🐛 Fix problem when changing colors with multiple fonts 2025-12-22 17:14:37 +01:00
Alejandro Alonso 5780a43fe0 🐛 Fix object added in different page (#7988) 2025-12-22 16:59:47 +01:00
Alejandro Alonso 737eceda3a 🐛 Fix unmasking shapes (#7989) 2025-12-22 16:59:04 +01:00
Alonso Torres 923c3c2dbd 🐛 Fix font weight token (#7991) 2025-12-22 16:58:26 +01:00
Alejandro Alonso a14b4561e7 🐛 Fix comment bubbles (#7990) 2025-12-22 16:57:45 +01:00
Andrey Antukh bb5568e15a 🎉 Enable hindi translations on the application 2025-12-22 16:57:00 +01:00
Pablo Alba 5cbcec3db6 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:57:00 +01:00
Anonymous 105e1fe86c 🌐 Add translations for: Spanish
Currently translated at 97.1% (1940 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-12-22 16:34:43 +01:00
Yaron Shahrabani 3e0a916883 🌐 Add translations for: Hebrew
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-22 16:34:42 +01:00
Ahmad HosseinBor 4f80238bc2 🌐 Add translations for: Persian
Currently translated at 39.2% (783 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2025-12-22 16:34:42 +01:00
Alejandro Alonso 5156cc5d9a 🌐 Add translations for: Yoruba
Currently translated at 58.8% (1176 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-12-22 16:34:42 +01:00
Yessenia Villarte Vaca 42c46b6cfc 🌐 Add translations for: Spanish (Latin America)
Currently translated at 6.5% (131 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es_419/
2025-12-22 16:34:41 +01:00
VKing9 8b3c40b35e 🌐 Add translations for: Hindi
Currently translated at 99.6% (1991 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-22 16:34:41 +01:00
Andy Li d3996e5fb1 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 80.1% (1601 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-12-22 16:34:40 +01:00
Anonymous 0c42bca866 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Marius e5685c1f1c 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Anonymous 2784209bde 🌐 Add translations for: Turkish
Currently translated at 99.5% (1989 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-12-22 16:34:40 +01:00
Alejandro Alonso 024f460e99 🌐 Add translations for: Igbo
Currently translated at 25.6% (512 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2025-12-22 16:34:39 +01:00
Anonymous 1d9b76b62a 🌐 Add translations for: Romanian
Currently translated at 97.2% (1942 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-12-22 16:34:39 +01:00
Shuaib Zahda 7e17a75b7d 🌐 Add translations for: Arabic
Currently translated at 56.4% (1127 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-12-22 16:34:39 +01:00
Anonymous ca093d6fae 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Alexandre Pawlak 0f0b7562b5 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Anonymous 9cdc694697 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Dário b972a4033b 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Anonymous cbe9f4da51 🌐 Add translations for: Swedish
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-12-22 16:34:37 +01:00
Anonymous c583bde9e3 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:37 +01:00
Vint Prox 3911ebdc4e 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:36 +01:00
Anonymous 3e3b18667b 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 69.9% (1396 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-12-22 16:34:36 +01:00
Anonymous ed81c9b8df 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Sebastiaan Pasma fbdf98d29c 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Anonymous e603825a55 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Valentina Chapellu 1d724783e6 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Hosted Weblate e0abe7dcb5 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-12-22 16:33:05 +01:00
Andrey Antukh 5c1bbf5be8 Merge remote-tracking branch 'weblate/develop' into develop 2025-12-22 16:32:30 +01:00
Pablo Alba bbb0d58190 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:27:10 +01:00
Andrey Antukh 88dcf9d1fe 🐛 Mark rpc calls as authenticated when shared key is used (#7901) 2025-12-22 12:18:36 +01:00
Alejandro Alonso fe44c14bac Merge pull request #7982 from penpot/niwinz-staging-import-bucket
🐛 Prefill storage object bucket if it comes nil on import binfile
2025-12-22 12:17:16 +01:00
Belén Albeza 20061067ad 🐛 Fix text editor not getting focus back after font variant change 2025-12-22 11:18:25 +01:00
Andrey Antukh 336173645e 🐛 Fix regression on export shape on plungins API 2025-12-22 10:41:42 +01:00
Andrey Antukh 2acf15958b Merge branch 'staging-render' into develop 2025-12-22 09:24:04 +01:00
Andrey Antukh 08267de242 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 09:23:48 +01:00
Pablo Alba 35fb376a78 Add proxypass to caddyfile on devenv (#7985) 2025-12-22 09:21:22 +01:00
Dalai FelintoandDalai Felinto 13fcf3a9bb 💄 Set import Tokens default option to be Single JSON value (#7918)
This patches makes the default Tokens importing option to match the
current default Tokens exporting option (single JSON value). This way it
is more obvious and quick to export the tokens from a file and import
in new one,

---

While testing our design system we are often re-exporting and
re-importing the Tokens to the files using the design system components.

I'm aware that this may be addressed in the future so the Tokens are
brought in together with the library. Meanwhile (and even in the future)
I think it is sensible to have a symmetry between the export and import
defeault options.

Co-authored-by: Dalai Felinto <dalai@blender.org>
2025-12-19 10:44:05 +01:00
Andrey Antukh 83bb4bf221 🐛 Prefill storage object bucket if it comes nil on import binfile 2025-12-19 09:32:51 +01:00
Henrik Steffens dba6ae2820 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:25 +01:00
Marius ada101c236 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:24 +01:00
Marius ea48fb5825 🌐 Add translations for: German
Currently translated at 90.4% (1804 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-16 11:00:25 +01:00
Alejandro Alonso 15ed25ca79 Merge pull request #7966 from penpot/niwinz-staging-abrreviate
🐛 Fix incorrect string truncation with abbreviate template filter
2025-12-12 13:53:33 +01:00
Andrey Antukh 9aa387a473 🐛 Fix incorrect string truncation with abbreviate template filter 2025-12-12 13:50:46 +01:00
Alejandro Alonso 67ba91b4b9 Merge pull request #7971 from penpot/niwinz-staging-bugfix-6
🐛 Fix tokens-lib encoding when value is nilable
2025-12-12 13:46:06 +01:00
Alejandro Alonso f67f1a6a0e Merge pull request #7972 from penpot/niwinz-staging-bugfix-7
🐛 Fix exception on assinging gradient to shadow on multiple selection
2025-12-12 13:42:39 +01:00
Alejandro Alonso 82d3e2024e Merge pull request #7973 from penpot/niwinz-staging-worker-scheduler
🐛 Fix incorrect redis connection error handling
2025-12-12 13:23:49 +01:00
Alejandro Alonso 4bd846c16d Merge pull request #7969 from penpot/niwinz-staging-fix-ratelimit
🐛 Fix issue on reading rlimit config
2025-12-12 13:22:53 +01:00
alonso.torres 8fde6b28ed 🐛 Fix problems with alignments and margins 2025-12-12 13:21:04 +01:00
alonso.torres 63325ec796 🐛 Fix problem with flex fill size distribution 2025-12-12 13:21:04 +01:00
alonso.torres 84415476d0 🐛 Fix problem with reflow layout 2025-12-12 13:21:04 +01:00
Andrey Antukh 94f95ca6b8 🐛 Fix incorrect redis connection error handling 2025-12-12 12:33:38 +01:00
Andrey Antukh 33c786498d Merge remote-tracking branch 'origin/staging-render' into develop 2025-12-12 12:19:49 +01:00
Andrey Antukh 5abc1aafb4 Merge tag '2.12.0-RC3' 2025-12-12 12:19:29 +01:00
Andrey Antukh 1f886b1f88 Merge remote-tracking branch 'origin/staging' into develop 2025-12-12 12:16:41 +01:00
Aitor Moreno 5a922c6bd6 Merge pull request #7960 from penpot/superalex-fix-too-many-active-webgl-contexts
🐛 Fix too many active WEBGL contexts
2025-12-12 12:03:46 +01:00
Andrey Antukh 507bf7445b 🐛 Fix tokens-lib encoding when value is nilable 2025-12-12 11:42:15 +01:00
Andrey Antukh 81b72c5acd 🐛 Fix exception on assinging gradient to shadow on multiple selection 2025-12-12 11:24:53 +01:00
Alejandro Alonso 1388865cfc 🐛 Fix too many active WEBGL contexts 2025-12-12 11:16:47 +01:00
Andrey Antukh 1738847694 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-12 10:20:21 +01:00
Aitor Moreno ca1c3c799d Merge pull request #7968 from penpot/alotor-fix-border-radius
🐛 Fix problem with border radius to path
2025-12-12 10:18:07 +01:00
Andrey Antukh 974495e08f Reduce log level for profile picture download error
Because it is not blocking operation and does not provents user
to proceed.
2025-12-12 08:17:13 +01:00
Andrey Antukh 2ed39e43c3 🐛 Fix issue on reading rlimit config 2025-12-11 23:50:01 +01:00
alonso.torres ce5006ae84 🐛 Fix problem with border radius to path 2025-12-11 22:40:44 +01:00
Eva Marco 50dbe6ab12 🐛 Fix horizontal scroll on layer panel (#7956) 2025-12-11 21:34:18 +01:00
Belén Albeza 0a7a65af5d ♻️ Make SerializableResult to depend on From traits 2025-12-11 16:00:03 +01:00
alonso.torres ea4d0e1238 Calculate position data in wasm 2025-12-11 16:00:03 +01:00
Elena Torro b705cf953a 🐛 Set layout data from set-object 2025-12-11 14:52:32 +01:00
Alejandro Alonso 90ce1f56e7 Merge pull request #7958 from penpot/superalex-fix-svg-extract-ids
🐛 Fix svg extract ids
2025-12-11 14:02:05 +01:00
Alejandro Alonso ab0438cc6f 🐛 Fix svg extract ids 2025-12-11 13:47:00 +01:00
Aitor Moreno c6aa9cc4b7 Merge pull request #7950 from penpot/ladybenko-12851-fix-text-selection
🐛 Fix text selection when editor regains focus
2025-12-11 13:45:29 +01:00
Andrey Antukh 5779adef33 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-11 13:30:59 +01:00
Andrey Antukh 2f46cbc0d4 Make render wasm import on worker http cache aware 2025-12-11 13:27:20 +01:00
Elena Torró ebf1758958 Merge pull request #7935 from penpot/superalex-improve-svg-import
🎉 Improve svg import
2025-12-11 13:21:29 +01:00
Elena Torró e94c56bfa7 Merge pull request #7954 from penpot/azazeln28-fix-font-weight-mixed-value
🐛 Fix font weight mixed value
2025-12-11 12:43:53 +01:00
Andrey Antukh 53be6f996b 🐛 Fix issues on build processs related to render-wasm 2025-12-11 12:41:19 +01:00
Alejandro Alonso 89d9591011 🎉 Improve svg import 2025-12-11 12:02:34 +01:00
Andrey Antukh 5a260294a1 🔧 Update build-tag.yml github workflow 2025-12-11 12:00:42 +01:00
Andrey Antukh 3becfcd723 🔧 Update build-tag.yml github workflow 2025-12-11 11:59:16 +01:00
Andrey Antukh 3f6e44316e 🐛 Add missing node depes install on render-wasm 2025-12-11 11:51:47 +01:00
Aitor Moreno 5501a2815f 🐛 Fix font-variant-id mixed value 2025-12-11 11:32:27 +01:00
Eva Marco 77ef8e6fe6 🐛 Fix scroll on move library modal (#7952) 2025-12-11 10:46:54 +01:00
Alejandro Alonso 1066438b02 Merge pull request #7922 from penpot/elenatorro-12855-improve-pan-rendering
🔧 Improve pan rendering
2025-12-10 15:58:59 +01:00
Alejandro Alonso 3b23a3ad19 Merge pull request #7947 from penpot/elenatorro-12880-fix-variant-ui
🔧 Support variants interactivity on the new render's UI
2025-12-10 15:27:48 +01:00
Andrey Antukh 7396f4bfb6 Merge remote-tracking branch 'origin/staging' into develop 2025-12-10 15:17:50 +01:00
Alejandro Alonso 916b7709dc Update Pencil Penpot Design System System template in carousel (#7948) 2025-12-10 15:09:28 +01:00
Belén Albeza 5cf51f3d26 🐛 Fix text selection not being restore if it was only 1 word 2025-12-10 15:05:13 +01:00
Belén Albeza 25acad5154 🔧 Add formatting rules to the TextEditor 2025-12-10 15:04:34 +01:00
Elena Torro 0a212b6291 🔧 Support variants interactivity on the new render's UI 2025-12-10 14:39:59 +01:00
Eva Marco 443e41fea4 🐛 Fix multiple selection with color tokens (#7941) 2025-12-10 14:36:08 +01:00
Alejandro Alonso c7c9b04095 Merge pull request #7944 from penpot/niwinz-staging-exporter-fix
🐛 Fix incorrect resource lifetime handling on exporter
2025-12-10 14:35:20 +01:00
Eva Marco c61a0c0332 📚 Add line to changelog (#7945) 2025-12-10 13:58:18 +01:00
Andrey Antukh eb1eeb4750 Merge remote-tracking branch 'origin/staging-render' into niwinz-develop-merge 2025-12-10 13:53:15 +01:00
Andrey Antukh a78477592b Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-10 13:36:23 +01:00
Eva Marco 8707ff6511 🎉 Add spanish translation 2025-12-10 13:12:30 +01:00
Florian Schroedl 3d8a251741 🐛 Disallow font-family referencing composite token 2025-12-10 13:12:30 +01:00
Andrey Antukh 34e84ee3c8 🐛 Fix incorrect resource lifetime handling on exporter 2025-12-10 13:02:31 +01:00
Xaviju 0956b66281 💄 Group tokens by name path (#7775)
* 💄 Group tokens by name path
2025-12-10 12:34:19 +01:00
Luis de Dios 007b3f11f9 🐛 Fix pass new icons to radio buttons (#7939) 2025-12-10 12:28:27 +01:00
Alejandro Alonso e8201402a7 Merge pull request #7938 from penpot/niwinz-staging-bugfix-5
🐛 Fix several issues
2025-12-10 12:05:42 +01:00
Aitor Moreno 8a22477b96 Merge pull request #7932 from penpot/niwinz-staging-worker-wasm-load
🐛 Fix WASM loading strategy on worker
2025-12-10 11:47:31 +01:00
Elena Torro a661b2564f 🐛 Fix default case on vertical align 2025-12-10 10:59:27 +01:00
Elena Torro 2c3732f3f4 🔧 Fix line height calculation 2025-12-10 10:59:27 +01:00
Andrey Antukh e16645227b Merge branch 'staging-render' into develop 2025-12-10 10:10:44 +01:00
Andrey Antukh 45665a3c21 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-10 10:09:58 +01:00
Alejandro Alonso 3e684ea54f ⬆️ Update svgo dependency on frontend (#7936) 2025-12-10 10:07:02 +01:00
Eva Marco 179e6a195d 🎉 Add test for token creation (#7915) 2025-12-10 09:56:21 +01:00
Andrey Antukh 98039f13d8 🐛 Fix main toolbar z-index 2025-12-10 09:47:40 +01:00
Alejandro Alonso 40c27591f6 🐛 Fix svg import (#7925) 2025-12-10 08:36:54 +01:00
Andrey Antukh 91d20a46d1 💄 Add cosmetic changes to exports assets progress component 2025-12-10 08:23:05 +01:00
Andrey Antukh 50bead7c56 🐛 Fix react warning on having p inside p on assets export progress 2025-12-10 08:22:41 +01:00
Andrey Antukh b75b999903 📎 Fix devenv jvm warning 2025-12-10 08:22:05 +01:00
Andrey Antukh 810f1721c8 🐛 Fix recursion render on subscription modal 2025-12-10 07:54:52 +01:00
Stephan Paternotte b45bdd723f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-09 22:00:21 +00:00
Ingrid Pigueron 8696044620 🌐 Add translations for: French
Currently translated at 97.1% (1937 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-09 22:00:19 +00:00
Andrey Antukh 8a8f360c7f Merge remote-tracking branch 'origin/staging' into develop 2025-12-09 19:53:38 +01:00
Andrey Antukh a4646373cf ♻️ Refactor wasm loading strategy on worker 2025-12-09 19:41:19 +01:00
Andrey Antukh f111cbb2a4 Add better cache config on devenv nginx 2025-12-09 19:38:30 +01:00
Luis de Dios e35fc85c3d 🎉 Create new empty-state component (#7903) 2025-12-09 16:48:12 +01:00
Aitor Moreno a614207f7e 🐛 Fix exporter failing with HTTPS 2025-12-09 16:08:20 +01:00
Elena Torro 81bc1bb0af 🔧 Log performance when building using profile-macros 2025-12-09 15:25:13 +01:00
Yamila Moreno 1798461d21 🐳 Add override for assets (#7926) 2025-12-09 14:55:21 +01:00
Luis de Dios 6ce3249c6d 🐛 Fix color format does not switch in the view mode (#7923)
* 🐛 Fix color format does not switch in the inspect mode of the view mode

* ♻️ Update components
2025-12-09 14:38:15 +01:00
Yamila Moreno dde0fddd6f 🐳 Add missing override to Dockerfile.frontend (#7920) 2025-12-09 12:08:46 +01:00
Aitor Moreno 7d36bc4025 Merge pull request #7907 from penpot/alotor-fix-export-text
🐛 Fix problem when exporting texts
2025-12-09 11:28:47 +01:00
Elena Torro b8feb6374d 🔧 Rebuild indices on zoom change, not pan 2025-12-09 11:26:03 +01:00
Elena Torro 0889df8e08 🔧 Skip slow operations on fast render 2025-12-09 11:26:03 +01:00
Andrey Antukh 4637aced8c Add support auto decoding and validation syntax for obj/reify 2025-12-09 11:13:06 +01:00
Andrey Antukh 9dfe5b0865 🐛 Fix inconsistencies on using obj/reify on plugins 2025-12-09 11:13:06 +01:00
Andrey Antukh 33bcc9544a Update frontend repl script 2025-12-09 11:13:06 +01:00
Andrey Antukh babd481b7f Make sm/coercer lazy 2025-12-09 11:13:06 +01:00
Andrey Antukh a9733c792d Make check-fn completly lazy 2025-12-09 11:13:06 +01:00
Belén Albeza 7be8ac3fd7 🐛 Fix internal error while importing a library 2025-12-09 11:10:32 +01:00
Pablo Alba b0351be724 🐛 Fix switch variants with paths 2025-12-09 11:08:55 +01:00
Elena Torro 9216d965ef 🔧 Update rendering settings to smooth render 2025-12-09 10:43:33 +01:00
Andrey Antukh d04fdb5fbd Make the dist bundle use consistent and cache-aware uris (#7911) 2025-12-09 08:05:28 +01:00
Ingrid Pigueron 4f3ca6422c 🌐 Add translations for: French
Currently translated at 96.8% (1931 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-08 19:40:14 +01:00
Nicola Bortoletto 1c03457fda 🌐 Add translations for: Italian
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-06 07:00:19 +00:00
Eva Marco 81e0e4f222 ♻️ Replace token form files (#7896)
* ♻️ Replace shadow form

* ♻️ Rename files and components

* ♻️ Replace offsetx and offsety names

* ♻️ Replace form file for new form component using new form system

* ♻️ Rename files and props
2025-12-05 17:04:07 +01:00
Andrey Antukh b8392b3731 🐛 Fix regression on sending team invitations (#7912) 2025-12-05 12:36:06 +01:00
Andrey Antukh 935728aa39 🔧 Backport build-tag github workflow from develop 2025-12-05 10:26:01 +01:00
Andrey Antukh 77dba477ca 🔧 Backport build-tag github workflow from develop 2025-12-05 10:25:03 +01:00
Eva Marco b6598d1f07 🐛 Fix scrollbar on color modal (#7906) 2025-12-05 09:55:41 +01:00
Yamila Moreno f13b3c8737 🔧 Fix bug in Github Actions (#7908) 2025-12-04 20:24:33 +01:00
alonso.torres 520e979363 🐛 Fix problem when exporting texts 2025-12-04 17:32:54 +01:00
Yamila Moreno a0f8559ffc 🔧 Add ci/cd for nitrate-module (#7905) 2025-12-04 16:02:29 +01:00
Xaviju bf1dc21c75 💄 Hide themes & sets panels when none active (#7902) 2025-12-04 14:11:57 +01:00
Alejandro Alonso 46c20a993f Merge pull request #7904 from penpot/niwinz-staging-fix-invitation-resend
🐛 Fix exception on resending invitation
2025-12-04 11:56:07 +01:00
Andrey Antukh 0e0106f69a 🐛 Add correct assertion on create-invitation fn 2025-12-04 11:38:32 +01:00
Andrey Antukh 19bb69cc60 Improve invalid schema error report 2025-12-04 11:38:16 +01:00
Alejandro Alonso 504eb70988 Merge pull request #7885 from penpot/niwinz-staging-bugfix-2
🐛 Make workspace palette reposition on left sidebar collapse
2025-12-04 11:19:20 +01:00
Andrey Antukh a38f425dd3 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-04 11:06:48 +01:00
Xaviju 75a2331edf 💄 Set low-emphasis color for both light/dark modes (#7884) 2025-12-04 11:04:07 +01:00
VKing9 74d4b9b045 🌐 Add translations for: Hindi
Currently translated at 100.0% (1994 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-04 11:00:32 +01:00
Alejandro Alonso c2b4c9907d Merge pull request #7886 from penpot/niwinz-staging-bugfix-3
🐛 Fix casing on a translation of export files modal option
2025-12-04 10:59:51 +01:00
Alejandro Alonso bd5bbcae26 Merge pull request #7894 from penpot/niwinz-staging-bugfix-4
🐛 Fix incorrect interaction betwen hower and scroll on assets sidebar
2025-12-04 10:58:54 +01:00
Andrey Antukh 84273508ad 🐛 Fix incorrect interaction betwen hower and scroll on assets sidebar 2025-12-04 10:56:29 +01:00
Andrey Antukh 9245ba6bc2 💄 Adapt component style for assets-local-library on sidebar assets 2025-12-04 10:55:57 +01:00
Andrey Antukh 4be046406d Pass direct args instead of a vector to toggle-values on sidebar assets 2025-12-04 10:55:57 +01:00
Alejandro Alonso 84c747cd31 Merge pull request #7883 from penpot/niwinz-staging-bugfix
🐛 Fix exception on paste text on comments input
2025-12-04 10:32:07 +01:00
Alejandro Alonso 0036a9a0cd Merge pull request #7865 from penpot/niwinz-staging-audit
 Add minor improvements to the audit module
2025-12-04 10:04:00 +01:00
Alejandro Alonso 2105c3a68c Merge pull request #7866 from penpot/niwinz-staging-fix-emails
🐛 Change internal ordering on how email parts are assembled
2025-12-04 09:56:22 +01:00
Belén Albeza 38efa88460 🐛 Fix unpublish library modal not scrolling file list (#7892)
* 🐛 Fix unpublish library modal not scrolling when the linked files list is too long

* 💄 Remove deprecated tokens in unpublish library modal

* 🔧 Update CHANGELOG
2025-12-03 22:41:20 +01:00
Pablo Alba 6e254c2cf4 🐛 Fix change of library on swap (#7898) 2025-12-03 22:40:23 +01:00
Andrey Antukh 416980f063 🐛 Fix issue on render template on dist bundle (#7899) 2025-12-03 20:48:02 +01:00
Andrey Antukh f76710296c Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 18:52:28 +01:00
Andrey Antukh 6251fa6b22 🐛 Close other open context menus on open a context menu (#7895) 2025-12-03 18:50:00 +01:00
alonso.torres aedd8cc11e 🐛 Fix problem when renaming variants in plugins 2025-12-03 17:42:17 +01:00
Andrey Antukh d1379c55f6 Make i18n translation files load on demand 2025-12-03 16:44:37 +01:00
Andrey Antukh b125c7b5a3 Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 13:55:01 +01:00
Andrey Antukh 496d37795b Adapt docker images nginx config template to latest changes (#7891) 2025-12-03 13:45:18 +01:00
Alonso Torres 2f0853f5cc 🐛 Fix problem with variant plugins api (#7890) 2025-12-03 13:27:32 +01:00
Juan de la CruzandAndrey Antukh 648e660bcf 🎉 Add new content and images for the slides of 2.12 (#7874)
* 🎉 Add new slide's content

* 🎉 Add new slides images

* 📎 Fix clj fmt

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-12-03 13:26:55 +01:00
Andrey Antukh 9f6899007a Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 13:10:30 +01:00
Slava "nerfur" Voronzoff bee2f70bfa 🐛 Add missing amd64 option on arch detection code on mange.sh script
Signed-off-by: Slava "nerfur" Voronzoff <nerfur@gmail.com>
2025-12-03 13:09:49 +01:00
Pablo Alba 00f8eac8fa 🐛 Fix can't delete unsaved variant prop (#7878) 2025-12-03 13:03:17 +01:00
Dalai FelintoandDalai Felinto df7caacb45 🐛 Fix crash in token grid view due to tooltip validation (#7887)
The color tokens in grid view have a tooltip which is a map.
This is done so the frontend can render:

```
Name: foo
Resolved value: #000000
```

However the validation scheme for tooltips was only accepting functions
and strings.

---

How to reproduce the original (unreported) crash:
* Create a color token
* Create a shape, add a fill
* Pick a color, chose the Token options
* Click on the Grid View

Crash: `{:hint "invalid props on component tooltip*\n\n  -> 'content'
    should be a string\n"}`

Signed-off-by: Dalai Felinto <dalai@blender.org>
Co-authored-by: Dalai Felinto <dalai@blender.org>
2025-12-03 13:01:36 +01:00
Marina López 641df77834 🐛 Fix wrong board size presets in Android (#7888) 2025-12-03 12:52:47 +01:00
Marina López 49bbdfb257 🐛 Fix U and E icon displayed in project list (#7875)
* 🐛 Fix U and E icon displayed in project lis

* 🐛 Fix U and E icon displayed in project list
2025-12-03 12:50:51 +01:00
Aitor Moreno 4e84deca44 Merge pull request #7879 from penpot/elenatorro-12797-fix-update-spans
🐛 Fix paragraph with text spans with multiple styles
2025-12-03 11:30:17 +01:00
Aitor Moreno 0d21e52068 🐛 Fix applyStylesTo entire selection 2025-12-03 11:07:33 +01:00
alonso.torres 1b29e9a50f 🐛 Fix race condition with fix fonts patch 2025-12-03 10:39:05 +01:00
Andrey Antukh 94af978be8 🐛 Fix casing on a translation of export files modal option 2025-12-03 10:22:45 +01:00
Andrey Antukh feababe2a8 🐛 Make workspace palette reposition on left sidebar collapse 2025-12-03 09:56:14 +01:00
Andrey Antukh 5ef06685fc 💄 Add cosmetic improvements to workspace palette component 2025-12-03 09:38:23 +01:00
Elena Torro 9f567c3bf4 🐛 Fix italic variant 2025-12-03 08:59:25 +01:00
Elena Torro 1ba15e5d10 🐛 Do not merge fill styles 2025-12-03 08:55:11 +01:00
Andrey Antukh 57fcec5afc 🐛 Make from-synthetic-clipboard-event function return always a stream
Causes an execption on steam processing when it returns nil
2025-12-03 08:32:38 +01:00
Andrey Antukh 58f82da61e 🐛 Fix exception on paste text on comments input 2025-12-03 08:20:58 +01:00
Andrey Antukh a28c5b61ca 💄 Adapt viewport paste code codestyle
And remove some not necessary constructions
2025-12-03 08:09:13 +01:00
Yaron Shahrabani 60df56caa3 🌐 Add translations for: Hebrew
Currently translated at 99.6% (1988 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-02 21:00:32 +00:00
Andrey Antukh 53aad7bc15 Merge remote-tracking branch 'origin/staging' into develop 2025-12-02 17:43:34 +01:00
Andrey Antukh 9123d199b7 🐛 Fix scripts/fmt 2025-12-02 17:43:21 +01:00
alonso.torres 37e45a8bbf 🐛 Fix race condition with text and type 2025-12-02 17:28:20 +01:00
alonso.torres 3471d40f46 🐛 Fix problem with boolean shapes updates 2025-12-02 17:28:20 +01:00
Elena Torro c6b64a8e39 🐛 Fix selectAll on mixed span styles 2025-12-02 16:50:48 +01:00
Elena Torro 511e80c948 🐛 Fix merge fill styles when there are multiple fills 2025-12-02 16:50:04 +01:00
Elena Torró f5a640d104 Merge pull request #7876 from penpot/ladybenko-12805-slow-loading
🐛 Fix viewport not being fully drawn on first load until a mouse …
2025-12-02 15:31:43 +01:00
Belén Albeza 3ae7c514e4 🐛 Fix viewport not being fully drawn on first load until a mouse hover 2025-12-02 15:06:28 +01:00
Andrey Antukh 57297741f5 Merge remote-tracking branch 'origin/staging' into develop 2025-12-02 13:28:50 +01:00
Andrey Antukh eeaf28bb25 📎 Disable caddy logging 2025-12-02 13:27:09 +01:00
Dalai Felinto d63d692d34 🐛 Fix mask issues with component swap #7675
The logic to swap a component would delete the swapped out component
first before bringing in the new one.

In the process of doing so, the sanitization code would unmask the
group, now orphan of its mask shape component, when it was the first
element of the group.

The fix  was to pass an optional argument to the generate-delete-shapes
function to ignore mask in special cases like this.

Signed-off-by: Dalai Felinto <dalai@blender.org>
2025-12-02 12:31:44 +01:00
alonso.torres fad9ed1c48 🐛 Fix problem with reordering layers 2025-12-02 12:27:00 +01:00
alonso.torres 0caaefefea 🐛 Fix outline with single click text creation 2025-12-02 11:08:58 +01:00
Elena Torro b179aa79b1 🐛 Fix create empty text on click regression 2025-12-02 11:08:58 +01:00
Andrey Antukh 6b8091bb90 Make devenv https and http2 capable (#7871)
Making it more similar on how it runs on production
environments and improves large amount of files loading
thanks to http2.
2025-12-02 10:49:37 +01:00
Andrey Antukh fe72d0af82 Add self-signed cert to caddy (#7872) 2025-12-02 10:45:26 +01:00
Aitor Moreno 405ddb60d8 🐛 Fix letter spacing applied to paragraph 2025-12-02 10:45:19 +01:00
Luis de Dios ef68081d1d 🎉 Add prototype tab UI tweaks (#7832)
* 🎉 Add prototype tab UI tweaks

* 📎 PR changes
2025-12-02 10:44:16 +01:00
Madalena MeloandAndres Gonzalez bba02473d5 📚 Update subtitles in the new user guide cards (#7823)
Co-authored-by: Andres Gonzalez <andres.gonzalez79@gmail.com>
2025-12-02 09:21:05 +01:00
Andrey Antukh 4ed49cdc5d Make devenv https and http2 capable (#7871)
Making it more similar on how it runs on production
environments and improves large amount of files loading
thanks to http2.
2025-12-01 20:43:23 +01:00
Elena Torró 95c0d42d5b Merge pull request #7868 from penpot/alotor-fix-flex-tools
🐛 Fix visual feedback on padding/margin/gaps modified
2025-12-01 17:51:44 +01:00
alonso.torres 721b337511 🐛 Fix visual feedback on padding/margin/gaps modified 2025-12-01 16:31:15 +01:00
Elena Torró 359379be09 Merge pull request #7867 from penpot/azazeln28-add-text-editor-v2-tests-to-staging
 Add text editor v2 integration tests
2025-12-01 16:11:25 +01:00
Aitor Moreno 876d5783cf Add text editor v2 integration tests 2025-12-01 15:56:52 +01:00
Elena Torro 786f73767b 🔧 Normalize font attributes to support old formats 2025-12-01 14:59:24 +01:00
Andrey Antukh 50f9eedcdf Merge remote-tracking branch 'origin/staging' into develop 2025-12-01 14:33:38 +01:00
Andrey Antukh 77c9d8a2c8 🐛 Revert exporter dockerfile changes 2025-12-01 14:32:00 +01:00
Andrey Antukh 95b7784a42 🐛 Change internal ordering on how email parts are assembled
This fixes the html email rendering on gmail. Other clients (like proton,
emailcatcher) properly renders html independently of the order of parts
on the multipart email structure but gmail requires that html should be
the last one.
2025-12-01 14:27:21 +01:00
Andrey Antukh 4690f740b9 Add minor improvements to the audit module 2025-12-01 13:57:55 +01:00
Xaviju 529c4eb38a 💄 Avoid code tab overflow (#7854) 2025-12-01 11:37:37 +01:00
Andrey Antukh c3a9919c4d 🐛 Fix typo on exporter dockerfile 2025-12-01 11:19:41 +01:00
Eva Marco efe74e62e8 🎉 Replace font family form (#7825) 2025-12-01 11:17:25 +01:00
Juanfran 10a2732a55 Merge pull request #7863 from penpot/niwinz-staging-improve-yarn-independency
 Use setup script on exporter instead of direct commands
2025-12-01 10:13:58 +01:00
Eva Marco 456afe46de 🎉 Replace font family form (#7784) 2025-12-01 10:11:29 +01:00
Andrey Antukh 4282cdcd2c Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-01 10:11:06 +01:00
Andrey Antukh 964ef799c2 🔥 Remove core.spec usage on common and frontend 2025-12-01 09:30:21 +01:00
Andrey Antukh d34b6b88b6 Remove malli dev stuff from cljs build
It only used on backend.
2025-12-01 09:30:21 +01:00
Andrey Antukh 9a58f0e954 🔧 Disable code motion on shadow config 2025-12-01 09:30:21 +01:00
Andrey Antukh adaf8be56d Use sm/coercer on app.render entry point 2025-12-01 09:30:21 +01:00
Andrey Antukh 2f1b99fa53 ♻️ Use ESM target for build frontend 2025-12-01 09:30:21 +01:00
Andrey Antukh 5080fcc594 🔥 Remove unused require of edn reader on loggin ns 2025-12-01 09:30:21 +01:00
Andrey Antukh ea2d3758f0 Merge remote-tracking branch 'origin/staging' into develop 2025-12-01 09:28:49 +01:00
Andrey Antukh 40e3617138 Use setup script on exporter instead of direct commands 2025-12-01 09:23:11 +01:00
Alejandro Alonso e889413f26 🐛 Fix nested shadows clipping 2025-12-01 09:22:23 +01:00
Andrey Antukh b18c421415 📎 Update .gitignore 2025-12-01 09:20:33 +01:00
Andrey Antukh e7029f2182 Make automatic workflows not dependent on yarn 2025-12-01 08:17:52 +01:00
Elena Torró 115273b478 Merge pull request #7852 from penpot/alotor-flex-issues
🐛 Fix flex problems in new render
2025-11-28 14:10:42 +01:00
Elena Torró fdddd3284a Merge pull request #7859 from penpot/ladybenko-12801-fix-mismatched-fonts
🐛 Fix mismatch between fonts for rendered and selected text when no fallback fonts apply
2025-11-28 14:10:17 +01:00
Belén Albeza 51385a04a0 🐛 Fix mismatch between fonts for rendered and selected text when no fallback fonts apply 2025-11-28 13:54:17 +01:00
Alonso Torres 2c3becb408 🐛 Fix problem with plugins content attribute (#7835) 2025-11-28 13:41:27 +01:00
Belén Albeza f96ed8ccd6 Fix playwright tests 2025-11-28 13:25:13 +01:00
Belén Albeza bda5de5c1b 🔧 Update google fonts list 2025-11-28 13:25:13 +01:00
Juanfran 94c15916e2 Merge pull request #7857 from penpot/niwinz-develop-prepare-for-pnpm
 Make automatic workflows not dependent on yarn
2025-11-28 13:07:30 +01:00
Andrey Antukh ed0f3c3595 Make automatic workflows not dependent on yarn 2025-11-28 12:26:56 +01:00
alonso.torres 59f3b4db4c 🐛 Fix problem with auto-size and element margins 2025-11-28 12:12:19 +01:00
alonso.torres 7ee03ad911 🐛 Fix problem with grid layout editor 2025-11-28 12:12:09 +01:00
alonso.torres 130b8c8214 🐛 Fix problems with flex layout in new render 2025-11-28 10:49:55 +01:00
alonso.torres 0198d41757 🐛 Fix crash when cleanup 2025-11-28 10:44:54 +01:00
alonso.torres 567a955151 🐛 Fix problem with change gap/margin/padding 2025-11-28 10:44:38 +01:00
Xaviju a4e6aa0588 💄 Limit inspect layer info message to avoid overflow (#7847) 2025-11-28 10:19:02 +01:00
Nicola Bortoletto 34da754357 🌐 Add translations for: Italian
Currently translated at 98.9% (1973 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-11-28 05:00:28 +00:00
alonso.torres c2014a37b4 🐛 Fix problem when pasting elements in reverse flex layout 2025-11-27 18:02:34 +01:00
alonso.torres 6611fbd13b 🐛 Fix problem when drag+duplicate a full grid 2025-11-27 18:02:34 +01:00
Andrey Antukh b5a6867058 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 18:01:08 +01:00
Andrey Antukh 7fe20b65dc 🔧 Add more cache efficient configuration for devenv nginx 2025-11-27 17:59:12 +01:00
Andrey Antukh e5638cd769 ⬆️ Update clojure tools version on devenv 2025-11-27 17:58:56 +01:00
Eva Marco 8e79dfcb82 🐛 Fix input variant 2025-11-27 17:54:11 +01:00
Eva Marco 508db99a57 🐛 Restore empty field error on dimension, text-case and color forms 2025-11-27 17:54:11 +01:00
Andrey Antukh 3c6c9894da 🐛 Restore empty value error on border radius token form 2025-11-27 17:54:11 +01:00
Andrey Antukh 972b23e6c0 🐛 Fix incorect pred build on ::sm/text schema 2025-11-27 17:54:11 +01:00
Andrey Antukh 28f550d533 🔥 Remove commented code 2025-11-27 17:54:11 +01:00
Elena Torró 2b20f75fd4 Merge pull request #7837 from penpot/ladybenko-12719-fix-editor-unicode-fonts
🐛 Fix editor not using fallback fonts for selected text
2025-11-27 17:37:00 +01:00
Belén Albeza 4d6d7a6a3d 🐛 Fix emoji font not being used as fallback in text editor dom 2025-11-27 17:23:20 +01:00
Andrey Antukh 0f88253dd5 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 16:11:36 +01:00
Andrey Antukh db1ab7be69 📎 Run worker bundling serially on devenv 2025-11-27 16:09:15 +01:00
Andrey Antukh fcbe9d92dc 🐛 Fix unexpected exception on rendering feedback email
Looks like a bug on selmer library
2025-11-27 16:09:15 +01:00
Andrey Antukh 9998ce0bb4 🔥 Remove fipps direct dependency 2025-11-27 16:09:15 +01:00
Andrey Antukh 6061391c89 Don't require cljs.analyzer api under cljs on data.macros
Reduces the final production bundle size
2025-11-27 16:09:15 +01:00
Andrey Antukh eabf6e36ed Remove a level of indentation on subscriptions-dashboard tests 2025-11-27 16:09:15 +01:00
Andrey Antukh 04274e53fa 📎 Fix advanced compilation warnings related to jsdoc 2025-11-27 16:09:15 +01:00
Andrey Antukh 52dd9271a9 🐛 Encode header values as strings on audit archive task 2025-11-27 16:09:15 +01:00
andrés gonzález 8f5a81e179 📚 Add info about boolean variants (#7828) 2025-11-27 16:03:11 +01:00
Alonso Torres a940c08da9 🐛 Fix problem with worker bundling in development (#7844) 2025-11-27 14:13:48 +01:00
Alejandro Alonso 3de4473251 Merge pull request #7845 from penpot/elenatorro-fix-case
🐛 Fix editor vertical align default case
2025-11-27 14:00:12 +01:00
Andrey Antukh 0735140f07 🔧 Change concurrency rules on tests github workflow 2025-11-27 13:46:48 +01:00
Elena Torro dc8a07099d 🐛 Fix vertical align default case 2025-11-27 13:38:51 +01:00
Andrey Antukh 8e3996fbb0 🔧 Change concirrency rules on tests github workflow 2025-11-27 13:16:08 +01:00
Alonso Torres 67762d9450 🐛 Fix problem with worker bundling in development (#7844) 2025-11-27 13:02:47 +01:00
Elena Torró 90dcf04fb0 Merge pull request #7841 from penpot/superalex-fix-boolean-operators-no-selection
🐛 Fix boolean operators no selection
2025-11-27 12:50:16 +01:00
Belén Albeza f84c236e02 🐛 Fix text editor v2 not using fallback fonts for selected text 2025-11-27 12:26:39 +01:00
Alejandro Alonso 63959a22cc 🐛 Fix svg attrs 2025-11-27 12:23:46 +01:00
Alejandro Alonso 8840246425 🐛 Fix bleeding masks 2025-11-27 12:23:46 +01:00
Alejandro Alonso 62ec66cd15 🔧 Adding more e2e tests for nested frames with clipping 2025-11-27 12:23:46 +01:00
Alejandro Alonso e3b87390f6 🐛 Fix nested shadows clipping 2025-11-27 12:23:46 +01:00
Alejandro Alonso d9ab28e6ed 🐛 Fix nested clipping 2025-11-27 12:23:46 +01:00
Belén Albeza 9183dbbc43 🔧 Fix lint error (rust) 2025-11-27 11:51:05 +01:00
Andrey Antukh 74d00473e9 Add missing render-wasm to the ci workflow 2025-11-27 11:51:05 +01:00
Alejandro Alonso 1c70f5a36b 🐛 Fix boolean operatos shown when there is no selection 2025-11-27 11:22:15 +01:00
Andrey Antukh b23e0c0642 Add tempfile storage bucket handler test case (#7839) 2025-11-27 10:27:57 +01:00
Andrey Antukh 7f62652870 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 09:24:40 +01:00
Marina López db0cbbbc2e 🐛 Fix logic preventing incorrect trial flow in subscription modal (#7831) 2025-11-26 12:08:02 +01:00
alonso.torres 48304bd26f 🐛 Fix issue when exporting files 2025-11-26 12:04:34 +01:00
Elena Torro 60e32bbc71 🐛 Fix text editor vertical align 2025-11-26 11:46:47 +01:00
André Carvalhais 54451608dc 💄 Fix spelling of 'smtp' in email configuration section
Corrected the spelling of 'smtp' in the documentation.

Signed-off-by: André Carvalhais <carvalhais@live.com>
2025-11-26 08:11:27 +01:00
Andrey Antukh 78d31ab11a 🐳 Update devenv docker and compose files
Reuse the already builded imagemagick instead of building
it again on the devenv.
2025-11-26 07:44:56 +01:00
Andrey Antukh 0a80c47901 Merge remote-tracking branch 'origin/staging' into develop 2025-11-26 07:30:42 +01:00
Alejandro Alonso b7727122d5 Merge pull request #7829 from penpot/alotor-fixes
🐛 Fix problem with thumbnails in parallel
2025-11-26 07:21:49 +01:00
alonso.torres 8880f07a6a 🐛 Fix problem with thumbnails in parallel 2025-11-25 17:56:00 +01:00
Oğuz Ersen 39eafae251 🌐 Add translations for: Turkish
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-11-25 16:51:25 +00:00
Edgars Andersons e1e09b7f96 🌐 Add translations for: Latvian
Currently translated at 94.0% (1876 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-25 16:51:24 +00:00
Stephan Paternotte 3b39980f2f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-25 16:51:23 +00:00
Anton Palmqvist 223b12d2c7 🌐 Add translations for: Swedish
Currently translated at 99.6% (1987 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-25 16:51:21 +00:00
andrés gonzález aaca2c41d8 📚 Add metadescriptions to some help center pages (#7821) 2025-11-25 17:00:14 +01:00
Yamila Moreno 77f1046fc8 🔧 Add MT notification when a docker image with final tag is built (#7824) 2025-11-25 16:39:42 +01:00
Belén Albeza 33417a4b20 🐛 Fix svg attrs stroke-linecap stroke-linejoin fill-rule 2025-11-25 12:43:40 +01:00
Andrés Moya 2640889dc8 🐛 Fix backwards compatibility importing files with token themes 2025-11-25 10:56:33 +01:00
alonso.torres dd5f3396d1 🐛 Fix problem with layout z-index 2025-11-24 17:48:58 +01:00
Andrey Antukh dedeae8641 🐛 Fix incorrect subscription fetching after profile registration 2025-11-24 14:36:46 +01:00
Andrey Antukh a7552d412a Add explicit network asingation and alias on devenv compose 2025-11-24 14:36:46 +01:00
Aitor Moreno f58475a7c9 🐛 Fix pasting application/transit+json (#7812) 2025-11-24 14:36:24 +01:00
Marina López 00bbb0bfb6 ♻️ Add format and refactor payments 2025-11-24 11:41:03 +01:00
Andrey Antukh d93fe89c12 📎 Backport CI github workflog from develop 2025-11-24 10:48:51 +01:00
Andrey Antukh 553b73a83c ♻️ Replace CircleCI with Github Actions (#7789)
* ♻️ Replace circleci with github actions

* 📎 Add integration test sharding

* 📎 Reuse single build for integration tests shards
2025-11-24 10:44:04 +01:00
Andrey Antukh 00a45cb274 📎 Bump new version on changelog 2025-11-24 09:47:00 +01:00
Andrey Antukh 6e44330af4 Merge remote-tracking branch 'origin/develop' into staging 2025-11-24 09:42:45 +01:00
Andrey Antukh 624805fd6b Merge remote-tracking branch 'weblate/develop' into develop 2025-11-24 09:32:06 +01:00
Eva MarcoandXavier Julian 9b6bb77422 Materialize several tokens related flags (#7773)
* 📚 Add line to changelog

* ♻️ Remove typography types flag

* ♻️ Remove composite typography token flag

* ♻️ Remove token units flag

* 🎉 Activate by default two token flags

* ♻️ Update inspect tab tests to navigate to the right info tab

* 🐛 Fix test

---------

Co-authored-by: Xavier Julian <xavier.julian@kaleidos.net>
2025-11-24 09:26:05 +01:00
Yamila Moreno 9b8e04bb3c 🐳 Remove minio service from docker-compose.yml (#7809) 2025-11-24 08:15:36 +01:00
Edgars Andersons 2e919809c9 🌐 Add translations for: Latvian
Currently translated at 94.1% (1873 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-23 12:51:20 +00:00
Nicola Bortoletto 645e123e3a 🌐 Add translations for: Italian
Currently translated at 98.8% (1967 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-11-23 12:51:17 +00:00
Oğuz Ersen cfb94d17b6 🌐 Add translations for: Turkish
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-11-22 10:51:22 +00:00
Keunes e9cb409ca4 🌐 Add translations for: Dutch
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-22 10:51:19 +00:00
jonnysemon 8a0cd75257 🌐 Add translations for: Arabic
Currently translated at 56.6% (1128 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-22 10:51:17 +00:00
Pablo Alba fae488b15a 🐛 Fix after changing a variant property value, the value appears as empty (#7791) 2025-11-21 17:51:12 +01:00
Elena Torró b82828632e Merge pull request #7807 from penpot/alotor-fix-hover-text
🐛 Fix hover text
2025-11-21 15:35:41 +01:00
alonso.torres bf24e22588 🐛 Fix hover text 2025-11-21 14:27:15 +01:00
Alejandro Alonso 7399b4d423 📚 Remove wrong line on CHANGES 2025-11-21 14:21:14 +01:00
Alejandro Alonso 77b9eee6bd 🐛 Fix svg fills defined in svg-attrs with url or color format 2025-11-21 14:15:27 +01:00
Elena Torro 55896db49e 🔧 Check for emtpy/nil attrs when getting inline style 2025-11-21 14:10:23 +01:00
Elena Torró f4c569d619 Merge pull request #7802 from penpot/alotor-fix-text-data-problem
🐛 Fix problems with text editor size
2025-11-21 13:41:38 +01:00
alonso.torres ca2cf18a49 🐛 Fix problems with text editor size 2025-11-21 13:17:43 +01:00
Andrey Antukh 6e352c167c 🐛 Fix dev build of frontend 2025-11-21 13:02:44 +01:00
Andrey Antukh 3ec001de44 🔧 Add nitrate url to devenv nginx (#7800) 2025-11-21 12:30:49 +01:00
Elena Torró a1f11c89f2 Merge pull request #7799 from penpot/alotor-fix-text-data-problem
🐛 Fix problem with text data serialization
2025-11-21 12:30:35 +01:00
alonso.torres 33d70f0e45 🐛 Fix problem with text data serialization 2025-11-21 12:07:01 +01:00
Elena Torró 4f24a8f5f1 Merge pull request #7770 from penpot/ladybenko-12587-fix-text-editor-crash-empty
🐛 Fix crash when using a font family with a number in its name
2025-11-21 12:02:40 +01:00
Andrey Antukh b03cfffb9e Restore the dashboard thumbnail rendering using wasm (#7796)
* Revert "🐛 Rollback esm worker (#7792)"

This reverts commit 0120a5335b.

* 🐛 Fix incorrect manifest reading on building worker
2025-11-21 11:42:40 +01:00
Elena Torró 956ad88e51 Merge pull request #7795 from penpot/alotor-fix-paste-crash
🐛 Fix paste crash
2025-11-21 11:00:00 +01:00
Belén Albeza 76f5c73de6 Remove leftover console.log/trace 2025-11-21 10:59:15 +01:00
Belén Albeza c6dd3e0eeb Add missing param to cut handler 2025-11-21 10:28:48 +01:00
alonso.torres fde73f30b9 🐛 Fix paste crash 2025-11-21 09:51:54 +01:00
Edgars Andersons 9d35a4317c 🌐 Add translations for: Latvian
Currently translated at 93.6% (1864 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-21 05:51:27 +00:00
jonnysemon e7ccfeccbf 🌐 Add translations for: Arabic
Currently translated at 56.6% (1128 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-21 05:51:26 +00:00
Stephan Paternotte aa043d284f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-21 05:51:21 +00:00
Alejandro Alonso 537dd171c0 Merge pull request #7793 from penpot/alotor-tiles-improvement
 Improve cache rendering
2025-11-20 18:50:24 +01:00
alonso.torres c2026918a4 Improve cache rendering 2025-11-20 17:33:37 +01:00
Alonso Torres 0120a5335b 🐛 Rollback esm worker (#7792) 2025-11-20 16:07:22 +01:00
Belén Albeza d0d2f43ca1 🐛 Fix text editor crash with font families with a number in their name 2025-11-20 15:22:40 +01:00
Alejandro Alonso 7e33a7c1a7 Merge pull request #7666 from penpot/azazeln28-feat-allow-disabling-rich-paste
🎉 Add an option to enable and disable HTML paste
2025-11-20 14:17:16 +01:00
Elena Torró c13b58f42a Merge pull request #7764 from penpot/superalex-fix-blurs
🐛 Fix shadows and blurs
2025-11-20 13:37:57 +01:00
alonso.torres a5c9f9e454 📚 Adds contributor to the changelog 2025-11-20 13:35:43 +01:00
Aitor Moreno d73be5832b 🎉 Add an option to enable and disable HTML paste 2025-11-20 13:33:51 +01:00
Alejandro Alonso e1f2fca4af Merge pull request #7771 from penpot/elenatorro-12541-improve-text-selection-and-cursor
 Improve text shape selection
2025-11-20 13:33:48 +01:00
Diana Veiga 37d5a31589 Drop zoom snap (#7774)
*  Remove const `zoom-half-pixel-precision`

* ♻️ Adjust usages
2025-11-20 13:28:45 +01:00
Luis de Dios 177bdaa72c 🐛 Fix variant toggle does not work for uppercase or mixed case (#7716)
* 🐛 Fix variant toggle does not work for uppercase or mixed case

* 📎 PR changes
2025-11-20 13:27:04 +01:00
Aitor Moreno 38ab2c61b9 Merge pull request #7782 from penpot/alotor-wasm-thumbnails
 Render WASM dashboard thumbnails
2025-11-20 13:12:26 +01:00
Marina López cc32b22e8a Add improvements to the payment flow (#7776)
*  Add improvements payment flow

* 📎 PR feedback

* 📎 Fix conflicts
2025-11-20 13:07:57 +01:00
Alejandro Alonso d331c5ad83 Merge pull request #7769 from penpot/niwinz-develop-exporter-refactor
 Remove exporter dependency on shared-fs on scaling
2025-11-20 12:44:34 +01:00
iPagar 6c6c2c3012 📚 Update copyright year on doc (#7502)
Signed-off-by: iPagar <iPagar@users.noreply.github.com>
2025-11-20 12:38:31 +01:00
Andrey Antukh 81632a03dd ♻️ Make exporter upload resources using backend management api
Instead of custon shared fs approach. This commit fixes the main
scalability issue of exporter removing the need of shared-fs
for make it work with multiple instances.
2025-11-20 12:20:13 +01:00
Andrey Antukh 4fddf3d986 ♻️ Make management key derivable from secret key
Still preserves the ability to set management
2025-11-20 12:20:13 +01:00
Andrey Antukh 57aa9a585b 🔧 Add explicit network alias for minio on devenv 2025-11-20 12:20:13 +01:00
Andrey Antukh f71f491590 🐛 Fix incorrect bearer token decoding 2025-11-20 12:20:13 +01:00
Andrey Antukh 6ae2401c5e ♻️ Change how shapes are validated after changes apply operation 2025-11-20 12:08:48 +01:00
Andrey Antukh 53d8a2d6d7 🔥 Remove obsolete code on :move-objects related to old components 2025-11-20 12:08:48 +01:00
Andrey Antukh bd65f3932e 🐛 Fix a race condition on move-object
That happens when an in-flight move-object change tries
to move object to an already deleted parent
2025-11-20 12:08:48 +01:00
alonso.torres 59845b756f Render WASM dashboard thumbnails 2025-11-20 11:56:25 +01:00
Alejandro Alonso b8c0c5c310 Merge pull request #7742 from penpot/alotor-plugins-improvements
 Plugin API improvements with images and indexes
2025-11-20 11:47:50 +01:00
Alejandro Alonso cfa8c21ee6 Merge pull request #7788 from penpot/elenatorro-fix-insert-shape-on-empty-frame
🐛 Fix insert shape on empty frame
2025-11-20 11:44:13 +01:00
Elena Torro 624bdaec88 Show text cursor in the entire text rect 2025-11-20 11:42:07 +01:00
Alejandro Alonso 24745bed40 🐛 Fix shadows and blurs for high levels of zoom 2025-11-20 11:25:23 +01:00
Eva Marco d26c08f8e2 ♻️ Replace token forms (#7759)
* 🎉 Create dimensions form

* 🎉 Create text-case form

* 🎉 Create color form

* ♻️ Remove unused code on form file
2025-11-20 11:04:39 +01:00
Elena Torro 36adbd9118 🐛 Fix insert shape on empty frame 2025-11-20 10:59:44 +01:00
Elena Torró 0a3fe9836a Merge pull request #7777 from penpot/superalex-fix-extrect-calculation
🐛 Fix extrect calculation
2025-11-20 09:57:59 +01:00
Andrey Antukh fef0c11503 🔧 Update tests github flow 2025-11-20 09:37:38 +01:00
Alejandro Alonso 7e858784a1 Merge pull request #7785 from penpot/niwinz-develop-binary-fills
🐛 Fix invalid fills schema when binary fills are used
2025-11-20 09:06:45 +01:00
Miguel de Benito Delgado 203368c2ee Add parameter to openPage to toggle new window behaviour (#7753)
*  Add parameter to openPage() to toggle opening a new tab/window

* 💄 Fix formatting
2025-11-20 08:05:08 +01:00
Alejandro Alonso 4f54469629 Merge pull request #7747 from penpot/niwinz-develop-storage-changes
 Make the binfile exportation process more reliable
2025-11-20 07:58:57 +01:00
Andrey Antukh 5343e799f8 🐛 Fix invalid fills schema when binary fills are used 2025-11-20 07:45:37 +01:00
Andrey Antukh 51e54a6bad 🐛 Fix incorrect project restoration on restoring file (#7778) 2025-11-19 18:24:24 +01:00
Aitor Moreno f609747322 🐛 Fix inert element error 2025-11-19 18:23:44 +01:00
Andrey Antukh 26ad039d99 ⬆️ Update playwright dependency on frontend 2025-11-19 18:23:44 +01:00
Andrey Antukh 3136096123 🔧 Add general improvements to integration tests
This commit marks as skip (temporal) several flaky/randomly-failing
tests.

It also moves the integration test execution from circleci to github
actions.
2025-11-19 18:23:44 +01:00
Andrey Antukh 122d3bc41c 💄 Add code formatting for js on frontend 2025-11-19 18:23:44 +01:00
Andrey Antukh 3b52051113 Fix closure compiler issues on clipboard js impl
With minor naming fixes
2025-11-19 18:23:44 +01:00
Aitor Moreno 32e1b55658 ♻️ Refactor clipboard 2025-11-19 18:23:44 +01:00
Andrey Antukh e9d177eae3 Make the binfile export process more resilent to errors
The current binfile export process uses a streaming technique. The
major problem with the streaming approach is the case when an error
happens on the middle of generation, because we have no way to
notify the user about the error (because the response is already
is sent and contents are streaming directly to the user
client/browser).

This commit replaces the streaming with temporal files and SSE
encoded response for emit the export progress events; once the
exportation is finished, a temporal uri to the exported artifact
is emited to the user via "end" event and the frontend code
will automatically trigger the download.

Using the SSE approach removes possible transport timeouts on export
large files by sending progress data over the open connection.

This commit also removes obsolete code related to old binfile
formats.
2025-11-19 17:28:55 +01:00
Andrey Antukh d42c65b9ca Improve logging on shape detach operation 2025-11-19 17:28:55 +01:00
Andrey Antukh 86ad56797b Simplify tempfile deletion handling
Mainly removes the jvm on-exit hook usage because it can lead
to slow stops and unnecesary memory consumption over the time
the jvm is running.
2025-11-19 17:28:55 +01:00
Andrey Antukh 63497b8930 Add tempfile bucket to the storage subsystem
This enables storing temporal files under storage subsystem. The
temporal objects (the objects that uses templfile bucket) will
always evaluate to "for deletion" after touched garbage collection;
and the deletion threshold will be 2 hours (the threshold is always
calculated from the instant when the touched garbage collector is
running).
2025-11-19 17:28:55 +01:00
Andrey Antukh 94719eebf8 ♻️ Make storage and other objects deletion task vclock aware
This simplifes the mental model on how it works and simplifies testing
of the related code.

This also normalizes storage object deletion in the same way as the
rest of objects in penpot (now future deletion date on storage object
also means storage object to be deleted).
2025-11-19 17:28:55 +01:00
Andrey Antukh 9532dea2c6 📎 Skip inspect integration tests (#7781) 2025-11-19 17:26:40 +01:00
Andrey Antukh 40e1e27bf0 🐛 Fix not covered case on schema decode fn on tokens-lib 2025-11-19 15:04:49 +01:00
Andrés Moya 4338f97e9f 🐛 Allow deleting the library in the undo change of add tokens-lib 2025-11-19 15:04:49 +01:00
Andrey Antukh 2c4ec43d5f 🐛 Fix invalid syntax on translation files 2025-11-19 15:03:26 +01:00
Andrey Antukh 3d782a322d 🐛 Fix issue related to labels.code on translations 2025-11-19 14:53:13 +01:00
Andrey Antukh 407d28d187 🌐 Rehash and sync translation files 2025-11-19 14:18:41 +01:00
Andrey Antukh bf582ec55f 🌐 Add several fixes on weblate merge 2025-11-19 13:25:11 +01:00
Andrey Antukh 858bc05ed5 Merge remote-tracking branch 'weblate/develop' into develop 2025-11-19 13:11:07 +01:00
Andrey Antukh cd01386210 📎 Set version 1.1.0 final to sdk/library 2025-11-19 13:04:29 +01:00
Xaviju 3b2bb5f225 ♻️ Follow translations guidelines on several inspect components (#7766)
Signed-off-by: Xaviju <xavier.julian@kaleidos.net>
2025-11-19 13:03:25 +01:00
Alejandro Alonso fe3bc96d0d Merge pull request #7772 from penpot/niwinz-develop-auth-bugfix
 Improvements to the auth internal flows changes
2025-11-19 12:46:10 +01:00
Alejandro Alonso 28f23f397e 🐛 Fix extrect calculation 2025-11-19 12:38:01 +01:00
Andrey Antukh a487dfe004 Add better approach for cookie token decoding
Remove unnecesary decoding for old tokens and add key identifier
and versioning to cookie tokens for handle future changes.
2025-11-19 07:47:52 +01:00
Andrey Antukh 4f29156929 📎 Add better formatting of public-uri on db report 2025-11-18 20:35:26 +01:00
Andrey Antukh ce2d3d1652 🐛 Fix incorrect handling of session renewal
A regression introduced in the prev auth refactor.
2025-11-18 20:35:16 +01:00
Andrey Antukh 3639ff9dbc 🔧 Update devenv logging configuration 2025-11-18 20:34:17 +01:00
Andrey Antukh ca5ec734a0 Merge remote-tracking branch 'origin/staging' into develop 2025-11-18 18:19:36 +01:00
Andrey Antukh b08da4c3ff Merge remote-tracking branch 'origin/main' into staging 2025-11-18 18:19:11 +01:00
Yamila Moreno c9bec3924d 🐳 Use the secret key both in the backend and the exporter (#7746) 2025-11-18 18:18:49 +01:00
Yamila Moreno 6e725a75e1 🐳 Use the secret key both in the backend and the exporter (#7746) 2025-11-18 18:17:34 +01:00
Anton Palmqvist 81c3b84972 🌐 Add translations for: Swedish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-18 14:52:28 +01:00
jonnysemon 5868f7f6b2 🌐 Add translations for: Arabic
Currently translated at 57.7% (1130 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-18 14:52:25 +01:00
Tiago José 653567d7de 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 71.6% (1402 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-11-18 14:51:51 +01:00
Alejandro Alonso ce651fa0a9 Merge pull request #7767 from penpot/alotor-fix-problem-compatibility
🐛 Fix problem with tainted canvas in thumbnails
2025-11-18 14:15:06 +01:00
alonso.torres e8a26ef83b 🐛 Fix problem with tainted canvas in thumbnails 2025-11-18 13:05:56 +01:00
alonso.torres 8fd17c9c84 🐛 Fix problem not checking feature flag 2025-11-18 13:05:29 +01:00
Xaviju 64b892f82d ♻️ Copy shorthands using user selected color space (#7752)
* ♻️ Copy shorthands using user selected color space

* ♻️ Add tests to ensure color space changes affect all properties
2025-11-18 10:54:10 +01:00
Alejandro Alonso 04185b3544 Merge pull request #7762 from penpot/alotor-fix-selection
🐛 Fix problem with selection and text shapes for new render
2025-11-18 10:39:36 +01:00
alonso.torres 0a01fc8af9 🐛 Fix problem with selection and text shapes for new render 2025-11-18 09:34:17 +01:00
Alejandro Alonso ae624b3728 Merge pull request #7760 from penpot/elenatorro-12533-fix-selection-and-paste-and-word-deletion
🐛 Fix text editor select all functionality and inner paste corner cases
2025-11-18 09:31:57 +01:00
Alejandro Alonso a48b719966 Merge pull request #7748 from penpot/elenatorro-12586-fix-offset-y-on-new-lines
🐛 Fix new lines spacing between paragraphs
2025-11-18 09:23:22 +01:00
Elena Torró 6425c0cb7d Merge pull request #7757 from penpot/superalex-fix-apply-shadow-and-blur-bounds
🐛 Fix apply shadow and blur bounds
2025-11-17 16:50:15 +01:00
Elena Torro 368f4cfe81 🐛 Fix text editor select all functionality and inner paste corner cases 2025-11-17 16:24:52 +01:00
Alejandro Alonso fdffa14d75 🐛 Fix apply shadow and blur bounds 2025-11-17 15:20:22 +01:00
Eva MarcoandAndrey Antukh 7fe965a870 🎉 Add new form system on workspace (#7738)
* 🎉 Add new form system on border-radius token modals

* ♻️ Create new namespace and separate components

* ♻️ Refactor submit button

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-11-17 13:44:56 +01:00
Anton Palmqvist d03f5c10fb 🌐 Add translations for: Swedish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-15 20:51:48 +00:00
Anton Palmqvist 3eb0f1c225 🌐 Add translations for: Swedish
Currently translated at 88.9% (1740 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-14 18:51:24 +01:00
Elena Torro 127fa931c7 🐛 Fix new lines spacing between paragraphs 2025-11-14 12:00:39 +01:00
Andrey Antukh 30413dbc66 Add small changes to the auth/login button label (#7754)
* 📎 Update changelog

*  Update login button label

* 📎 Adapt playwright tests
2025-11-14 11:35:10 +01:00
Andrey Antukh 2810ae681f ⬆️ Update yarn requirement on library module 2025-11-14 11:15:26 +01:00
Andrey Antukh d706bb7c8d 🐛 Fix validation issues with dtcg-node schema 2025-11-14 11:15:26 +01:00
Andrey Antukh ef271db879 🎉 Add addTokensLib method to the library 2025-11-14 11:15:26 +01:00
Andrey Antukh ec5e814a72 ⬆️ Update npm deps on library 2025-11-14 11:15:26 +01:00
Andrey Antukh c44fd2dd1d 💄 Use correct comments style on tokens-lib 2025-11-14 11:15:26 +01:00
Andrey Antukh 6aa797f51b Normalize token theme serialization to JSON 2025-11-14 11:15:26 +01:00
Andrés Moyaandalonso.torres 3cc54fd988 🎉 Add design tokens to plugins API (#7602)
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2025-11-14 11:14:56 +01:00
Xaviju 2233f34a15 🎉 Set default button behaviour as type button instead of submit (#7741) 2025-11-14 10:25:38 +01:00
Andrey Antukh 839bb470df Merge remote-tracking branch 'origin/staging' into develop 2025-11-14 09:55:14 +01:00
Eva Marco 450ce869ba 🐛 Fix gap on export section on sidebar 2025-11-14 09:08:33 +01:00
Xaviju 665587d492 ♻️ Review inspect tab UI (#7727)
* ♻️ Review inspect tab UI

* ♻️ Capitalize English strings and remove from styles

* ♻️ Set a minimum size por color space selector and adjust visually the UI

* 🐛 Fix error on hooks order when selecting texts

* 🐛 Set minim size to inspect tab element

* 🐛 Fix broken typography panel

* ♻️ Design review
2025-11-13 22:19:43 +01:00
Elena Torró 8aaa953604 Merge pull request #7730 from penpot/alotor-fixes-layouts
 Fix new render problems with layout
2025-11-13 16:38:20 +01:00
Marina López a2cb84ba0d Add improvements payment flow 2025-11-13 13:48:27 +01:00
alonso.torres 639952abc8 🐛 Fix problems with text positioning in layout 2025-11-13 12:31:26 +01:00
alonso.torres 2d63730bfa Improved performance in modifiers 2025-11-13 12:31:26 +01:00
alonso.torres c1638817b2 🐛 Fix problem with frame titles not moving 2025-11-13 12:31:26 +01:00
alonso.torres 76f6f71e02 🐛 Fix z-ordering for flex elements 2025-11-13 12:31:26 +01:00
alonso.torres 0a700864c9 🐛 Fix problem with grid layout modifiers 2025-11-13 12:31:26 +01:00
Yamila Moreno 04ce4c3233 🔧 Fix repository name in release.yml (#7731) 2025-11-13 11:42:33 +01:00
Andrey Antukh befcca86df 📚 Update changelog 2025-11-12 21:37:16 +01:00
Andrey Antukh b7bae3850b 🐛 Fix webp exportation on exporter docker image (#7739) 2025-11-12 21:31:19 +01:00
Elena Torró 3f05dae455 Merge pull request #7735 from penpot/superalex-fix-create-empty-text
🐛 Fix some text issues
2025-11-12 17:48:41 +01:00
alonso.torres 48c9fb5690 Add methods to plugins for modifying indices 2025-11-12 17:07:38 +01:00
alonso.torres 4cdf1eed0c 🐛 Add method to retrieve image data in plugins 2025-11-12 17:07:38 +01:00
Aitor Moreno 4a887840c6 Merge pull request #7737 from penpot/sueralex-fix-shadows-clipping
🐛 Fix shadows clipping
2025-11-12 16:58:06 +01:00
Elena Torró 10cf2c7f35 Merge pull request #7729 from penpot/ladybenko-12514-fix-font-variants
🐛 Fix downloading wrong font variant
2025-11-12 15:30:08 +01:00
Belén Albeza d048a251f1 🐛 Fix render of text baseline (wasm) 2025-11-12 14:59:57 +01:00
Belén Albeza 0b3fc6a663 🔧 Fix broken playwright tests (wasm render) 2025-11-12 14:48:31 +01:00
Andrey Antukh 363b4e3778 ♻️ Make the SSO code more modular (#7575)
* 📎 Disable by default social auth on devenv

* 🎉 Add the ability to import profile picture from SSO provider

* 📎 Add srepl helper for insert custom sso config

* 🎉 Add custom SSO auth flow
2025-11-12 12:49:10 +01:00
Andrey Antukh f248ab5644 🐛 Relax schema for importing plain path data related to curve-to command 2025-11-12 12:13:17 +01:00
Alejandro Alonso 33da6fbec2 🐛 Fix shadows clipping 2025-11-12 11:47:53 +01:00
Belén Albeza 07bede8ba2 🐛 Fix unicode ranges for codepoints that need surrogate pairs 2025-11-12 10:11:19 +01:00
Eva Marco 05bea14a88 🐛 Fix review selected colors (#7715)
* 🐛 Fix gap between token sets

* 🐛 Show token selected on color selecction modal
2025-11-12 10:04:29 +01:00
Alejandro Alonso 718f42aa94 🐛 Fix deselect and delete events for empty texts 2025-11-12 08:33:17 +01:00
Alejandro Alonso f2f8a488ad Merge pull request #7724 from penpot/elenatorro-12551-fix-blurs-and-shadows-bounding-box
🐛 Fix extrect calculation for shadows and blurs depending on the scale
2025-11-12 08:25:50 +01:00
Alejandro Alonso 7594f1883b 🐛 Fix create empty text 2025-11-12 08:20:58 +01:00
Belén Albeza 5c2dde7308 🐛 Fix font family not being updated when changed from dropdown 2025-11-11 15:52:18 +01:00
Belén Albeza 483a1bd703 🐛 Fix downloading wrong font variant 2025-11-11 14:44:56 +01:00
Andrey Antukh e1a275c7a9 Merge remote-tracking branch 'origin/staging' into develop 2025-11-11 14:07:07 +01:00
Andrey Antukh 96d9724516 📎 Update changelog 2025-11-11 14:04:04 +01:00
Andrey Antukh 8158f2956f Backport github release workflow from develop 2025-11-11 14:01:25 +01:00
Eva Marco e45994e836 🐛 Fix color row opacity (#7550) 2025-11-11 13:30:08 +01:00
Xaviju 83da59e03c Add composite shadow token to inspect tab (#7703) 2025-11-11 13:28:11 +01:00
Yamila Moreno fb21a98b0c Merge pull request #7706 from penpot/yms-fix-release-docker-images
🚧 Fix docker images arch during release
2025-11-11 13:21:21 +01:00
Elena Torro 23baf6d18b 🐛 Fix extrect calculation for shadows and blurs depending on the scale 2025-11-11 12:50:15 +01:00
Andrey Antukh 28cf67e7ff 🎉 Add management RPC API (#7700)
* 🎉 Add management RPC API

And refactor internal http auth flow

* 📎 Adjust final url namings

* 📚 Update changelog
2025-11-10 17:10:59 +01:00
Elena Torro 1b50c13c4d 🐛 Render shadows on nested shapes 2025-11-10 14:13:49 +01:00
Pablo Alba 7de95e108b 🐛 Fix crash when using decimal values for X/Y or width/height (#7722) 2025-11-10 11:28:00 +01:00
Luis de Dios c6b907d05c 📚 Improve switch component documentation (#7714) 2025-11-10 11:00:44 +01:00
Pablo Alba ffb4d6a890 🐛 Fix input confirmation behavior is not uniform 2025-11-10 09:50:26 +01:00
Ahmad HosseinBor 69c4a8932a 🌐 Add translations for: Persian
Currently translated at 40.2% (787 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2025-11-10 08:51:23 +01:00
Luis de Dios fa25307c05 🐛 Fix correct alignment of property names (#7717) 2025-11-09 17:52:11 +01:00
Xaviju 43a136a9e9 💄 Fix minor style details on DS select ghost variant (#7707) 2025-11-07 22:46:57 +01:00
Yamila Moreno 3ec4c96b48 🚧 Fix docker images arch during release 2025-11-07 17:50:09 +01:00
Eva Marco 2eaeb8e9a5 🐛 Fix flex children subgrid gap 2025-11-07 13:49:45 +01:00
Andrey Antukh 604f6ca024 🐛 Fix incorrect value coercing on legacy select component (#7710)
on managing values with select
2025-11-07 13:16:39 +01:00
Andrey Antukh e3cf70d3a8 Add URI to the report.txt (#7709) 2025-11-07 13:16:21 +01:00
Alejandro Alonso 6aedac35f2 🐛 Fix wasm erros when images are not found 2025-11-07 13:08:41 +01:00
Alejandro Alonso a11b0f54d7 🐛 Fix changing properties resizes the text box height 2025-11-07 12:34:51 +01:00
Belén Albeza ec0dc2931c Update copyright string in static page (#7701) 2025-11-07 10:54:27 +01:00
Andrey Antukh 9d65d11c91 Merge remote-tracking branch 'origin/staging' into develop 2025-11-07 10:43:27 +01:00
Luis de Dios f00fd1d5a8 🎉 Use toggle for switching boolean variant property names (#7564) 2025-11-07 09:47:57 +01:00
Alejandro Alonso d796dbb572 Merge pull request #7705 from penpot/niwinz-staging-fix-shadows
🐛 Restrict shadow colors to plain colors only
2025-11-06 16:10:02 +01:00
Andrey Antukh e979476b0e 🐛 Restrict shadow colors to plain colors only
Previously, shadows used a general-purpose color schema that allowed
to have gradients and images on the data structure. This commit fixes
that using a specific schema for shadow colors that only allows plain
colors.

A migration is added to clean up existing shadows with non-plain
colors.
2025-11-06 15:54:50 +01:00
Andrey Antukh 097897d8da Add better sse parser for backend tests 2025-11-06 15:54:50 +01:00
Alejandro Alonso ba092f03e1 🎉 Use Vec instead of Indexset 2025-11-06 14:16:07 +01:00
Alejandro Alonso 61202e1cab Merge pull request #7698 from penpot/elenatorro-fix-word-breaking-different-browsers
🔧 Fix cross-browser text issues
2025-11-06 12:34:22 +01:00
Elena Torro f496ba78f3 🔧 Fix cross-browser text issues 2025-11-06 12:20:02 +01:00
Alejandro Alonso b9a0c6d932 Merge pull request #7702 from penpot/alotor-tiles-fixes
 Removed some artifacts when tile rendering
2025-11-06 12:11:23 +01:00
alonso.torres a59ce2ed16 Removed some artifacts when tile rendering 2025-11-06 11:46:02 +01:00
Xaviju c221b9366f Add e2e tests to inspect tab (#7685) 2025-11-06 10:07:50 +01:00
Alejandro Alonso 8e0aa683a1 Merge pull request #7583 from penpot/niwinz-develop-backend-access-deleted-files
 Add RPC methods for enable access to deleted but recoverable projects and files
2025-11-06 06:44:56 +01:00
Alejandro Alonso 445d40b71c Merge pull request #7691 from penpot/alotor-improved-render-tiling
 Improve tile rendering updating
2025-11-05 17:29:25 +01:00
Alejandro Alonso 7889578ced 🎉 Use textures directly for images 2025-11-05 17:16:06 +01:00
alonso.torres a230d2fcf6 Improve tile rendering updating 2025-11-05 17:16:06 +01:00
Belén Albeza 78fde35df9 🔧 Upgrade storybook (#7693)
* 🔧 Upgrade to storybook 9.x

* 🔧 Upgrade to storybook 10.x

* 🔧 Update watch:storybook script so it builds its assets dependencies first

* 🔧 Use vitest for storybook tests (test-storybook was deprecated)
2025-11-05 17:15:19 +01:00
Eva Marco bb65782d08 🎉 Add sidebar css variables (#7645)
* 🎉 Add sidebar css variables

* 🎉 Explain more in depth the grid structure
2025-11-05 14:06:26 +01:00
Alejandro Alonso 02a1992a0a Merge pull request #7694 from penpot/niwinz-staging-runner-fixes
🐛 Fix precision issues on worker task scheduling mechanism
2025-11-05 12:18:23 +01:00
Andrey Antukh 1cce82f958 Merge remote-tracking branch 'origin/staging' into develop 2025-11-05 12:15:15 +01:00
Alejandro Alonso a576c0404a 🐛 Fix focus mode across page and file navigation (#7695) 2025-11-05 12:05:00 +01:00
Andrey Antukh 7d5c1c9b5f Make file-gc-scheduler task compatible with virtual clock
And simplify implementation
2025-11-05 10:47:31 +01:00
Andrey Antukh cd53d3659c 🐛 Truncate worker scheduled-at to milliseconds
The nanosecond precision has the problem with transit serialization
roundtrip used for pass data on the worker scheduler throught redis
and generates unnecesary rescheduling.
2025-11-05 10:47:31 +01:00
Andrey Antukh 132f7d6d3e ♻️ Add minor refactor on tokens main form (#7690) 2025-11-05 10:37:38 +01:00
Alejandro Alonso b2a9c55874 Merge pull request #7674 from penpot/elenatorro-12478-fix-new-lines
🐛 Fix new lines issues
2025-11-05 10:13:41 +01:00
Alejandro Alonso d610e7c892 Merge pull request #7671 from penpot/niwinz-develop-path-data-fix
🐛 Relax schema for importing plain path data related to curve-to command
2025-11-05 10:04:03 +01:00
Alejandro Alonso 1b5557759a Merge pull request #7687 from penpot/ladybenko-12440-fix-corrupt-files
🐛 Fix wasm crash when loading a file with missing font assets
2025-11-05 07:57:39 +01:00
Belén Albeza 8148da58ed 🐛 Fix wasm crash when loading a file with missing font assets 2025-11-05 07:47:16 +01:00
Alejandro Alonso 537f681944 Merge pull request #7692 from penpot/niwinz-develop-logging-improvements
 Remove unnecesary report on duplicate email error validation
2025-11-05 07:46:16 +01:00
Alejandro Alonso 9e7ec594ca Merge pull request #7680 from penpot/niwinz-staging-file-export-fix
🐛 Fix race condition on file export process
2025-11-05 07:45:26 +01:00
Alejandro Alonso 7c529eedd4 Merge pull request #7682 from penpot/niwinz-staging-worker-runner-exceptions
🐛 Fix incorrect status return on worker runner
2025-11-05 07:44:28 +01:00
Alejandro Alonso 500c5c81d4 Merge pull request #7686 from penpot/elenatorro-12499-fix-nested-blur
🐛 Fix children blur rendering
2025-11-05 07:27:43 +01:00
Alejandro Alonso 6ea69c94ee 🎉 Improve big images performance 2025-11-04 22:02:34 +01:00
Andrey Antukh 9b3f68ad14 Remove unnecesary report on duplicate email error validation 2025-11-04 20:34:25 +01:00
Andrey Antukh 34363320ae Merge branch 'main' into staging 2025-11-04 16:49:53 +01:00
Andrey Antukh 092a5139e3 🐛 Fix incorrect token sets migration (#7673) 2025-11-04 16:49:08 +01:00
Andrey Antukh 4a01121043 Merge tag '2.11.0-RC3' 2025-11-04 16:43:32 +01:00
Elena Torro 564ad8adba 🐛 Fix children blur rendering 2025-11-04 15:37:49 +01:00
Andrey Antukh 78e2d6fec3 🐛 Relax schema for importing plain path data related to curve-to command 2025-11-04 12:59:26 +01:00
Andrey Antukh c850f101d3 Merge remote-tracking branch 'origin/staging' into develop 2025-11-04 12:49:57 +01:00
Andrey Antukh 49721c0bcd Add better logging context report on worker runner 2025-11-04 12:44:38 +01:00
Andrey Antukh c214cc1544 🐛 Do not process runner result if no result returned 2025-11-04 12:44:38 +01:00
Andrey Antukh eaabe54c4b 💄 Check the runner task exists as first condition 2025-11-04 12:44:38 +01:00
Eva Marco 21fb38e5bd 🐛 Fixes some problems with dropdowns and token inputs (#7640)
* 🐛 Fix apply color token on strokes

* 🐛 Fix size and position of some numeric inputs

* 🐛 Fix padding token application

* ♻️ Fix ci

* 🐛 Fix selected color tick

* 🐛 Fix comments and design review
2025-11-04 12:39:41 +01:00
Luis de Dios 37aa59b164 🐛 Fix hidden advanced frame grid options menu (#7681) 2025-11-04 11:57:52 +01:00
Elena Torro 24e4ece323 🐛 Fix line-height rendering on empty lines 2025-11-04 11:25:14 +01:00
Andrey Antukh cbae3dca34 Simplify the approach for return streamable body
Removing unnecesary syntax overhead with simplier abstraction
2025-11-04 10:56:05 +01:00
Andrey Antukh 8307b699bf 🐛 Remove a race condition on file export
Caused when file is deleted in the middle of an exportation. The
current export process is not transactional, and on file deletion
several queries can start return not-found exception because of
concurrent file deletion.

With the changes on this PR we allow query deleted files internally
on the exportation process and make it resilent to possible
concurrent deletion.
2025-11-04 10:56:05 +01:00
Andrey Antukh cd6865f54b ⬆️ Update yetti dependency
Bugfixes
2025-11-04 10:56:05 +01:00
Elena Torro e673035817 🔧 Filter out empty paragraph content 2025-11-04 09:57:13 +01:00
Stas Haas f6e77c09b3 🌐 Add translations for: German
Currently translated at 90.4% (1770 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-11-03 21:51:14 +01:00
Elena Torro 87fc71b55d 🐛 Ignore non-frequent chars 2025-11-03 17:08:38 +01:00
Elena Torro b76bfa2197 🐛 Fix width on rotation 2025-11-03 16:37:17 +01:00
Andrey Antukh 88493f6805 🐛 Fix incorrect query for subscription editors (#7672)
Default teams should be present on the query results
2025-11-03 16:14:24 +01:00
Pablo Alba 69bbdad570 🐛 Fix nested variant in a component doesn't keep inherited overrides (3) 2025-11-03 15:36:37 +01:00
Pablo Alba df4279bdee Revert "🐛 Fix nested variant in a component doesn't keep inherited overrides (2)"
This reverts commit 7c6515aa7b.
2025-11-03 15:36:37 +01:00
Alejandro Alonso c8c901ee4c Merge pull request #7670 from penpot/ladybenko-fix-broken-wasm-test
 Fix broken wasm test
2025-11-03 14:49:29 +01:00
Belén Albeza 8f0e5e36e9 Fix broken wasm test 2025-11-03 14:18:15 +01:00
Andrés Moya a5e9f7229b 💄 Fix tests nesting 2025-11-03 14:02:29 +01:00
Andrés Moya 5f22220a8b 🐛 Add test to catch a fixed bug and avoid regressions 2025-11-03 14:02:29 +01:00
Luis de Dios 6c7661b04d 🐛 Fix add missing use in SCSS 2025-11-03 12:47:25 +01:00
Alejandro Alonso b867f276f2 Merge pull request #7665 from penpot/superalex-fix-texts-migrations
🐛 Fix texts migrations
2025-11-03 12:20:16 +01:00
Alejandro Alonso da8d7a78cf 🐛 Add migration for texts with fills only in position-data 2025-11-03 12:06:41 +01:00
Alejandro Alonso ec4936f5fe 🐛 Fix 0006 migration for strings that should be uuids 2025-11-03 12:06:41 +01:00
Alejandro Alonso dd9ec54bd1 Merge pull request #7664 from penpot/alotor-performance-improvements
 Improve boolean calculations
2025-11-03 12:04:36 +01:00
Alejandro Alonso 3ad4b0a453 Merge pull request #7657 from penpot/elenatorro-12448-fix-text-tabs
🐛 Fix tabs rendering
2025-11-03 11:53:20 +01:00
Belén Albeza 83cd9c3db6 🔧 Fix rust linter errors 2025-11-03 11:45:05 +01:00
Andrey Antukh 399feec032 ⬆️ Update rust to 1.91 2025-11-03 11:45:00 +01:00
Andrey Antukh 481fa44f18 Merge remote-tracking branch 'origin/staging' into develop 2025-11-03 11:41:13 +01:00
Andrey Antukh 42c9f2123d Merge pull request #7663 from penpot/niwinz-staging-update-rust
⬆️ Update rust to 1.91
2025-11-03 11:26:01 +01:00
Elena Torro d18a018236 🔧 Fix tab rendering with the text editor 2025-11-03 11:02:28 +01:00
Belén Albeza 4ab6ecec21 🔧 Fix rust linter errors 2025-11-03 10:43:35 +01:00
alonso.torres b39c00fbf6 Improve boolean calculations 2025-11-03 09:50:29 +01:00
Andrey Antukh 8a0fddf1e4 ⬆️ Update rust to 1.91 2025-11-03 09:10:40 +01:00
Luis de Dios 95fdd75030 🐛 Fix misaligned right sidebar menus 2025-11-03 08:34:09 +01:00
Andrey Antukh 54489c4285 🔧 Add better regex for commit checker 2025-11-01 18:46:26 +01:00
Stas Haas e7b8ad8ee2 🌐 Add translations for: German
Currently translated at 89.3% (1747 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-11-01 10:51:52 +00:00
Andrey Antukh 6815806669 Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 18:15:12 +01:00
Andrey Antukh febe87aa7b 🐛 Fix incorrect checksum of the jdk on dockerfiles 2025-10-31 18:01:55 +01:00
Andrey Antukh 83763b46ce Add RPC methods for manage deleted files
This includes: get already deletedf files, restore deleted files
and permanently delete files marked for deletion.
2025-10-31 16:07:23 +01:00
Andrey Antukh 1ddc196484 Make the get-projects return deleted projects
And adapt the frontend code to properly filter
deleted projects on appropriate pages
2025-10-31 16:07:23 +01:00
Andrey Antukh 37d4844518 💄 Add minor cosmetic changes to get-project-files rpc method 2025-10-31 16:07:23 +01:00
Andrey Antukh 76e610dd06 🔥 Remove duplicated functions from file tests namespace 2025-10-31 16:07:23 +01:00
Andrés Moya 99e8b22672 🐛 Fix theme validation when still no tokens library exists 2025-10-31 16:04:50 +01:00
Andrey Antukh 65adbfaadb Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 14:50:59 +01:00
Andrey Antukh 0581c60800 ⬆️ Update jdk and node on docker images 2025-10-31 14:50:12 +01:00
Andrey Antukh 7e92408807 ⬆️ Update jdk and node on devenv 2025-10-31 14:50:12 +01:00
Marina López 03eeeda44f Add improvements payment flow 2025-10-31 14:15:22 +01:00
Alejandro Alonso 2f33009e69 Merge pull request #7655 from penpot/superalex-add-migration-to-fix-text-attrs-with-blank-strings
📎 Add migration to fix text attrs with blank strings
2025-10-31 13:58:42 +01:00
Alejandro Alonso 1d5c407456 📎 Add migration to fix text attrs with blank strings 2025-10-31 13:30:46 +01:00
Alejandro Alonso aa15232cc7 Merge pull request #7648 from penpot/alotor-performance-improvements
 Add performance improvements for wasm render
2025-10-31 12:22:14 +01:00
Andrey Antukh f53935f5df Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 12:13:29 +01:00
alonso.torres de04026dc8 After review changes 2025-10-31 12:04:52 +01:00
alonso.torres f3b914534f Add scale_content to shapes_pool 2025-10-31 11:56:28 +01:00
alonso.torres fcc9282304 Fix problems with SVGraw and modifiers 2025-10-31 11:56:28 +01:00
alonso.torres 122619b197 Support for booleans dynamic transforms 2025-10-31 11:56:28 +01:00
alonso.torres dbf9bdceb5 Removed modifiers from code 2025-10-31 11:56:28 +01:00
Alejandro Alonso f6eb492329 🐛 Fig shapes pool extending size 2025-10-31 11:56:28 +01:00
Alejandro Alonso c66a8f5dc5 Improve shapes pool performance 2025-10-31 11:56:28 +01:00
alonso.torres ed4df73e42 Changes to modifiers 2025-10-31 11:56:28 +01:00
alonso.torres 59e745e9ab Improve performance of group bounds 2025-10-31 11:56:28 +01:00
alonso.torres d4b4d943c6 Store bounds inside the shape 2025-10-31 11:56:28 +01:00
alonso.torres e4b4f1bd08 Removed all_ancestors traversals 2025-10-31 11:56:28 +01:00
alonso.torres e58b2453b1 Removed method set_selrect_for_current_shape 2025-10-31 11:56:28 +01:00
alonso.torres e9230b8b54 Change internal data type for tiles 2025-10-31 11:56:28 +01:00
alonso.torres 9d7cac5e73 Improved performance of children ancestors 2025-10-31 11:56:28 +01:00
alonso.torres 17fefcf0bc Changes WASM serialization mechanism 2025-10-31 11:56:28 +01:00
Alejandro Alonso 4367bd2dc6 Merge pull request #7651 from penpot/niwinz-email-bugfix-1
🐛 Fix regression on sending quote notification email
2025-10-31 11:23:39 +01:00
Miguel de Benito Delgado 6e2b2e8924 📚 Update increase/decrease font shortcuts (#7652) 2025-10-31 11:21:53 +01:00
Andrey Antukh f3805e3b70 🐛 Fix regression on sending quote notification email 2025-10-31 10:56:33 +01:00
David Barragán Merino 262937c421 📚 Add recommendations for valkey/redis configuration 2025-10-31 10:45:33 +01:00
Alejandro Alonso 15ee75a692 Merge pull request #7647 from penpot/elenatorro-remove-unnecessary-text-properties
🔧 Remove unused text attrs
2025-10-31 09:56:12 +01:00
Alonso Torres 942e3300dd 🐛 Fix problem when checking usage with removed teams (#7638) 2025-10-31 09:22:31 +01:00
Elena Torro eaa3904a3a 🔧 Remove unused text attrs 2025-10-31 09:22:01 +01:00
Alejandro Alonso 0c66b5db73 📎 Set stronger text validation (#7646) 2025-10-31 09:19:53 +01:00
Elena Torró cc40448cb5 Merge pull request #7644 from penpot/azazeln28-fix-empty-text-attr-defaults
🐛 Fix empty text attr defaults
2025-10-31 09:00:19 +01:00
Alejandro Alonso 6a2029ca3b 🐛 Fix error comment message after the demo account creation (#7615) 2025-10-31 08:56:34 +01:00
David Barragán Merino f32913adcf 📚 Adapt doc with the storage settings changes (#7607) 2025-10-31 08:56:06 +01:00
Juan de la Cruz d906f05a6f 🎉 Add 2.11 release slides and images (#7606) 2025-10-31 08:54:19 +01:00
Yamila Moreno 2402334fb2 Merge pull request #7641 from penpot/yms-add-bundle-version-to-docker-metadata
🚧 Add bundle version to Docker metadata
2025-10-30 15:12:46 +01:00
Elena Torró c3e2621ed5 Merge pull request #7643 from penpot/superalex-serialize-font-weight-properly
🐛 Serialize font weight properly
2025-10-30 14:59:47 +01:00
Yamila Moreno d37695d7a5 🚧 Add bundle version to Docker metadata 2025-10-30 14:36:23 +01:00
Aitor Moreno fadbe24aaa 🐛 Fix empty text attr defaults 2025-10-30 14:16:30 +01:00
Alejandro Alonso 9d29d5e8cc 🐛 Serialize font weight properly 2025-10-30 14:15:57 +01:00
Florian Schroedl e681f95a70 Add box shadow token 2025-10-30 14:05:42 +01:00
Alejandro Alonso 5c8b401037 Merge pull request #7637 from penpot/elenatorro-12439-fix-fonts-default-values
🐛 Fix default font size in text spans
2025-10-30 11:14:20 +01:00
Elena Torro 9dfb0ebe84 🐛 Fix default font size in text spans 2025-10-29 17:23:29 +01:00
Alejandro Alonso 08162c825d Merge pull request #7633 from penpot/superalex-options-button-does-not-work-for-comments-created-in-the-lower-part-of-the-screen-with-an-active-reply-field
🐛 Fix options button does not work for comments created in the lower part of the screen
2025-10-29 16:18:21 +01:00
Alejandro Alonso bc700334ca 🐛 Fix options button does not work for comments created in the lower part of the screen 2025-10-29 16:17:57 +01:00
Alejandro Alonso 133590f19c Merge pull request #7635 from penpot/alotor-fix-paste-position
🐛 Fix paste without selection sends the new element in the back
2025-10-29 16:16:17 +01:00
alonso.torres 66c5a0570e 🐛 Fix paste without selection sends the new element in the back 2025-10-29 16:15:55 +01:00
Andrés Moya 94cbf9d8f2 🎉 Add integration test to check new validation 2025-10-29 15:40:45 +01:00
Andrés Moya 70143f8ae3 🐛 Fix theme renaming and small refactor tokens forms validation 2025-10-29 15:40:45 +01:00
Xaviju 6c824651df 🎉 Add copy shortands button to panels (#7580)
* 🎉 Add copy shorthands button to panels
* 🎉 Add shorthand for strokes
* 🎉 Add shorthand for fonts
* 🎉 Add shorthand for borders
* 🎉 Add shorthand for padding
* 🎉 Add shorthand for grid
* 🎉 Add shorthand for layout element
* 🐛 Refactor to fix hook rendering
2025-10-29 13:51:36 +01:00
David Barragán Merino 1b81ddebb4 🐛 Fix some paths and add missed nginx config file for the storybook docker image 2025-10-29 13:46:29 +01:00
David Barragán Merino 6076df5c80 🎉 Detach storybook from the frontend build process 2025-10-29 13:45:54 +01:00
Alejandro Alonso 6d2d66a079 Merge pull request #7634 from penpot/alotor-fix-editable-label
🐛 Fix problem with certain text input and drag/drop
2025-10-29 12:50:03 +01:00
Alejandro Alonso 239af4fb82 🐛 Fix problem with text grow types 2025-10-29 12:40:11 +01:00
alonso.torres 0ad4a9ca7e 🐛 Fix problem with certain text input and drag/drop 2025-10-29 12:35:13 +01:00
David Barragán Merino 034463e63a 🐛 Fix some paths and add missed nginx config file for the storybook docker image 2025-10-29 12:02:32 +01:00
Eva Marco aadc1aac1c 🐛 Fix some error translations 2025-10-29 11:14:20 +01:00
Elena Torró 2cdc76f1af Merge pull request #7573 from penpot/superalex-select-boards-to-export-to-pdf
 Select boards to export to PDF
2025-10-29 10:13:32 +01:00
alonso.torres 23f49237f8 🐛 Fix problem with plugins generating code for pages different than current one 2025-10-29 10:08:41 +01:00
Xaviju 93fb54c116 Enable single color-space selector for styles and computed… (#7525)
*  Enable single color-space selector for styles and computed tab
2025-10-29 09:53:37 +01:00
Alejandro Alonso 7565bb8d24 Merge pull request #7466 from penpot/bameda-storybook-detachment
🎉 Detach storybook from the frontend build process
2025-10-29 09:53:18 +01:00
Alejandro Alonso 0d394ee962 Merge pull request #7593 from penpot/eva-fix-some-translations
🐛 Fix some error translations
2025-10-29 09:36:06 +01:00
Alejandro Alonso c4bebc1b0a Merge pull request #7625 from penpot/elenatorro-12374-fix-remove-selection
🐛 Fix text selection
2025-10-29 09:35:56 +01:00
Elena Torro 6edc29dce2 🐛 Fix text selection 2025-10-29 09:20:51 +01:00
Eva Marco d773e3a966 🐛 Fix some error translations 2025-10-29 09:20:07 +01:00
Alejandro Alonso e18aef1d39 Merge pull request #7610 from mdbenito/fix/conflicting-selection-shortcut-text-shape
🐛 Fix conflicting shortcut in text editor
2025-10-29 08:45:05 +01:00
Alejandro Alonso b033690239 Merge pull request #7618 from penpot/andy-docs-typography-token
📚 Add typography token to the user guide
2025-10-29 08:22:20 +01:00
Alejandro Alonso 9f732eb45a Merge pull request #7595 from penpot/esther-moreno-user-guide-new-architecture
📚 New architecture in user guide
2025-10-29 08:02:45 +01:00
Alejandro Alonso 474453a503 Merge pull request #7594 from penpot/eva-fix-dropdown-submenu
🐛 Fix submenu visibility
2025-10-29 07:53:06 +01:00
Alejandro Alonso c3d40659a9 Merge pull request #7600 from penpot/elenatorro-12344-fix-different-text-span-font-sizes
🔧 Add support for text spans of different sizes
2025-10-29 07:25:49 +01:00
David Barragán Merino 15e2b35afc 🎉 Detach storybook from the frontend build process 2025-10-28 20:58:50 +01:00
Elena Torró ad15887d57 Merge pull request #7623 from penpot/superalex-fix-nested-fills-for-shapes-with-svg-attrs
🐛 Fix nested fills for shapes with svg attrs
2025-10-28 16:01:27 +01:00
Elena Torró d01f921344 Merge pull request #7624 from penpot/superalex-fix-prevent-rendering-of-unused-fill-slots-in-shapes
🐛 Prevent rendering of unused fill slots in shapes
2025-10-28 15:54:51 +01:00
Alejandro Alonso 9e035ec4fe Merge pull request #7605 from penpot/ladybenko-fix-text-playground-crash
🔧 Fix text-related playgrounds (wasm)
2025-10-28 14:11:52 +01:00
Alejandro Alonso fbacdf0351 🔧 Fix shapes-related playgrounds (wasm) 2025-10-28 14:09:24 +01:00
Alejandro Alonso 3f4d699395 🐛 Prevent rendering of unused fill slots in shapes 2025-10-28 13:35:34 +01:00
Alejandro Alonso 1626371337 Merge pull request #7619 from penpot/elenatorro-11889-fix-text-span-selection
🐛 Fix data-itype for text spans
2025-10-28 13:33:19 +01:00
Marina López 4d8a70f1fa Improvements payments 2025-10-28 12:36:32 +01:00
Alejandro Alonso 14d5de29da 🐛 Fix nested fills for shapes with svg attrs 2025-10-28 12:25:57 +01:00
Esther Moreno df718c940f 📚 New architecture in user guide 2025-10-28 11:04:09 +01:00
Elena Torro 80c78d9cd4 🐛 Fix pasting text within an existing text 2025-10-28 09:39:52 +01:00
Pablo Alba e2ce226814 🐛 Fix remove flex button doesn’t work within variant 2025-10-28 09:38:38 +01:00
Elena Torro 28c4c1a286 🐛 Fix data-itype for text spans 2025-10-27 16:55:51 +01:00
alonso.torres f64105ad08 🐛 Fix problem with changing gap in flex layout 2025-10-27 16:29:59 +01:00
Andres Gonzalez a346d29d76 📚 Add typography token to the user guide 2025-10-27 15:18:14 +01:00
Ingrid Pigueron ccb7b41b3a 🌐 Add translations for: French
Currently translated at 98.3% (1923 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-27 12:02:59 +00:00
Elena Torro 2c37c5c8ed 🔧 Add support for text spans of different sizes 2025-10-27 12:43:41 +01:00
Andrés Moya ed767d9a5b 🐛 Fix library update notificacions showing when they should not 2025-10-27 11:14:41 +01:00
Miguel de Benito Delgado 57bfca4062 🐛 Maintain selection after font change 2025-10-26 12:02:20 +00:00
Miguel de Benito Delgado e9dcd64463 🐛 Fix conflicting shortcut in text editor 2025-10-26 11:11:50 +00:00
Belén Albeza b498056c01 🔧 Fix text-related playgrounds (wasm) 2025-10-24 14:35:28 +02:00
Andrés Moya 81f851cad4 🔧 Deactivate debug traces for fonts module 2025-10-24 11:49:43 +02:00
Pablo Alba 245190f4f9 🐛 Fix variant validation when nil 2025-10-24 10:59:16 +02:00
Alejandro Alonso 479ce99b32 Improve setting svg attrs in wasm 2025-10-24 10:35:30 +02:00
Alejandro Alonso 6290b88d2e Merge pull request #7601 from penpot/alotor-fix-text-grow-type-problem
🐛 Fix problem with text grow types
2025-10-24 09:45:47 +02:00
Elena Torró dba718b850 Merge pull request #7484 from penpot/superalex-fix-text-line-height-values-are-wrong
🐛 Text line-height values are wrong
2025-10-24 08:49:35 +02:00
alonso.torres 7c1205018b 🐛 Fix problem with text grow types 2025-10-23 17:39:18 +02:00
Elena Torró 89763d7c5a Merge pull request #7554 from penpot/azazeln28-fix-text-editor-v2-issues
🐛 Fix text editor v2 render integration issues
2025-10-23 15:55:54 +02:00
Aitor Moreno 7f6af6179b 🐛 Fix paste when collpaseNode is a br 2025-10-23 15:06:32 +02:00
Aitor Moreno ceb184782f 🐛 Fix text editor paste inline/paragraph 2025-10-23 15:06:01 +02:00
Aitor Moreno 247c5c3700 Merge pull request #7588 from penpot/elenatorro-fix-text-tests
🔧 Fix text align selrect
2025-10-23 15:04:48 +02:00
Pablo Alba 0882c448f6 📎 Cleanup log files 2025-10-23 14:34:26 +02:00
Belén Albeza f8cebb9d63 🐛 Fix scroll bar in design tab (#7582)
* 🐛 Fix scroll bar in design tab

* ♻️ Remove deprecated css tokens in options.scss
2025-10-23 14:11:11 +02:00
Alejandro Alonso 1e248c7177 🐛 Fix demo accounts creation 2025-10-23 13:45:11 +02:00
Elena Torró 351a35dad6 Merge pull request #7574 from penpot/azazeln28-refactor-text-struct-naming
♻️ Rename textleafs and inline to textspan
2025-10-23 13:39:36 +02:00
Aitor Moreno eb088c31c1 🔧 Rename textleafs and inlines to keep coherence between render and editor 2025-10-23 13:04:21 +02:00
Belén Albeza 45af469a11 🐛 Fix invite selection copy
* 🐛 Fix selected invitations copy not being localized/pluralized

*  Add integration test for team invites + fixes unaccessible dom
2025-10-23 12:04:34 +02:00
Eva Marco 232f2271d3 🐛 Fix submenu visibility 2025-10-23 11:52:03 +02:00
Elena Torro a30315c91c 🔧 Fix text align selrect and update regression tests 2025-10-23 11:44:40 +02:00
Pablo Alba 04542e1e66 Add variants to plugins API 2025-10-23 10:52:10 +02:00
Alejandro Alonso 36c986d8e8 🐛 Fix file doesn’t open after deleting the library used in it 2025-10-23 09:51:10 +02:00
Alejandro Alonso 38c3b2eaba Merge pull request #7584 from penpot/alotor-fix-flex-issue
🐛 Fix problem with flex type conversions
2025-10-22 16:49:56 +02:00
alonso.torres 98e91ecda5 🐛 Fix problem with flex type conversions 2025-10-22 16:03:51 +02:00
Alejandro Alonso 54ac64db4b Merge pull request #7578 from penpot/supealex-fix-selected-colors-children-shapes-in-multiple-selection
🐛 Fix selected colors not showing colors from children shapes in multiple selection
2025-10-22 15:18:58 +02:00
Alejandro Alonso 30ca6bf6ff 🐛 Fix selected colors not showing colors from children shapes in multiple selection 2025-10-22 14:53:06 +02:00
David Barragán Merino 81a364dfc4 🐳 Set default values for maxmemory and maxmemory-policy in Valkey 2025-10-22 13:43:30 +02:00
Pablo Alba c6b9954af8 🐛 Fix nested variant in a component doesn't keep inherited overrides 2025-10-22 13:35:22 +02:00
Alejandro Alonso f120cf82d3 Select boards to export to PDF 2025-10-22 13:19:10 +02:00
Belén Albeza 7ec335ae96 🐛 Fix export element crashing the app 2025-10-22 13:02:55 +02:00
Eva Marco 8dcc46aba8 🐛 Fix color row opacity (#7550) 2025-10-22 12:20:58 +02:00
Xaviju 058a555594 🎉 Add shadow panel to inspect styles tab (#7566) 2025-10-22 12:17:14 +02:00
Luis de Dios e073b89604 🐛 Fix property input remains editable after keeping default property name (#7549)
* 🐛 Fix property input remains focused when keeping default property name

* 📎 PR changes
2025-10-22 10:48:03 +02:00
Elena Torró 140290cd60 Merge pull request #7556 from penpot/ladybenko-12362-fix-underline-spacing
🐛 Fix underline not matching spacing/thickness
2025-10-22 10:21:48 +02:00
Pablo Alba 5e6af5aea9 🐛 Fix text override is lost after switch 2025-10-22 09:43:12 +02:00
Alejandro Alonso 5df2a740b9 Merge pull request #7571 from penpot/superalex-fix-cleaning-nested-fills
🐛 Fix cleaning nested fills
2025-10-21 17:05:00 +02:00
Pablo Alba fd596a1371 🐛 Fix incorrect behavior of Alt + Drag for variants 2025-10-21 17:02:10 +02:00
Belén Albeza 87221eb7db 🐛 Fix underline not matching spacing/thickness 2025-10-21 16:57:02 +02:00
Aitor Moreno 69f2e131d7 Merge pull request #7570 from penpot/elenatorro-12386-fix-missplaced-text-strokes
🐛 Fix texts offset-y calculation when there are multiple lines and stroke paints
2025-10-21 16:46:53 +02:00
Alejandro Alonso 69da63e01c 🐛 Fix cleaning nested fills 2025-10-21 16:45:53 +02:00
Elena Torro dc689f9756 🐛 Fix texts offset-y calculation when there are multiple lines and stroke paints 2025-10-21 16:13:40 +02:00
Marina López 82e1a5003c 🐛 Fix condition report feedback 2025-10-21 15:29:06 +02:00
Andrey Antukh 024697ff87 Add cause stack trace logging on ui error boundary 2025-10-21 15:04:53 +02:00
alonso.torres fc4b717287 🐛 Fix problem with CI 2025-10-21 14:45:59 +02:00
Marina López 9e8cdc8a3f 🐛 Fix search shortcut 2025-10-21 13:28:31 +02:00
Elena Torro a51fd009bc 🔧 Improve text tiles intersection on changes 2025-10-21 13:21:02 +02:00
David Barragán Merino f795f20ef8 📎 Notify about failures in releasing and creating docker images 2025-10-21 13:16:04 +02:00
Eva Marco ca21e7e8b4 🐛 Fix font size placeholder 2025-10-21 12:27:15 +02:00
alonso.torres 93e7f2950b 🐛 Fix problem with multiple selection and shadows 2025-10-21 12:27:14 +02:00
Xaviju d0e5d0d952 Display resolved value for composite typography tokens (#7537)
*  Display resolved value for composite typography tokens tooltip

*  Add missing key to iteration
2025-10-21 11:30:34 +02:00
Andrey Antukh e4c07e0ec0 Merge staging into develop 2025-10-21 11:23:59 +02:00
alonso.torres 068caf2784 Merge remote-tracking branch 'origin/staging' into develop 2025-10-21 11:19:21 +02:00
Xavier Julian 436bc23da4 🐛 Remove duplicated tokens on stroke panel 2025-10-21 10:56:38 +02:00
Pablo Alba 579de6558a 🐛 Fix on copy instance inside a components chain touched are missing 2025-10-21 10:37:07 +02:00
Pablo Alba 2d45cba36c 🐛 Fix Restoring a variant from another file makes it overlap 2025-10-21 10:09:50 +02:00
Alejandro Alonso cf21ffb30f Merge pull request #7528 from penpot/alotor-set-children
 Add set_children granular methods for performance
2025-10-21 09:57:22 +02:00
Elena Torró 7a2fe232d5 Merge pull request #7527 from penpot/ladybenko-12329-break-editor-word
🐛 Make internal DOM of text editor v2 break words as the render engine does
2025-10-21 09:41:15 +02:00
Alejandro Alonso 9e17a0e65d 🐛 Fix unread comments 2025-10-21 09:30:01 +02:00
Pablo Alba 220c27c354 🐛 Fix nested variant in a component doesn't keep inherited overrides (2) 2025-10-20 18:28:10 +02:00
Pablo Alba b0e4257e56 📚 Remove wrong line on CHANGES 2025-10-17 14:20:31 +02:00
Pablo Alba b3cb7df33c 🐛 Fix nested variant in a component doesn't keep inherited overrides 2025-10-17 14:01:54 +02:00
Pablo Alba fec420b6e9 🐛 Fix variants not syncronizing tokens on switch 2025-10-17 13:46:49 +02:00
Eva Marco 35af5455a0 🐛 Fix dropdown width (#7534) 2025-10-17 13:39:29 +02:00
AlexTECPlayz 597fba79cc 🌐 Add translations for: Romanian
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-17 13:07:28 +02:00
Luis de Dios 216b2d3072 🐛 Fix drag & drop functionality is swapping instead or reordering (#7489)
* 🐛 Fix drag & drop functionality is swapping instead or reordering

* ♻️ SCSS improvements
2025-10-17 12:12:34 +02:00
Eva Marco bbc6709943 🐛 Fix applying color tokens on a row from colorpicker (#7524) 2025-10-17 11:10:10 +02:00
Eva Marco 14f6e22610 🐛 Fix composite token placeholders (#7526)
* 🐛 Fix composite token placeholders

* 📚 Recover some translations
2025-10-17 10:57:32 +02:00
Eva Marco 2f27a78bc0 🐛 Fix color row erros from review (#7516)
* 🐛 Fix text Ellipsis on token color row

* 🐛 Fix show token only on first stroke

* 🐛 Fix detach broken token
2025-10-17 10:39:41 +02:00
Marina López f5761066a9 Merge pull request #7511 from penpot/marina-improve-users-give-feedback
 Improve the way users give us feedback
2025-10-17 10:19:34 +02:00
Andrey Antukh 3665bccaed Merge remote-tracking branch 'origin/staging' into develop 2025-10-17 09:43:30 +02:00
Andrey Antukh fbbee98c3d Add proper backend integration of for new feedback form 2025-10-17 09:40:27 +02:00
Marina López 854ad5bb4d Improve the way users give us feedback 2025-10-17 09:39:58 +02:00
Andrey Antukh a32f44a62c 🐛 Use correct error boundary fallback on ui ns 2025-10-17 09:39:58 +02:00
Andrey Antukh 95f58ffda5 Allow add attachements on emails 2025-10-17 09:39:58 +02:00
Andrey Antukh e8e27c25c0 💄 Fix naming on several components under ui ns
Following the current naming convention
2025-10-17 09:39:58 +02:00
Andrey Antukh 42c416e3cb 📎 Add user feedback defaults to backend scripts/_env 2025-10-17 09:39:58 +02:00
Andrés Moya 5ad04e0f4c 🐛 Fix error when selecting set in theme 2025-10-16 16:17:16 +02:00
Belén Albeza 9f4db4479c 🐛 Make internal DOM of text editor v2 break words as the render engine does 2025-10-16 15:55:53 +02:00
alonso.torres 66997d2bc9 Add set_children granular methods for performance 2025-10-16 15:33:08 +02:00
Alejandro Alonso 7350329658 🐛 Filter svg attrs supported in wasm 2025-10-16 14:48:22 +02:00
Elena Torró 544b118925 Merge pull request #7361 from penpot/azazeln28-feat-dom-textarea-position
🎉 Text Editor DOM textarea position
2025-10-16 14:30:41 +02:00
Alejandro Alonso 8ceb909cda Merge pull request #7490 from penpot/elenatorro-12258-fix-text-shapes-intersection
🐛 Fix text tiles intersection
2025-10-16 14:23:41 +02:00
Elena Torro af54e6ccc2 🔧 Fix text layout extrect intersection and refactor calculate_extrect function 2025-10-16 14:00:59 +02:00
Elena Torro 6ef0b8fd16 🔧 Update fixed visual tests 2025-10-16 14:00:54 +02:00
Alejandro Alonso 4a6d143a15 Merge pull request #7522 from penpot/alotor-render-performance
 New render small performance optimizations
2025-10-16 13:45:49 +02:00
David Barragán Merino 07dedbd3bb 📎 Fix registry uri
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2025-10-16 13:06:34 +02:00
Aitor Moreno 7ca8bf32b2 🎉 Set DOM text editor element caret 2025-10-16 12:59:24 +02:00
alonso.torres 2e6fb1b9c5 New render small performance optimizations 2025-10-16 12:40:29 +02:00
AlexTECPlayz 43b03b9714 🌐 Add translations for: Romanian
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-16 12:07:26 +02:00
Alejandro Alonso 8e8d46b314 🐛 Fix performance macros disabled in production mode 2025-10-16 12:01:29 +02:00
Eva Marco e964f9820e 🐛 Fix tooltip position of proportion lock button (#7519) 2025-10-16 11:40:19 +02:00
Pablo Alba d933e91c6c 🐛 Fix variants not syncronizing tokens on switch 2025-10-16 11:21:18 +02:00
Eva Marco 24264e7d8a 🎉 Replace numeric inputs on layout menus (#7488)
* 🎉 Replace numeric inputs on layout menus

* 🎉 Add numeric inputs on gaps

* ♻️ Refactor scss file

* 🐛 Fix comments
2025-10-16 10:05:25 +02:00
Xaviju 5b77df997b 🐛 Remove color validation for non-color tokens in strokes (#7513) 2025-10-16 09:54:50 +02:00
Xaviju 968274096d 🐛 Fix visual glitch in transparent background for swatch component (#7509) 2025-10-16 09:54:33 +02:00
Alejandro Alonso 392e3ac34e Merge pull request #7507 from penpot/elenatorro-11974-fix-resize-on-blocked-shapes
🐛 Fix resize on blocked shapes from the viewport
2025-10-16 09:17:13 +02:00
Alejandro Alonso 9266ace537 Merge pull request #7514 from penpot/ladybenko-12293-fix-scroll-inspect
🐛 Fix scrollbar in the inspect tab
2025-10-16 07:07:56 +02:00
Elena Torro e8336a401e 🐛 Do not show resize controls when a selected shape is blocked 2025-10-16 06:43:34 +02:00
Elena Torro 18048a4b2e 🐛 Do not select a blocked frame when clicking its name 2025-10-16 06:42:45 +02:00
Elena Torro e28d4eaff1 🐛 Do not allow to resize a shape that is blocked from the viewport 2025-10-16 06:42:45 +02:00
Alejandro Alonso fae574796f Merge pull request #7512 from penpot/elenatorro-11963-change-font-styles-label
🌐 Add font-style label on workspace assets
2025-10-16 06:36:39 +02:00
Belén Albeza b057ed1b9a 🐛 Fix scroll on inspect tab 2025-10-15 15:30:27 +02:00
Elena Torro db59209b21 🌐 Add font-style label on workspace assets 2025-10-15 14:41:15 +02:00
Alejandro Alonso a62f1fb46f Merge pull request #7505 from penpot/elenatorro-review-extrect
🔧 Fix extrect and selrect debug interactivity
2025-10-15 14:02:22 +02:00
Alejandro Alonso ffd2aa03a9 Merge pull request #7510 from penpot/alotor-bugfix-pan-with-guides
🐛 Fix pan cursor not disabling viewport guides
2025-10-15 13:36:12 +02:00
Xaviju f1ebcaf635 Add composite typography token to text panel (#7496) 2025-10-15 13:21:30 +02:00
alonso.torres 6a4d0f05bc 🐛 Fix pan cursor not disabling viewport guides 2025-10-15 13:02:56 +02:00
Alejandro Alonso 4d751c5acd Merge pull request #7504 from penpot/azazeln28-feat-webgl-error
🎉 Add debug and alert when WebGL is unsupported
2025-10-15 12:51:45 +02:00
Alejandro Alonso 2c5abb0cbf Merge pull request #7506 from penpot/niwinz-staging-hotfix-6-comments-threads
 Add minor comment threads queries optimization
2025-10-15 12:04:42 +02:00
Andrey Antukh 7f6bffdbfc Add minor comment threads queries optimization 2025-10-15 11:45:24 +02:00
Andrey Antukh 9a5efe8671 Merge remote-tracking branch 'origin/staging' into develop 2025-10-15 11:32:15 +02:00
Andrey Antukh e0dd8247d4 Merge branch 'main' into staging 2025-10-15 11:25:36 +02:00
Andrey Antukh 1f65e2f560 Merge branch 'staging' into develop 2025-10-15 11:22:03 +02:00
Andrey Antukh abad6a15bc Merge branch 'main' into staging 2025-10-15 11:21:43 +02:00
Alejandro Alonso bf6874a96d Merge pull request #7460 from penpot/ladybenko-12205-cap-fills-text
 Cap the amount of text fills
2025-10-15 11:17:32 +02:00
Alejandro Alonso 7cdb1925d6 Merge pull request #7497 from penpot/ladybenko-12287-text-fills
🐛 Fix adding/removing identical text fills
2025-10-15 11:00:00 +02:00
Alejandro Alonso aec4464749 Merge pull request #7498 from penpot/niwinz-staging-hotfix-4
 Make worker subsystem more resilent to redis restarts
2025-10-15 10:39:04 +02:00
Alejandro Alonso 1d14644250 Merge pull request #7494 from penpot/niwinz-staging-hotfix-3
 Use system clock for check invitation expiration
2025-10-15 10:23:48 +02:00
Alejandro Alonso fad148e6a6 📎 Reorder jvm opts on _env 2025-10-15 10:15:06 +02:00
Elena Torro 3c05067c99 🔧 Fix extrect and selrect debug interactivity 2025-10-15 10:10:24 +02:00
Aitor Moreno bbb78904fb 🎉 Add debug and alert when WebGL is unsupported 2025-10-15 10:06:02 +02:00
Alejandro Alonso 879caf66eb Merge pull request #7493 from penpot/niwinz-staging-hotfix-2
🐛 Fix corrner case on error report validation
2025-10-15 09:51:52 +02:00
Andrey Antukh 4daf086214 📎 Backport circleci config from develop 2025-10-15 09:33:42 +02:00
Andrey Antukh c8b3a41117 📎 Backport all github workflows from develop 2025-10-15 09:33:42 +02:00
Andrey Antukh c9dcc8a4ee 🐛 Add migration for clearing :objects nil from components
on the local library
2025-10-15 09:33:40 +02:00
Stephan Paternotte 4739c4730c 🌐 Add translations for: Dutch
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-14 19:08:02 +02:00
Edgars Andersons 603bb860ba 🌐 Add translations for: Latvian
Currently translated at 95.3% (1866 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-14 19:08:01 +02:00
Yaron Shahrabani 55d9ca1439 🌐 Add translations for: Hebrew
Currently translated at 99.4% (1945 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-14 19:07:59 +02:00
Oğuz Ersen a2f397c329 🌐 Add translations for: Turkish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-14 19:07:57 +02:00
Roman D ada4e72c27 🌐 Add translations for: Russian
Currently translated at 78.2% (1530 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-14 19:07:54 +02:00
Andrey Antukh 17376dfa3f Add mark-file-as-trimmed srepl helper 2025-10-14 19:04:02 +02:00
Andrey Antukh 8d65e1cc94 Simplify reset-password srepl helper 2025-10-14 19:04:02 +02:00
Andrey Antukh d4de367499 🐛 Fix ::sm/set schema validation
It has several corner cases where set specific type
is not checked. It also now checks for ordered type
specifically when ordered is specified
2025-10-14 19:04:02 +02:00
Andrey Antukh 25521b18ff Make the restriction errors report as warning to logging 2025-10-14 19:04:02 +02:00
Andrey Antukh 39bdf026ca 🐛 Fix corrner case on error report validationt 2025-10-14 19:03:59 +02:00
Andrey Antukh 1b6a833166 🐛 Add migration for fix fills on position-data 2025-10-14 18:11:42 +02:00
Andrey Antukh 928dcf5cb8 🐛 Fix migrations set decoding on binfile import
that causes incorrect migration ordering on the table
2025-10-14 18:11:42 +02:00
Elena Torró 12e91751c3 🔧 Improve debugging UI (#7500) 2025-10-14 17:29:04 +02:00
David Barragán Merino 0f0c8466be 📎 Add bundle version to the metadata in S3 2025-10-14 16:30:31 +02:00
Belén Albeza 204d0dfb9d Add integration test to check fills limit in UI 2025-10-14 16:14:53 +02:00
Belén Albeza e4a3fc3940 Limit the amount of text fills passed to wasm 2025-10-14 16:05:03 +02:00
Andrey Antukh 5ae173f01c Make worker subsystem more resilent to redis restarts 2025-10-14 15:48:54 +02:00
Belén Albeza 840c1f59bc 🐛 Fix adding/removing identical text fills 2025-10-14 15:34:44 +02:00
Francis Santiago 29dc99deae 🔧 Replace ADD with COPY in Dockerfiles to fix hadolint warnings 2025-10-14 13:53:48 +02:00
Andrey Antukh f7b3913c71 Use system clock for check invitation expiration
instead of db time
2025-10-14 13:35:50 +02:00
alonso.torres 025f0d2fdb 🐛 Fix problem with tiles and transform 2025-10-14 12:51:48 +02:00
Andrey Antukh 85591bd579 Merge branch 'main' into staging 2025-10-14 12:30:21 +02:00
Andrey Antukh b3ae54775b 📎 Update changelog 2025-10-14 12:26:56 +02:00
Eva Marco 045aa7c788 🎉 Add tokens to color selection (#7447)
* 🎉 Add tokens to color row

* 🎉 Add color-token to stroke input

* 🐛 Fix change token on multiselection with groups

* 🎉 Create token colors on selected-colors section

* ♻️ Fix comments
2025-10-14 11:17:14 +02:00
Alejandro Alonso 497282d964 Merge pull request #7491 from penpot/niwinz-staging-fix-fills-position-data
🐛 Add migration for fix fills on position-data
2025-10-14 10:45:25 +02:00
Andrey Antukh 362bb7d2f6 🐛 Add migration for fix fills on position-data 2025-10-14 10:34:19 +02:00
Alejandro Alonso b4cd955484 🐛 Text line-height values are wrong 2025-10-14 07:53:37 +02:00
Andrey Antukh 55353b80a2 🔧 Simplfy circleci workflow dependencies 2025-10-13 18:54:47 +02:00
Andrey Antukh c46ab38d58 🔧 Add missing construction_worker to commit checker regex 2025-10-13 18:54:40 +02:00
Andrey Antukh 0f3ca67773 🔧 Simplfy circleci workflow dependencies 2025-10-13 18:53:39 +02:00
Andrey Antukh 1c06c87acf 🔧 Add missing construction_worker to commit checker regex 2025-10-13 18:53:39 +02:00
Andrey Antukh d532558bab Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 18:41:18 +02:00
Andrey Antukh a4192ce835 🐛 Fix incorrect file data migration from db to legacy-db 2025-10-13 18:41:02 +02:00
Andrey Antukh d3e28a8307 🐛 Set correct name to tokens lib data reader 2025-10-13 18:41:02 +02:00
Andrey Antukh 3122917872 Add more file-grained validation for tokens lib migration helpers 2025-10-13 18:41:02 +02:00
Andrey Antukh 95df07a364 Add strong file schema validation after file data migration 2025-10-13 18:41:02 +02:00
Andrey Antukh 71ed845307 Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 16:35:53 +02:00
Andrey Antukh c9761684c1 🐛 Fix regression introduced on duplicate-id on token-sets commit 2025-10-13 16:35:17 +02:00
Andrey Antukh dd35c82824 Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 15:19:07 +02:00
Andrey Antukh 515b381f66 🐛 Fix random failure of tokens lib test 2025-10-13 15:16:53 +02:00
Andrés Moya 160873c63e 🐛 Remove duplicated token set ids 2025-10-13 15:16:53 +02:00
Andrey Antukh fc35dc77ce 🐛 Enable migrations on calculate file library summary 2025-10-13 14:52:39 +02:00
Andrey Antukh b5648e1241 ⬆️ Update yarn to 2.10.3 on frontend module 2025-10-13 14:52:39 +02:00
Andrey Antukh d07e00da21 🐛 Fix incorrect filtering of unread comment threads
Do not return threads for deleted files
2025-10-13 14:52:39 +02:00
Xaviju 253605f6cc 🎉 Add text panel to inspect styles tab (#7413) 2025-10-13 14:20:55 +02:00
Xaviju 2548bec651 🐛 Fix visual glitch in background for swatch component (#7468) 2025-10-13 14:20:14 +02:00
Andrey Antukh da5da00bd4 📚 Update changelog 2025-10-13 14:08:33 +02:00
Andrey Antukh 72b44240b1 Merge branch 'develop' into staging 2025-10-13 13:30:43 +02:00
Andrey Antukh b21f79490b Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 13:22:26 +02:00
Andrey Antukh db09eacd4c Merge remote-tracking branch 'origin/main' into staging 2025-10-13 13:22:07 +02:00
Andrey Antukh 731afb0e70 📚 Update changelog 2025-10-13 13:19:18 +02:00
Pablo Alba 1080251d9a Add missing variant-switch tracking event 2025-10-13 13:01:02 +02:00
Andrey Antukh ea5bfbd72d 📎 Add token-typography-composite config flag to defaults 2025-10-13 12:54:27 +02:00
David Barragán Merino b10dcb2d63 🐳 Prevent error if config.js is a bind mounted file 2025-10-13 12:42:23 +02:00
Andrey Antukh c4cd665594 📎 Enable redis-cache flag on devenv start scripts 2025-10-13 12:32:29 +02:00
Andrey Antukh 1eb6f33bdd Enable optional caching of results for file summary RPC methods 2025-10-13 12:32:29 +02:00
Andrey Antukh 62dffd30a4 ♻️ Refactor redis internal API
The main idea behind this refactor is make the
API less especialized for specific use of out internal
submidules and make it more general and usable
for more general purposes (per example cache)
2025-10-13 12:32:29 +02:00
Andrey Antukh 12a4934c41 Allow pass :load-data? false to get-file 2025-10-13 12:32:29 +02:00
Andrey Antukh e3bd9148f2 Add ::sm/atom to schemas 2025-10-13 12:32:29 +02:00
Alejandro Alonso 2b7bd8fa5c 🐛 Fix deleted files are accesible 2025-10-13 12:24:05 +02:00
Andrey Antukh 5717708b56 ♻️ Refactor file storage
Make it more scallable and make it easily extensible
2025-10-13 12:24:05 +02:00
Andrey Antukh 27bed84543 Improve netty io executor shutdown 2025-10-13 12:24:05 +02:00
Andrey Antukh c6529f9585 🐛 Fix corner case on worker runner 2025-10-13 12:24:05 +02:00
Andrey AntukhandBelén Albeza 30e139ed10 🔥 Remove binary fills flag (#7462)
* 🐛 Add missing IEmptyableCollection protocol impl for wasm Shape

* 🔥 Remove frontend-binary-fills flag

*  Fix fill-limit integration tests

---------

Co-authored-by: Belén Albeza <belen@hey.com>
2025-10-13 12:14:25 +02:00
Andrey Antukh 0aadc3b6b3 Add management shared key authentication 2025-10-13 11:49:58 +02:00
Andrey Antukh 21a7ecb3fe 🔥 Remove obsolete code with object wrapping on components 2025-10-13 11:36:22 +02:00
Andrey Antukh 65a2b10875 Make component path mandatory on validations
And ensure it already present with a migration
2025-10-13 11:36:22 +02:00
Xavier Julian 9e676a7ab2 🐛 Fix unnecesarily overcomplated key generation 2025-10-13 11:30:47 +02:00
Xavier Julian 3d1933411b 🐛 Fix broken layout when multiple fill/strokes with tokens 2025-10-13 11:30:47 +02:00
VKing9 a44f1bb09c 🌐 Add translations for: Hindi
Currently translated at 92.2% (1805 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-10-13 11:26:55 +02:00
Henrik Allberg 65e59c8857 🌐 Add translations for: Swedish
Currently translated at 81.5% (1595 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-10-13 11:26:55 +02:00
Црнобог 2833854d8d 🌐 Add translations for: Serbian
Currently translated at 70.3% (1377 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2025-10-13 11:26:54 +02:00
Alejandro Alonso 19e367e112 🌐 Add translations for: Yoruba
Currently translated at 60.3% (1181 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-10-13 11:26:54 +02:00
Alejandro Alonso 3f731f57e6 🌐 Add translations for: Hausa
Currently translated at 63.7% (1247 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2025-10-13 11:26:54 +02:00
Stephan Paternotte b740ee254e 🌐 Add translations for: Dutch
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-13 11:26:53 +02:00
Sebastiaan Pasma 97d9480c8b 🌐 Add translations for: Dutch
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-13 11:26:53 +02:00
Edgars Andersons f1fcc77f74 🌐 Add translations for: Latvian
Currently translated at 94.6% (1852 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:26:52 +02:00
Ņikita K d078c49fe7 🌐 Add translations for: Latvian
Currently translated at 94.6% (1852 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:26:52 +02:00
Denys Kisil 9d30a1c1e9 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 93.4% (1827 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-10-13 11:26:52 +02:00
Zvonimir Juranko 3b9f732b16 🌐 Add translations for: Croatian
Currently translated at 82.2% (1608 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-10-13 11:26:51 +02:00
Dário 0a9c191582 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 80.6% (1578 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-10-13 11:26:51 +02:00
Amerey.eu cc6175d39c 🌐 Add translations for: Czech
Currently translated at 81.9% (1603 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-13 11:26:50 +02:00
Mikel Larreategi b424c0f84b 🌐 Add translations for: Basque
Currently translated at 59.3% (1161 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/
2025-10-13 11:26:50 +02:00
Radek Sawicki 29495474b1 🌐 Add translations for: Polish
Currently translated at 58.0% (1136 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2025-10-13 11:26:49 +02:00
Nicola Bortoletto 7c0bd4ac9a 🌐 Add translations for: Italian
Currently translated at 99.4% (1945 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-13 11:26:49 +02:00
william chen b98f5a9851 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 82.2% (1608 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-10-13 11:26:48 +02:00
Yaron Shahrabani 4d823af46d 🌐 Add translations for: Hebrew
Currently translated at 99.2% (1942 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-13 11:26:48 +02:00
Linerly ecec2db29e 🌐 Add translations for: Indonesian
Currently translated at 87.2% (1706 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-10-13 11:26:47 +02:00
Alejandro Alonso 55d2d53a22 🌐 Add translations for: Arabic
Currently translated at 57.0% (1115 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-10-13 11:26:46 +02:00
Mahmoud A. Rabo 86f7bec171 🌐 Add translations for: Arabic
Currently translated at 57.0% (1115 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-10-13 11:26:45 +02:00
AlexTECPlayz 5c6d296e60 🌐 Add translations for: Romanian
Currently translated at 65.6% (1284 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-13 11:26:45 +02:00
George Lemon bda6c61a11 🌐 Add translations for: Romanian
Currently translated at 65.6% (1284 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-13 11:26:44 +02:00
Allan Nordhøy 3aa966e553 🌐 Add translations for: Norwegian Bokmål
Currently translated at 8.3% (164 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nb_NO/
2025-10-13 11:26:44 +02:00
Marius 9ce0b9c86e 🌐 Add translations for: German
Currently translated at 89.0% (1741 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-13 11:26:43 +02:00
Pablo Alba 42ab42fb56 🌐 Add translations for: German
Currently translated at 89.0% (1741 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-13 11:26:43 +02:00
Hugo Figueira 7b60d386fb 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 64.9% (1271 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-10-13 11:26:42 +02:00
Anonymous ae60a7260c 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.6% (1813 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-13 11:26:42 +02:00
Çağlar Yeşilyurt bfacdc414f 🌐 Add translations for: Turkish
Currently translated at 93.7% (1833 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-13 11:26:42 +02:00
Vint Prox 29628eea0a 🌐 Add translations for: Russian
Currently translated at 73.1% (1430 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-13 11:26:41 +02:00
The_BadUser 6d7723c36b 🌐 Add translations for: Russian
Currently translated at 73.1% (1430 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-13 11:26:40 +02:00
Anonymous 710008ee9e 🌐 Add translations for: Greek
Currently translated at 26.1% (511 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/el/
2025-10-13 11:26:40 +02:00
Pablo Alba a15be5c2d0 🌐 Add translations for: French
Currently translated at 94.7% (1853 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-13 11:26:40 +02:00
Aryiu 45a09928b3 🌐 Add translations for: Catalan
Currently translated at 54.8% (1073 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ca/
2025-10-13 11:26:39 +02:00
Hosted Weblate bae5196d1e 🌐 Merge branch 'origin/develop' into Weblate. 2025-10-13 11:24:12 +02:00
Edgars Andersons 91fe7b2dd6 🌐 Add translations for: Latvian
Currently translated at 94.8% (1856 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:23:18 +02:00
Oğuz Ersen df6448e32e 🌐 Add translations for: Turkish
Currently translated at 93.9% (1837 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-13 11:23:16 +02:00
Luis de Dios a9a9245ab6 🐛 Fix component number has no singular translation string (#7469) 2025-10-13 11:22:25 +02:00
Eva Marco e18f20666b 🔧 Add scss refactor as element on PR checklist (#7480) 2025-10-13 11:21:12 +02:00
Eva Marco adafe0648c ♻️ Fix component schemas (#7481) 2025-10-13 11:20:43 +02:00
Andrey Antukh 1b9deecefc Make the binfile import process more resilient (#7464)
on small inconsistencies on file media object references
2025-10-13 11:13:10 +02:00
Nicola Bortoletto 666410602b 🌐 Add translations for: Italian
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-11 07:07:23 +02:00
Stephan Paternotte c956600d64 🌐 Add translations for: Dutch
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-08 18:07:31 +00:00
Edgars Andersons 2100c8a115 🌐 Add translations for: Latvian
Currently translated at 94.5% (1850 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-08 18:07:29 +00:00
Yaron Shahrabani d4fd246622 🌐 Add translations for: Hebrew
Currently translated at 99.4% (1946 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-08 18:07:25 +00:00
David Barragán Merino 71fd6640af 👷 Automate publication of docker images in a new release 2025-10-08 17:15:10 +02:00
Alonso Torres 40e9a78f67 Add telemetry for events performance measures (#7457) 2025-10-08 14:03:09 +02:00
Alejandro Alonso 551a25661f Improve ancestors modifiers performance 2025-10-08 12:10:18 +02:00
Luis de Dios 544bedf7c2 🎉 Reorder properties for a component (#7429)
* 🎉 Reorder properties when a component with variants is selected

* 🎉 Reorder properties when a single variant is selected

* ♻️ Refactor SCSS and component structure

* 📚 Update changelog

* 📎 PR changes (styling)

* 📎 PR changes (functionality)
2025-10-08 11:27:01 +02:00
Andrey Antukh 4937580585 🌐 Rehash and validate translation files 2025-10-07 18:38:28 +02:00
VKing9 dba0f11670 🌐 Add translations for: Hindi
Currently translated at 92.6% (1810 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-10-07 18:38:28 +02:00
Henrik Allberg 776af8ea22 🌐 Add translations for: Swedish
Currently translated at 81.7% (1597 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-10-07 18:38:28 +02:00
Црнобог 38e0d0035f 🌐 Add translations for: Serbian
Currently translated at 70.6% (1380 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2025-10-07 18:38:28 +02:00
Anonymous f0c01d8714 🌐 Add translations for: Yoruba
Currently translated at 60.5% (1184 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-10-07 18:38:28 +02:00
Anonymous 678c4acdbc 🌐 Add translations for: Igbo
Currently translated at 26.4% (516 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2025-10-07 18:38:28 +02:00
Anonymous 9b76048c2f 🌐 Add translations for: Malay
Currently translated at 34.6% (678 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ms/
2025-10-07 18:38:28 +02:00
Anonymous 2cf98745d7 🌐 Add translations for: Hausa
Currently translated at 63.9% (1250 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2025-10-07 18:38:28 +02:00
Stephan Paternotte 267acc5b80 🌐 Add translations for: Dutch
Currently translated at 97.1% (1898 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-07 18:38:28 +02:00
Anonymous ae38c8e840 🌐 Add translations for: Latvian
Currently translated at 94.6% (1850 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-07 18:38:27 +02:00
Edgars Andersons 1128303fa1 🌐 Add translations for: Latvian
Currently translated at 94.6% (1850 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-07 18:38:27 +02:00
Denys Kisil 5d1981047d 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 93.5% (1828 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-10-07 18:38:27 +02:00
al0cam 0dae2a3c24 🌐 Add translations for: Croatian
Currently translated at 82.3% (1610 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-10-07 18:38:27 +02:00
TheScientistPT 766accde29 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 80.8% (1580 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-10-07 18:38:27 +02:00
Anonymous c5f8bd5bea 🌐 Add translations for: Czech
Currently translated at 82.1% (1605 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-07 18:38:27 +02:00
Amerey.eu 4edfcba350 🌐 Add translations for: Czech
Currently translated at 82.1% (1605 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-07 18:38:27 +02:00
Nicola Bortoletto ce20059a4c 🌐 Add translations for: Italian
Currently translated at 96.9% (1895 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-07 18:38:27 +02:00
william chen 27fa0c0721 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 82.3% (1610 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-10-07 18:38:27 +02:00
Yaron Shahrabani ee83a07674 🌐 Add translations for: Hebrew
Currently translated at 96.7% (1891 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-07 18:38:27 +02:00
Linerly 57123569eb 🌐 Add translations for: Indonesian
Currently translated at 87.4% (1708 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-10-07 18:38:27 +02:00
Anonymous d112e83b0d 🌐 Add translations for: German
Currently translated at 89.2% (1743 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-07 18:38:27 +02:00
Stas Haas b259ca2cd1 🌐 Add translations for: German
Currently translated at 89.2% (1743 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-07 18:38:27 +02:00
Rick Benetti 56db7078ae 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 65.1% (1273 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-10-07 18:38:27 +02:00
Anonymous 7a75002cc9 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
DoubleCat 77ad1f57be 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
Jun Fang 9f881e49e5 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
Anonymous 238578f243 🌐 Add translations for: Turkish
Currently translated at 73.1% (1429 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-07 18:38:27 +02:00
The_BadUser 49d9dd7161 🌐 Add translations for: Russian
Currently translated at 73.2% (1432 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-07 18:38:27 +02:00
Vin be28a310f7 🌐 Add translations for: Russian
Currently translated at 73.2% (1432 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-07 18:38:27 +02:00
Anonymous 460aafbedf 🌐 Add translations for: French
Currently translated at 94.8% (1854 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-07 18:38:27 +02:00
Corentin Noël 9ba995edd9 🌐 Add translations for: French
Currently translated at 94.8% (1854 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-07 18:38:27 +02:00
Deleted User 85b15e4896 🌐 Add translations for: Spanish
Currently translated at 97.0% (1896 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-10-07 18:38:27 +02:00
Anonymous 1420e8a59c 🌐 Add translations for: Spanish
Currently translated at 97.0% (1896 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-10-07 18:38:27 +02:00
Hosted Weblate 3aa647e1f6 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-10-07 18:38:27 +02:00
Pablo AlbaandAndrey Antukh 11b7b458bf 🐛 Auto-width changes to fixed when switching variants (#7449)
* 🐛 Auto-width changes to fixed when switching variants

* 📎 Fix typo on changes

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-10-07 18:29:18 +02:00
Andrey Antukh cfa607f57f Merge remote-tracking branch 'weblate/develop' into develop 2025-10-07 18:23:36 +02:00
Andrey Antukh cea0143327 Add extra tenant validation for virtual clock dbg pannel 2025-10-07 18:21:13 +02:00
Andrey Antukh b8158ffec8 Merge remote-tracking branch 'origin/staging' into develop 2025-10-07 16:03:17 +02:00
Pablo Alba 640894acd8 🐛 Fix Restoring a variant from another file makes it overlap (#7448) 2025-10-07 16:01:59 +02:00
Alejandro Alonso 90bfae3ec1 Merge pull request #7456 from penpot/elenatorro-12248-fix-shadows-order
🔧 Always return shadows in reverse order
2025-10-07 13:16:49 +02:00
Alejandro Alonso 73ed5f8bc5 Merge pull request #7402 from penpot/niwinz-develop-enhancements-3
 Add additional http middlewares
2025-10-07 13:00:30 +02:00
Andrey Antukh 2c1a8b59ba Add client header check middleware
As an additional csrf protection for API requests
2025-10-07 12:47:14 +02:00
Andrey Antukh 47d9c6f282 Add sec-fetch metadata middleware support 2025-10-07 12:47:14 +02:00
Andrey Antukh 14d53c224f 🔥 Remove unused auth-data cookie asignation 2025-10-07 12:47:12 +02:00
Elena Torró a1b8eb7074 Merge pull request #7454 from penpot/elenatorro-12247-fix-line-height-min-value
🔧 Allow line height values from 0 to 1
2025-10-07 12:29:16 +02:00
Alejandro Alonso e0f9bbb23f Merge pull request #7432 from penpot/niwinz-develop-virtual-clock
🎉 Add virtual clock support
2025-10-07 12:27:49 +02:00
Elena Torro 9b16a6bbd1 🔧 Always return shadows in reverse order 2025-10-07 12:26:51 +02:00
Marina López fede63ac0b Update Design System template in carousel 2025-10-07 12:14:12 +02:00
Eva Marco ea1ab7c23b 🐛 Fix modal title on edit token (#7453)
* 🐛 Fix modal title on edit token

* 🐛 Fix font size on numeric input

* ♻️ Update color token border on hover

* ♻️ Use swatch component on token pill
2025-10-07 12:11:40 +02:00
Andrey Antukh 61d9b57bc7 ♻️ Refactor internal tokens API
Mainly make it receive the whol cfg/system instead only props. This
makes the api more flexible for a future extending without the need
to change the api again.
2025-10-07 12:08:00 +02:00
Andrey Antukh bd63598185 🎉 Add virtual clock implementation 2025-10-07 12:08:00 +02:00
Eva Marco 31af6aebbd 🐛 Fix swatch error on text without fills (#7451) 2025-10-07 10:08:46 +02:00
Andrey Antukh cc5f86bc84 🐛 Set correct path if is not provided on sdk addComponent method 2025-10-07 09:57:41 +02:00
Elena Torro 68cd7075c0 🔧 Allow line height values from 0 to 1 2025-10-07 09:28:24 +02:00
Andrey Antukh 11ff64b362 Merge tag '2.10.1-RC3' 2025-10-06 12:12:27 +02:00
Andrey Antukh 57a7b5b1da Merge remote-tracking branch 'origin/staging' into develop 2025-10-06 12:12:05 +02:00
Andrey Antukh cf24bdd7a8 Merge remote-tracking branch 'origin/main' into staging 2025-10-06 12:10:44 +02:00
Andrey Antukh 683db071d6 Merge remote-tracking branch 'origin/staging' into develop 2025-10-06 12:09:57 +02:00
Andrey Antukh d3943b9162 Merge branch 'niwinz-staging-library-changes' into staging 2025-10-06 11:59:17 +02:00
Andrey Antukh 613acd5b29 📎 Update version on library/package.json file 2025-10-06 11:56:23 +02:00
Andrey Antukh 987dea8048 📚 Update changelog 2025-10-06 11:53:50 +02:00
Andrey Antukh 6b0d0a302f Enable variant attrs on SDK addComponent method 2025-10-06 11:53:50 +02:00
Andrey Antukh 588eb0b4fa ⬆️ Update shadow-cljs dependency on sdk 2025-10-06 11:53:50 +02:00
Andrey Antukh b30cb0e084 Allow pass variant related attrs on add-component change 2025-10-06 11:53:50 +02:00
Andrey Antukh 9244501c6e Add variants/v1 feature to default features emited by sdk 2025-10-06 11:53:50 +02:00
Alonso Torres d5b743c604 🐛 Fix problem with text in plugins (#7446) 2025-10-06 11:19:33 +02:00
Andrey Antukh e38dd21307 ⬆️ Update exported dependencies 2025-10-06 10:56:04 +02:00
Andrey Antukh 8c91109c63 📚 Update changelog 2025-10-06 09:38:01 +02:00
Andrey Antukh c3eabbdb25 📎 Enable the fdata/objects-map feature by default
Using config flags
2025-10-06 09:38:01 +02:00
Andrey Antukh 67661674e2 Make deleted fonts fixer to run with more granular stragegy
Instead of running it on all the file, only run it to local library
and the current page, reducing considerably the overhead of analyzing
the whole file on each file load.

It stills executes for page each time the page is loaded, and add
some kind of local cache for not doing repeated work each time page
loads is pending to be implemented in other commit.
2025-10-06 09:38:01 +02:00
Andrey Antukh c70e7f3876 Add logging to frontend repo namespace 2025-10-06 09:38:01 +02:00
Andrey Antukh 0295f0f7c8 Add better workspace file indexing strategy
Improve file indexes initialization on workspace.

Instead of initialize indexes for all pages only initialize
indexes for the loaded page.
2025-10-06 09:38:01 +02:00
Andrey Antukh 54bb879cb6 📎 Add several missing imports on repl related namespaces 2025-10-06 09:38:01 +02:00
Andrey Antukh b72704e54b Set explicit no-buffering for sse responses 2025-10-06 09:19:48 +02:00
Natacha 362a31dd22 Add typography composite token to changelog (#7434)
Signed-off-by: Natacha <natachamenjibar@gmail.com>
2025-10-06 09:17:29 +02:00
Yamila Moreno 6c6ec7a620 Merge pull request #7433 from penpot/yms-improve-self-host-documentation
📚 Improve self-host documentation
2025-10-03 13:24:37 +02:00
Yamila Moreno 21a7d30c5e 📚 Improve self-host documentation 2025-10-03 13:21:06 +02:00
Andrés Moya 52fef6c318 🐛 Fix sync errors when there is a broken swap slot 2025-10-03 12:51:33 +02:00
Eva Marco fc8029abf7 🐛 Fix some errors from reviews (#7421)
* 🐛 Fix errors con colorpicker

* 🐛 Fix modal size

* 🐛 Fix form padding

* 🐛 Fix edit modal title

* 🐛 Fix resolved value message

* 🐛 Fix CI
2025-10-03 11:25:56 +02:00
Eva Marco 44f6c2f83c 🎉 Show tokens on color inputs (#7377)
* 🎉 Add tokens to color row

* 🎉 Add color-token to stroke input

* 🐛 FIx change token on multiselection with groups

* 🔧 Add config flag

* 🐛 Fix comments
2025-10-03 11:19:01 +02:00
Eva Marco a4f20564af 🐛 Fix numeric input errors detected on review (#7427)
* 🐛 Fix review errors

* 📚 Add docs for numeric input component

* 🐛 Sort tokens alphabetically
2025-10-03 10:14:25 +02:00
Eva Marco 93d4b19477 🐛 Fix shadow inputs (#7426) 2025-10-03 10:13:27 +02:00
Aitor Moreno 7dd26dee13 Merge pull request #7428 from penpot/elenatorro-fix-shadows-order
🐛 Fix shadows order
2025-10-02 17:16:58 +02:00
Elena Torró 4594635009 Merge pull request #7399 from penpot/ladybenko-12164-handle-font-404
🐛 Fix internal error when fonts return 404 (wasm)
2025-10-02 16:42:58 +02:00
Elena Torro 7e852cb3ac 🐛 Fix shadows order 2025-10-02 16:40:20 +02:00
Belén Albeza 6e82b0f1ba 🐛 Fix shadow serialization (#7423) 2025-10-02 15:17:01 +02:00
Florian Schroedl 472148ff9d 🐛 Fix empty values re-triggering validation 2025-10-02 14:31:55 +02:00
Florian Schroedl d01df7738a ♻️ Extract composite component wrapper 2025-10-02 14:31:55 +02:00
Xaviju 73222f22d0 🎉 Add stroke panel to inspect styles tab (#7408) 2025-10-02 13:58:08 +02:00
Andrey Antukh b90aba0f95 Merge tag '2.10.0' 2025-10-02 12:37:58 +02:00
Florian Schroedl 17fe012f7e 🐛 Fix form not being saveable when editing composite token and switching tabs 2025-10-02 10:48:14 +02:00
Belén Albeza 60f45d1fd7 🐛 Fix internal error crash when attempting to download a font resource that returns 404 2025-10-02 09:58:38 +02:00
alonso.torres 979b4276ca 🐛 Fix problem with component swapping panel 2025-10-02 09:10:21 +02:00
Elena Torró a32fe40528 Merge pull request #7409 from penpot/ladybenko-fix-wasm-playwright-ci
🔧 Fix Playwright config in CI to include the wasm build
2025-10-02 09:03:12 +02:00
Natacha b602df549e Add new shadow icons (#7416)
*  Adds new shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

*  Add shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

*  Adds shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

---------

Signed-off-by: Natacha <natachamenjibar@gmail.com>
2025-10-01 17:11:19 +02:00
Luis de Dios 7f1ab08ec8 🐛 Fix use a pointer cursor for adding variant from the viewport (#7410) 2025-10-01 17:01:07 +02:00
Luis de Dios 1263ea11fa 🐛 Fix order of component menu options in assets tab (#7388)
* 🐛 Reorder component menu options in assets tab

* ♻️ Use new component syntax

* 📚 Add bugfix to changelog

* ♻️ Code restructuring and SCSS improvements
2025-10-01 17:00:27 +02:00
Yamila Moreno ce26c52b30 👷 Automate docker images creation 2025-10-01 14:54:00 +02:00
Yamila Moreno 5c8b3ac3d6 👷 Automate docker images creation 2025-10-01 13:41:03 +02:00
Aitor Moreno bd4d576172 Merge pull request #7412 from penpot/elenatorro-fix-loop-all-ancestors
🐛 Break loop when no parent is present
2025-10-01 13:36:16 +02:00
Elena Torro e10169b3db 🐛 Break loop when no parent is present 2025-10-01 12:43:56 +02:00
Elena Torró f119a9548d Merge pull request #7411 from penpot/azazeln28-fix-issue-12185-wrong-text-width-height-layout
🐛 Fix wrong text auto width/height layout
2025-10-01 12:40:58 +02:00
Aitor Moreno c097aef152 🐛 Fix wrong text auto width/height layout 2025-10-01 12:27:38 +02:00
Andrey Antukh 000fa51c73 🐛 Fix zip handling on exporter 2025-10-01 11:56:57 +02:00
Belén Albeza d815494ffa 🔧 Fix playwright config to do a wasm build 2025-10-01 11:27:13 +02:00
Andrey Antukh a25ba6b482 📎 Fix incorrect regex for match merge and revert commits 2025-10-01 11:07:21 +02:00
Andrey Antukh e8434c3370 📎 Update devenv tmux script to start exporter using yarn 2025-10-01 10:59:41 +02:00
Andrey Antukh 7cf4ec2792 ♻️ Make the exporter build as esm module 2025-10-01 10:58:03 +02:00
Andrey Antukh 365ce25996 Merge remote-tracking branch 'origin/staging' into develop 2025-10-01 10:50:19 +02:00
Andrey Antukh 01ef55e4f4 Revert " Add minor improvement to cljs impl logging"
This reverts commit 960b76f760.
2025-10-01 10:48:24 +02:00
Andrey Antukh 3b81c1d750 Revert "♻️ Make the exporter build as esm module"
This reverts commit d0f34f06a9.
2025-10-01 10:47:47 +02:00
Elena Torró 40b34da788 Merge pull request #7269 from penpot/azazeln28-feat-caret-position
🎉 Feat caret position
2025-10-01 09:43:03 +02:00
Aitor Moreno 732c79b7b5 🎉 Add function to retrieve caret position 2025-10-01 09:18:46 +02:00
Andrey Antukh d0f34f06a9 ♻️ Make the exporter build as esm module 2025-10-01 08:10:37 +02:00
Andrey Antukh 23d5bdd20b 🐛 Add missing poppler-tools dependency on devenv 2025-10-01 08:10:37 +02:00
Andrey Antukh 9f2dc06c95 Add missing srepl helper for disable objects-map feat 2025-10-01 08:10:37 +02:00
Andrey Antukh 62563d28d0 📎 Bump library version to 1.0.9
Mainly fixes dependencies declaration on package.json file
2025-09-30 21:55:17 +02:00
Andrey Antukh 21e2ee9904 🐛 Fix dependencies on library 2025-09-30 21:53:04 +02:00
brian mwenda e6c418eb9c 🐛 Improve auto-width to fixed conversion logic in layout contexts
Signed-off-by: Brian Mwenda <brian@nathandigital.com>
2025-09-30 21:48:03 +02:00
Luis de Dios de5ff227d2 🎉 Create variant from the viewport (#7357)
* 🎉 Create variant from the viewport

* ♻️ Use DS styles and new component syntax

* 📎 PR changes
2025-09-30 18:15:17 +02:00
Florian Schroedl 0f67730198 🐛 Dont forward default-value for mismatching tab-type 2025-09-30 14:27:40 +02:00
Florian Schroedl 3da02e2b6b 🐛 Fixes resolved values being prefilled for existing referenced composite token 2025-09-30 14:27:40 +02:00
Florian Schroedl ab80021fb1 🐛 Fix performance issue on font-family 2025-09-30 14:27:40 +02:00
Xaviju f31e9b8ac9 🎉 Add blur panel to inspect styles tab (#7397) 2025-09-30 13:08:52 +02:00
Andrey Antukh 7d16515eb7 Add minor enhacements to logging on frontend (#7401)
*  Add logging consistency enhacements on fonts loading

*  Disable data evens ns logging

*  Simplify flags logging on application initialization

*  Improve features logging
2025-09-30 11:59:41 +02:00
Pablo Alba cd9ba482e3 🐛 Load dependant libraries, and don't allow unload them 2025-09-30 09:55:21 +02:00
David Barragán Merino dff1ca23d3 📚 Update changelog 2025-09-29 18:08:28 +02:00
Andrey Antukh c363d4d937 📎 Bump library version 2025-09-29 13:44:14 +02:00
Andrey Antukh de25a24a6d 🐛 Fix backend repl start issue with jdk 24 2025-09-29 13:35:48 +02:00
Andrey Antukh accc9a173f Merge remote-tracking branch 'origin/staging' into develop 2025-09-29 13:24:31 +02:00
Andrey Antukh 2d364dde5c Add several minor enhacements to features subsystem
Mainly fixes the team non-inheritable features handling and
removes unnecesary/duplicate checks.
2025-09-29 13:23:16 +02:00
Andrey Antukh c892a9f254 Integrate objects-map usage on backend and frontend 2025-09-29 13:23:16 +02:00
Andrey Antukh aaae35fb51 🎉 Add multiplatform impl of ObjectsMap
The new type get influentiated by the ObjectsMap impl on backend
code but with simplier implementation that no longer restricts keys
to UUID type but preserves the same performance characteristics.

This type encodes and decodes correctly both in fressian (backend)
and transit (backend and frontend).

This is an initial implementation and several memory usage
optimizations are still missing.
2025-09-29 13:23:16 +02:00
Andrey Antukh 960b76f760 Add minor improvement to cljs impl logging
Mainly reduce the emmited code, that will contribute to reduce the
bundle size and also adds timestamp to the default output.
2025-09-29 13:23:16 +02:00
Andrey Antukh d921e7eaa3 📎 Add not-empty generator to schema generator ns 2025-09-29 13:23:16 +02:00
Andrey Antukh 49f06b25fa 📚 Update changelog 2025-09-29 13:23:01 +02:00
Andrey Antukh 5ffb7ae2ec Add warning on using deprecated storage config 2025-09-29 13:23:01 +02:00
Andrey Antukh 27945ace65 Revert deprecated storage config cleaning 2025-09-29 13:23:01 +02:00
María ValderramaandEva Marco e39bf0b439 Invitations management improvements (#7230)
*  Invitations management improvements

* 📎 Change invite email subject

* 📎 Update icon usage

* ♻️ Fix css file

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
2025-09-29 13:18:57 +02:00
Alonso Torres deee7f7334 Merge pull request #7366 from penpot/niwinz-develop-page-data-type
 Add several enhancements for reduce workspace file load time
2025-09-29 12:43:34 +02:00
Xaviju 20d61cbce2 Create ghost variant for select DS component (#7392) 2025-09-29 12:24:20 +02:00
Andrés Moya 9ad8d3fd08 🔧 Make small improvements from PR comments 2025-09-29 12:16:42 +02:00
Andrés Moya 4c35571336 🔧 Read and modify token themes by id 2025-09-29 12:16:42 +02:00
Andrés Moya 37679b7ec6 🔧 Organize token changes API 2025-09-29 12:16:42 +02:00
Andrés Moya 194eded930 🔧 Unify path name helper functions 2025-09-29 12:16:42 +02:00
Andrés Moya 4e607d8da2 💄 Clarify and reorder interfaces 2025-09-29 12:16:42 +02:00
Andrés Moya f5fd978a07 🔧 Retrieve tokens from library and not from set 2025-09-29 12:16:42 +02:00
Andrés Moya b28be62845 🔧 Fix rebase problems 2025-09-29 12:16:42 +02:00
Andrés Moya d76a5c615c 🔧 Modify token sets by id instead of name and review usage 2025-09-29 12:16:42 +02:00
Andrés Moya 03e05da41e 💄 Normalize some attributes of changes 2025-09-29 12:16:42 +02:00
Andrés Moya 5f886e141a 💄 Minor changes 2025-09-29 12:16:42 +02:00
Andrés Moya 021b8f81ca 🔧 Read token sets by id instead of name 2025-09-29 12:16:42 +02:00
Andrey Antukh f32112544e Make deleted fonts fixer to run with more granular stragegy
Instead of running it on all the file, only run it to local library
and the current page, reducing considerably the overhead of analyzing
the whole file on each file load.

It stills executes for page each time the page is loaded, and add
some kind of local cache for not doing repeated work each time page
loads is pending to be implemented in other commit.
2025-09-29 12:07:49 +02:00
Andrey Antukh 27e311277a Add logging to frontend repo namespace 2025-09-29 12:07:49 +02:00
Andrey Antukh b9030fcc73 Add better workspace file indexing strategy
Improve file indexes initialization on workspace.

Instead of initialize indexes for all pages only initialize
indexes for the loaded page.
2025-09-29 12:07:49 +02:00
Andrey Antukh e1519f0ee4 Integrate objects-map usage on backend and frontend 2025-09-29 12:07:48 +02:00
Andrey Antukh 7fefe6dbc8 🎉 Add multiplatform impl of ObjectsMap
The new type get influentiated by the ObjectsMap impl on backend
code but with simplier implementation that no longer restricts keys
to UUID type but preserves the same performance characteristics.

This type encodes and decodes correctly both in fressian (backend)
and transit (backend and frontend).

This is an initial implementation and several memory usage
optimizations are still missing.
2025-09-29 12:06:56 +02:00
Andrey Antukh fdf70ae9c1 Fix docstring on common.weak ns function 2025-09-29 12:06:56 +02:00
Andrey Antukh 528315b75c 📎 Add not-empty generator to schema generator ns 2025-09-29 12:06:56 +02:00
Andrey Antukh 42d03a0325 📎 Add several missing imports on repl related namespaces 2025-09-29 12:06:56 +02:00
Andrey Antukh 0346c48b03 Add several minor enhacements to features subsystem
Mainly fixes the team non-inheritable features handling and
removes unnecesary/duplicate checks.
2025-09-29 12:06:56 +02:00
Andrey Antukh 1d54fe2e24 Add support for emit messages without waiting response on worker 2025-09-29 12:06:56 +02:00
Andrey Antukh 255f5af2e3 Add several enhacements to buffer namespace
The changes are just for completenes.
2025-09-29 12:06:56 +02:00
Andrey Antukh eea65b12dd Add minor improvement to cljs impl logging
Mainly reduce the emmited code, that will contribute to reduce the
bundle size and also adds timestamp to the default output.
2025-09-29 12:06:56 +02:00
Andrey Antukh d4b7f231c7 🔧 Add missing config for :rewind: on commit checker 2025-09-29 12:05:09 +02:00
Andrey Antukh 473066cf5c 🔧 Add missing config for :rewind: on commit checker 2025-09-29 12:04:37 +02:00
andrés gonzález d1607fbe54 💄 Update Help Center images (#7266) 2025-09-29 11:54:47 +02:00
Xaviju 5e84bda404 🎉 Add SVG panel to inspect styles tab (#7373) 2025-09-29 09:53:15 +02:00
Andrey Antukh c1058c7fdb ♻️ Add minor refactor for internal concurrency model
Replace general usage of virtual threads with platform threads
and use virtual threads for lightweight procs such that websocket
connections. This decision is made mainly because virtual threads
does not appear on thread dumps in an easy way so debugging issues
becomes very difficult.

The threads requirement of penpot for serving http requests
is not very big so having so this decision does not really affects
the resource usage.
2025-09-26 14:35:06 +02:00
Andrey Antukh 9d907071aa ⬆️ Update dependencies (#7330)
* ⬆️ Update to JDK25 on the devenv

* ⬆️ Update dependencies

* 🔥 Remove unused flag from devenv backend startup scripts

*  Enable shenandoah gc on backend scripts/repl
2025-09-26 13:43:43 +02:00
Elena Torró c32b94abcf Merge pull request #7343 from penpot/elenatorro-12118-support-large-svg-files
🐛 Fix parsing large paths with multiple subpaths
2025-09-26 13:35:17 +02:00
Elena Torro 9d8ad0ea6e 🐛 Fix parsing large paths with multiple subpaths 2025-09-26 13:04:47 +02:00
Yamila Moreno 2b1e107a44 Merge pull request #7390 from penpot/yms-add-curl-dependency
🐳 Add curl to the backend image
2025-09-26 11:40:42 +02:00
Yamila Moreno 2196318cfc 🐳 Add curl to the backend image 2025-09-26 11:23:02 +02:00
Yamila Moreno b3d1701698 Merge pull request #7355 from penpot/yms-docker-update-nginx-entrypoint
🐳 Improve Docker nginx
2025-09-26 10:49:24 +02:00
Yamila Moreno 042bd03beb 🐳 Improve Docker nginx 2025-09-26 10:31:23 +02:00
andrés gonzález cce1dd86a2 💄 Change variants video source to peertube (#7387) 2025-09-26 10:21:41 +02:00
Juan de la Cruz a39a127f03 🐛 Fix underline text in template card at carrusel 2025-09-26 09:56:05 +02:00
Pablo Alba bd665f70bf 💄 Add new library modal UI tweaks 2025-09-25 22:56:27 +02:00
Eva Marco e184a9a8b9 🐛 Fix context menu on spacing tokens (#7382) 2025-09-25 17:28:46 +02:00
Elena Torró 9b90236b72 Merge pull request #7385 from penpot/elenatorro-improve-image-load-performance
🔧 Improve image parsing performance
2025-09-25 17:20:49 +02:00
Elena Torro bf6cdf729d 🔧 Improve image parsing performance 2025-09-25 17:17:42 +02:00
Belén Albeza 361bdb4a04 ♻️ Decouple serialization from text/layout models" (#7360)
* ♻️ Move text serialization code to wasm module

* ♻️ Add serializer for TextAlign

* ♻️ Add serializers for TextDirection and TextDecoration

* ♻️ Add serializer for TextTransform

* ♻️ Remove unused font_style from TextLeaf model

* ♻️ Refactor parsing of TextLeaf from bytes

* ♻️ Decouple tight serialization of Paragraph
2025-09-25 16:54:07 +02:00
andrés gonzález 58c6c94cb8 📚 Update boards info at the user guide (#7383) 2025-09-25 16:36:35 +02:00
Elena Torró 3827aa6bd4 Merge pull request #7344 from penpot/elenatorro-11542-truncate-long-font-names-on-fonts-menu
🔧 Use two lines text ellipsis on custom font names
2025-09-25 15:25:50 +02:00
Xaviju adf7b0df50 🎉 Add visibility panel to inspect styles tab (#7362) 2025-09-25 12:52:43 +02:00
Elena Torro 97b4491a27 🔧 Use two lines text ellipsis on custom font names 2025-09-25 12:49:33 +02:00
andrés gonzález ecee7ecfc7 📚 Update workspace info at the user guide (#7376) 2025-09-25 12:24:59 +02:00
Xavier Julian 015bd9e453 🎉 Inspect styles tab: fill panel 2025-09-25 11:31:15 +02:00
Belén Albeza 49d5987b15 💄 Add deprecated namespace and fix import for remaining scss files (#7379) 2025-09-25 11:27:10 +02:00
Belén Albeza a5e4de97e3 💄 Use deprecated prefix for deprecated scss vars and mixins (#7375) 2025-09-25 09:22:25 +02:00
Alonso Torres 378be9473d 🐛 Fix problem with export size (#7374) 2025-09-25 08:50:31 +02:00
Juan de la Cruz 412cf61d7d 🐛 Remove translations form inspect tab text properties (#7369) 2025-09-25 08:48:41 +02:00
Juan de la Cruz 754a1b6fa2 🐛 Fix loading tips wording (#7368) 2025-09-25 08:48:10 +02:00
Eva Marco a4ada6dc8a 🐛 Add default flags for tokens (#7367) 2025-09-25 08:47:04 +02:00
Elenzakaleidos ec94d08f4a 🎉 Update README.md with Variants (#7353)
Update the Readme with new text and image that include Variants as feature

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2025-09-25 08:46:02 +02:00
Alonso Torres b6b2d28464 🐛 Fix problem with flow not being deleted (#7371) 2025-09-24 18:06:26 +02:00
Elena Torro 32770c685a 🐛 Do not add shadows on hidden children 2025-09-24 14:42:57 +02:00
andrés gonzález b770145436 💄 Update variants video at the user guide (#7363) 2025-09-24 13:41:20 +02:00
Eva Marco 441dc33e38 ♻️ Add shortcut to scss import paths (#7364)
* 🎉 Add config for shortcut imports

* ♻️ Change import paths
2025-09-24 11:18:34 +02:00
Eva MarcoandAndrey Antukh 3f87e768a7 ♻️ Fix color token reviews (#7322)
* ♻️ Fix some review changes

* 🐛 Fix more errors

* 🎉 Create token from colorpicker fixed

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-09-24 11:13:52 +02:00
David Barragán Merino 09e9340ba6 💄 Fix a description and remove an unused event 2025-09-24 09:38:42 +02:00
David Barragán Merino d5ff7b4144 📎 Add DevEnv builder workflow 2025-09-23 23:26:11 +02:00
David Barragán Merino ef0aee0a09 📎 Automatically publish github release and docker images with final version tags 2025-09-23 23:25:52 +02:00
Andrey Antukh 1e9682376e Merge remote-tracking branch 'origin/staging' into develop 2025-09-23 12:20:49 +02:00
Andrey Antukh 11b75408fe 🐛 Fix regression on importing binfile-v1 files (#7359) 2025-09-23 11:38:33 +02:00
Andrey Antukh 59f7ede4ff 🐛 Add migration for properly decode all position data on text shapes 2025-09-23 11:34:24 +02:00
Pablo Alba c9b61745a0 🎉 Switch several variant copies at the same time 2025-09-23 11:31:57 +02:00
Andrés Moya 8954b05d76 🐛 Fix error exporting a file with deleted tokens (#7356) 2025-09-22 17:41:31 +02:00
Aitor Moreno 974b76d7bd Merge pull request #7267 from penpot/azazeln28-feat-text-layout
🎉 Add internal TextContent layout data
2025-09-22 16:21:06 +02:00
Aitor Moreno f505fcfa0d 🎉 Add internal TextContent layout data 2025-09-22 16:01:23 +02:00
Belén Albeza e4d610d503 ♻️ Decouple shapes serialization from model (rust) (#7328)
* ♻️ Move shape type serialization to wasm module

* ♻️ Refactor serialization of constraints and vertical alignment into wasm module

* ♻️ Refactor serialization and model of shape blur

* ♻️ Refactor bool serialization to the wasm module

* ♻️ Split wasm::layout into submodules

* ♻️ Refactor serialization of AlignItems, AlignContent, JustifyItems and JustifyContent

* ♻️ Refactor serialization of WrapType and FlexDirection

* ♻️ Refactor serialization of JustifySelf

* ♻️ Refactor serialization of GridCell

* ♻️ Refactor serialization of AlignSelf

* 🐛 Fix AlignSelf not being serialized

* ♻️ Refactor handling of None variants in Raw* enums

* ♻️ Refactor serialization of grid direction

* ♻️ Refactor serialization of GridTrack and GridTrackType

* ♻️ Refactor serialization of Sizing

* ♻️ Refactor serialization of ShadowStyle

* ♻️ Refactor serialization of StrokeCap and StrokeStyle

* ♻️ Refactor serialization of BlendMode

* ♻️ Refactor serialization of FontStyle

* ♻️ Refactor serialization of GrowType
2025-09-22 13:47:54 +02:00
Andrey Antukh cb4c155b32 📎 Uncomment previously commented migrations 2025-09-22 11:38:52 +02:00
Andrey Antukh 0b346e02ff 🐛 Fix incorrect options pass on decode-file 2025-09-22 11:30:42 +02:00
Madalena Melo 5c23a678cc Merge pull request #7342 from penpot/madalenapmelo-kp-patch-1
📚 Add reference to the Teams section on the Dashboard section
2025-09-22 11:23:29 +02:00
David Barragán Merino fb3923924b 📎 Change the name of some action workflows 2025-09-22 09:58:26 +02:00
Florian Schroedl c882e8347a Add line-height to composite typography token 2025-09-22 09:52:56 +02:00
Pablo AlbaandLuis de Dios c1fd1a3b42 📚 Add variants doc for SDK (#7351)
* 📚 Add variants doc for SDK

* 📚 Spelling & style improvements

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2025-09-21 22:15:27 +02:00
Eva Marco b1fe32baea ♻️ Remove deprecated @import from scss files (#7347)
* 🐛 Fix import warnings 1 of 2

* 🐛 Fix import warnings 2 of 2

* 🐛 Fix visual tests and format files

* 🐛 Fix mixed declarations on scss
2025-09-19 11:50:08 +02:00
Andrey Antukh fb7a7d02da Merge pull request #7205 from penpot/niwinz-measures-tokens-backup
♻️ Replace numeric inputs on measure options
2025-09-19 11:44:17 +02:00
Eva Marco 20dfc2a216 🐛 Fix typo on event name (#7350) 2025-09-19 11:40:53 +02:00
Eva Marco d7d2d36e0a ♻️ Replace measure inputs for numeric input component 2025-09-19 11:28:22 +02:00
Andrey Antukh 07904bcc5d ♻️ Add needed changes to get tokens from sidebar
This reverts commit afe149f702148d86d1dea6cb6a537917ce7202aa.
2025-09-19 10:26:29 +02:00
Andrey Antukh 9686075104 🐛 Fix translations 2025-09-18 12:02:45 +02:00
María Valderrama 436e0e847d 🐛 Fix current version on sidebar 2025-09-18 11:56:47 +02:00
Eva Marco d50b070a64 🎉 Add usefull mixins to DS (#7340) 2025-09-18 10:47:55 +02:00
Andrey Antukh 80cb48fd6a Merge remote-tracking branch 'origin/staging' into develop 2025-09-18 10:44:21 +02:00
Madalena Melo 49c6efbc22 📚 Add reference to the Teams section on the Dashboard section
https://tree.taiga.io/project/penpot/task/11806

Signed-off-by: Madalena Melo <madalena.melo@kaleidos.net>
2025-09-17 16:17:24 +02:00
Andrey Antukh 4fb1c7a630 Merge remote-tracking branch 'origin/staging' into develop 2025-09-17 13:46:49 +02:00
David Barragán Merino fd37fdde93 📎 Add release action workflow 2025-09-16 18:06:06 +02:00
David Barragán Merino 66b1d5b7bd Merge remote-tracking branch 'origin/staging' into develop 2025-09-16 16:26:23 +02:00
Xavier Julian 2bf7a9dd5f ♻️ Remove unneeded fn parameters 2025-09-16 14:17:14 +02:00
Xavier Julian 7bacd8fbca ♻️ Refactor defmulti fn into case switches 2025-09-16 14:17:14 +02:00
Aitor Moreno b883882a32 🐛 Fix onboarding select keyboard interaction (#7295) 2025-09-16 13:59:15 +02:00
Belén Albeza e5e11b6383 🔧 Autogenerate serialization values for wasm enums (#7296)
* 🔧 Autogenerate serialization values for wasm enums

* 🔧 Add serializer values to the wasm api

*  Avoid converting to a clojure map the serializer js object

* 🔧 Update watch script for autoserialized enums

* 🐛 Fix missing serializer values
2025-09-16 12:29:14 +02:00
Eva Marco 01e963ae35 🐛 Fix font name hot update (#7316) 2025-09-16 12:23:41 +02:00
Eva Marco 90a80c4b63 🐛 Fix Uppercase on add token button (#7314) 2025-09-16 12:05:55 +02:00
VKing9 1bd45d3f8a 🌐 Add translations for: Hindi
Currently translated at 95.2% (1825 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-09-16 12:02:02 +02:00
Andrey Antukh b56f237780 Merge remote-tracking branch 'origin/staging' into develop 2025-09-16 11:38:58 +02:00
Xavier Julian 4970ae3eb4 💄 Align tokens panel vertically to the top 2025-09-16 11:38:33 +02:00
Elena Torró 2e21f084fc 🐛 Fix boolean operations on rotated shapes (#7309) 2025-09-15 14:46:56 +02:00
Xavier Julian 55513b9ae5 🎉 Inspect styles tab: layout element panel 2025-09-15 13:39:00 +02:00
Eva Marco 07d0062645 🐛 Fix sets shown without color tokens (#7312) 2025-09-15 10:38:06 +02:00
Xavier Julian f4b38af649 Display border-radius as logical properties in inspect tab 2025-09-15 09:46:01 +02:00
Andrey Antukh 6e7bcd1243 Merge remote-tracking branch 'origin/staging' into develop 2025-09-12 16:55:25 +02:00
Andrés Moya ed3fc5b8b2 🐛 Fix detaching a nested copy inside a main component (#7304)
* 🐛 Fix detaching a nested copy inside a main component

* 💄 Rename functions for more semantic precission
2025-09-12 16:00:01 +02:00
Pablo Alba f5f9157786 🐛 Fix paste behavior according to the selected element 2025-09-12 15:17:26 +02:00
Andrey Antukh 6cb0cb7f98 Merge remote-tracking branch 'origin/staging' into develop 2025-09-12 14:49:52 +02:00
Xavier Julian 0210b310b7 🎉 Inspect styles tab: layout panel 2025-09-12 10:27:41 +02:00
Stas Haas c77efc657c 🌐 Add translations for: German
Currently translated at 91.7% (1758 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-09-12 10:03:18 +02:00
Eva Marco ce1e44eda4 ♻️ Refactor set titles (#7301) 2025-09-12 08:46:05 +02:00
Marina López 48825e1e59 Show current penpot version 2025-09-11 13:18:42 +02:00
Florian Schroedl 61cfe2d142 🐛 Fix font-family being split up when restoring from backup value 2025-09-11 12:33:26 +02:00
Eva Marco 2d68f4dfd3 🐛 Fix icons (#7299) 2025-09-11 09:42:11 +02:00
Elena Torró 1e23937aa5 Merge pull request #7291 from penpot/superalex-fix-boolean-and-group-shadows
🐛 Fix boolean and group shadows
2025-09-11 09:27:56 +02:00
Eva Marco aecaf51953 Add color token on colorpicker (#7197)
*  Add token aplication to colorpicker

* 🐛 Change fn name

* 🐛 Change scss from file

* 🐛 Change color for direct-color

* 🐛 Remove vector from fns

* 🐛 Fix CI

* 🐛 Change color-option name

* 🐛 Fix comments

* 🐛 Remove sets without color tokens
2025-09-11 09:13:43 +02:00
Alejandro Alonso da05d6b67d 🐛 Fix boolean and group shadows 2025-09-10 15:59:39 +02:00
Alejandro Alonso 99a100ad63 Merge pull request #7264 from penpot/elenatorro-12002-draw-shadows-and-blurs-on-texts-on-surfaces
🐛 Fix text shadows and blur and refactor text rendering
2025-09-10 15:50:33 +02:00
Elena Torró bd3bcb4b18 Merge pull request #7284 from penpot/superalex-fix-blend-mode
🐛 Fix updating blend mode for shapes
2025-09-10 15:03:17 +02:00
Elena Torró 534c7864fc Merge pull request #7285 from penpot/superalex-fix-cornder-radius
🐛 Fix corner radius
2025-09-10 14:59:06 +02:00
Elena Torro 4bd2eba573 🐛 Fix text shadows and blur and refactor text rendering 2025-09-10 14:20:24 +02:00
Xavier Julian 563f608255 🐛 Display token themes as a string 2025-09-10 13:55:54 +02:00
Alejandro Alonso 382b5e7e3a Merge remote-tracking branch 'origin/staging' into develop 2025-09-10 12:33:54 +02:00
Eva Marco a503f8ae93 ♻️ Refactor composite token UI (#7287)
* ♻️ Refactor composite token UI

* 🐛 Fix comments
2025-09-10 12:16:39 +02:00
Xavier Julian e1935fb3fb 🎉 Inspect styles tab: geometry panel 2025-09-10 11:01:19 +02:00
Florian Schroedl b3763dec3f Typography import-export 2025-09-09 13:30:38 +02:00
Alejandro Alonso 41751d60d2 🐛 Fix corner radius 2025-09-09 10:24:56 +02:00
Yamila Moreno 8bd0edca46 Merge pull request #7282 from penpot/yms-update-ci
📎 Update CI
2025-09-09 09:30:18 +02:00
Alejandro Alonso e2f22b86c7 🐛 Fix updating blend mode for shapes 2025-09-09 09:19:09 +02:00
Alejandro Alonso 108b5ab225 🐛 Fix missing filter-icon 2025-09-09 09:05:42 +02:00
Alejandro Alonso 43a238a896 Merge remote-tracking branch 'origin/staging' into develop 2025-09-09 08:40:35 +02:00
Yamila Moreno daa408e291 📎 Update CI 2025-09-08 16:51:05 +02:00
Florian Schrödl 8aed47dad3 Allow references to other typography tokens (#7251) 2025-09-08 16:45:18 +02:00
Elena Torró 0e23c9f6ab Merge pull request #7278 from penpot/superalex-fix-fill-stroke-opacity-shouldnt-affect-shadows
🐛 Fix fills and strokes opacity shouldn't affect shadows
2025-09-08 14:08:20 +02:00
Alejandro Alonso 8fff9afee6 🐛 Fix fills and strokes opacity shouldn't affect shadows 2025-09-08 13:04:52 +02:00
Xavier Julian ff55318c04 🎉 Inspect styles tab: variants panel 2025-09-08 11:59:33 +02:00
Elena Torró 41b7957eff Merge pull request #7274 from penpot/superalex-refactor-drop-shadows
🐛 Fixing nested shadows
2025-09-08 11:38:19 +02:00
Alejandro Alonso 7e52aadb98 🐛 Fixing nested shadows 2025-09-08 11:20:03 +02:00
Alejandro Alonso 69f41c300f Merge pull request #7199 from penpot/elenatorro-11844-fix-font-long-names
🐛 Fix custom font-long names overflow
2025-09-08 10:48:54 +02:00
Elena Torro 18c5e0b9a8 🐛 Fix font long name overflow 2025-09-08 10:31:35 +02:00
Nicola Bortoletto 26f123f466 🌐 Add translations for: Italian
Currently translated at 99.7% (1912 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-06 09:02:02 +02:00
DoubleCat d9f186524d 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 95.6% (1834 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-09-06 09:02:00 +02:00
Nicola Bortoletto 5f33ce9ef6 🌐 Add translations for: Italian
Currently translated at 98.2% (1883 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-04 19:01:57 +02:00
Florian Schroedl 5230d54551 🐛 Fix when font-weight is a computed int (math resolver) 2025-09-04 12:23:43 +02:00
Alejandro Alonso a79be05261 🐛 Fix selection and devtools problem (#7259) 2025-09-04 09:29:38 +02:00
Belén Albeza 9c77296858 🔧 Make the watch script to compile the debug css when not in production env (#7250) 2025-09-03 13:45:11 +02:00
Xavier Julian 34da6b64df 🎉 Inspect styles tab tokens panel 2025-09-03 13:01:38 +02:00
Corentin Noël 4becd35e52 🌐 Add translations for: French
Currently translated at 97.6% (1871 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-09-03 09:02:01 +00:00
Florian Schroedl c4481be39f ♻️ Revert trigger interactive via actionize and propagation 2025-09-03 09:42:40 +02:00
Elena Torró f60b6a4869 Merge pull request #7247 from penpot/ladybenko-11983-textlayout-module
♻️ Refactor into new textlayout module
2025-09-02 17:17:12 +02:00
Belén Albeza 3e02dc550f ♻️ Create type alias for ParagraphBuilderGroup 2025-09-02 15:32:10 +02:00
Belén Albeza 1cf0de395c ♻️ Rename get_children to children (Paragraph) 2025-09-02 15:30:54 +02:00
Belén Albeza d40b68c004 ♻️ Refactor and rename ParagraphBuilder instantiating from TextContent 2025-09-02 15:22:05 +02:00
Belén Albeza 50b9e8c6e6 ♻️ Rename TextContent::get_width to TextContent::width 2025-09-02 15:07:13 +02:00
Belén Albeza d25f9cd4bd ♻️ Move auto_width and auto_height to their own textlayout module 2025-09-02 15:03:46 +02:00
Florian Schroedl bedb98ad9f Add context menu for typography 2025-09-02 13:19:45 +02:00
Elena Torró 5f37601122 🐛 Fix different fonts on texts shadows (#7214)
* 🐛 Fix different fonts on texts shadows

* 🔧 Refactor text rendering and move text-decoration logic outside

* 🔧 Use transparency correctly
2025-09-02 12:56:07 +02:00
Pablo Alba 796aaed11e 🐛 Fix prop creation on variants move layer 2025-09-02 10:01:30 +02:00
Alejandro Alonso 1da69cfa38 📎 Add next release entries to the changelog 2025-09-01 11:10:09 +02:00
Nicola Bortoletto b0712b6dc5 🌐 Add translations for: Italian
Currently translated at 96.1% (1844 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-01 11:02:03 +02:00
Yaron Shahrabani cc31ee50df 🌐 Add translations for: Hebrew
Currently translated at 99.5% (1908 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-09-01 11:02:01 +02:00
Stephan Paternotte 63456d2b75 🌐 Add translations for: Dutch
Currently translated at 99.8% (1915 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-31 06:05:48 +00:00
Stephan Paternotte 6a4a22c77a 🌐 Add translations for: Dutch
Currently translated at 99.8% (1915 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-29 17:01:57 +02:00
Edgars Andersons 32ad35aa19 🌐 Add translations for: Latvian
Currently translated at 97.3% (1867 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-08-29 17:01:56 +02:00
Yaron Shahrabani e1522f1e8a 🌐 Add translations for: Hebrew
Currently translated at 99.1% (1900 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-08-29 17:01:54 +02:00
Corentin Noël 05093a32f3 🌐 Add translations for: French
Currently translated at 97.4% (1868 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-08-29 17:01:53 +02:00
2886 changed files with 413894 additions and 128007 deletions

No files matched your search

-342
View File
@@ -1,342 +0,0 @@
version: 2.1
jobs:
lint:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
steps:
- checkout
- run:
name: "fmt check"
working_directory: "."
command: |
yarn install
yarn run fmt:clj:check
- run:
name: "lint clj common"
working_directory: "."
command: |
yarn run lint:clj:common
- run:
name: "lint clj frontend"
working_directory: "."
command: |
yarn run lint:clj:frontend
- run:
name: "lint clj backend"
working_directory: "."
command: |
yarn run lint:clj:backend
- run:
name: "lint clj exporter"
working_directory: "."
command: |
yarn run lint:clj:exporter
- run:
name: "lint clj library"
working_directory: "."
command: |
yarn run lint:clj:library
test-common:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
- run:
name: "JVM tests"
working_directory: "./common"
command: |
clojure -M:dev:test
- run:
name: "NODE tests"
working_directory: "./common"
command: |
yarn install
yarn run test
- save_cache:
paths:
- ~/.m2
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
test-frontend:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "install dependencies"
working_directory: "./frontend"
# We install playwright here because the dependent tasks
# uses the same cache as this task so we prepopulate it
command: |
yarn install
yarn run playwright install chromium
- run:
name: "lint scss on frontend"
working_directory: "./frontend"
command: |
yarn run lint:scss
- run:
name: "unit tests"
working_directory: "./frontend"
command: |
yarn run test
- save_cache:
paths:
- ~/.m2
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
test-library:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies and build
working_directory: "./library"
command: |
yarn install
- run:
name: Build and Test
working_directory: "./library"
command: |
./scripts/build
yarn run test
test-components:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g -Xms2g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies
working_directory: "./frontend"
command: |
yarn install
yarn run playwright install chromium
- run:
name: Build Storybook
working_directory: "./frontend"
command: yarn run build:storybook
- run:
name: Serve Storybook and run tests
working_directory: "./frontend"
command: |
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:6006 && yarn test:storybook"
test-integration:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: large
environment:
JAVA_OPTS: -Xmx6g -Xms2g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "integration tests"
working_directory: "./frontend"
command: |
yarn install
yarn run build:app:assets
yarn run build:app
yarn run build:app:libs
yarn run playwright install chromium
yarn run test:e2e -x --workers=4
test-backend:
docker:
- image: penpotapp/devenv:latest
- image: cimg/postgres:14.5
environment:
POSTGRES_USER: penpot_test
POSTGRES_PASSWORD: penpot_test
POSTGRES_DB: penpot_test
- image: cimg/redis:7.0.5
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "backend/deps.edn" }}
- run:
name: "tests"
working_directory: "./backend"
command: |
clojure -M:dev:test --reporter kaocha.report/documentation
environment:
PENPOT_TEST_DATABASE_URI: "postgresql://localhost/penpot_test"
PENPOT_TEST_DATABASE_USERNAME: penpot_test
PENPOT_TEST_DATABASE_PASSWORD: penpot_test
PENPOT_TEST_REDIS_URI: "redis://localhost/1"
- save_cache:
paths:
- ~/.m2
- ~/.gitlibs
key: v1-dependencies-{{ checksum "backend/deps.edn" }}
test-render-wasm:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
steps:
- checkout
- run:
name: "fmt check"
working_directory: "./render-wasm"
command: |
cargo fmt --check
- run:
name: "lint"
working_directory: "./render-wasm"
command: |
./lint
- run:
name: "cargo tests"
working_directory: "./render-wasm"
command: |
./test
workflows:
penpot:
jobs:
- lint
- test-frontend:
requires:
- lint: success
- test-library:
requires:
- test-frontend: success
- lint: success
- test-components:
requires:
- test-frontend: success
- lint: success
- test-integration:
requires:
- test-frontend: success
- lint: success
- test-backend:
requires:
- lint: success
- test-common:
requires:
- lint: success
- test-render-wasm
+9
View File
@@ -45,6 +45,15 @@
:potok/reify-type
{:level :error}
:redundant-primitive-coercion
{:level :off}
:unused-excluded-var
{:level :off}
:unresolved-excluded-var
{:level :off}
:missing-protocol-method
{:level :off}
+5
View File
@@ -2,6 +2,11 @@
:remove-multiple-non-indenting-spaces? false
:remove-surrounding-whitespace? true
:remove-consecutive-blank-lines? false
:indent-line-comments? true
:parallel? true
:align-form-columns? false
;; :align-map-columns? false
;; :align-single-column-lines? false
:extra-indents {rumext.v2/fnc [[:inner 0]]
cljs.test/async [[:inner 0]]
promesa.exec/thread [[:inner 0]]
+3 -2
View File
@@ -1,7 +1,8 @@
description: Create a report to help us improve
labels: ["bug"]
name: Bug report
title: "bug: "
type: Bug
labels: ["needs triage"]
body:
- type: markdown
attributes:
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
+3 -2
View File
@@ -1,7 +1,8 @@
description: Suggest an idea for this project.
labels: ["needs triage", "enhancement"]
labels: ["needs triage"]
name: "Feature request"
title: "feature: "
type: Enhancement
body:
- type: markdown
attributes:
+1 -1
View File
@@ -13,7 +13,7 @@
- [ ] Add a detailed explanation of how to reproduce the issue and/or verify the fix, if applicable.
- [ ] Include screenshots or videos, if applicable.
- [ ] Add or modify existing integration tests in case of bugs or new features, if applicable.
- [ ] Refactor any modified SCSS files following the refactor guide.
- [ ] Check CI passes successfully.
- [ ] Update the `CHANGES.md` file, referencing the related GitHub issue, if applicable.
<!-- For more details, check the contribution guidelines: https://github.com/penpot/penpot/blob/develop/CONTRIBUTING.md -->
+12 -16
View File
@@ -1,11 +1,11 @@
name: Build and Upload Penpot Bundle
name: Bundles Builder
on:
# Create bundle from manual action
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
@@ -22,7 +22,7 @@ on:
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch'
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
@@ -40,7 +40,7 @@ on:
jobs:
build-bundle:
name: Build and Upload Penpot Bundle
runs-on: ubuntu-24.04
runs-on: penpot-runner-01
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -48,7 +48,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
@@ -56,10 +56,10 @@ jobs:
- name: Extract some useful variables
id: vars
run: |
echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
- name: Run manage.sh build-bundle from host
- name: Build bundle
env:
BUILD_WASM: ${{ inputs.build_wasm }}
BUILD_STORYBOOK: ${{ inputs.build_storybook }}
@@ -76,22 +76,18 @@ jobs:
zip -r zips/penpot.zip penpot
- name: Upload Penpot bundle to S3
if: github.ref_type == 'branch'
run: |
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}-latest.zip
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.commit_hash }}.zip
- name: Upload Penpot bundle to S3
if: github.ref_type == 'tag'
run: |
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}.zip
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}.zip --metadata bundle-version=${{ steps.vars.outputs.bundle_version }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ *[PENPOT] Error during the execution of the job*
📦 *[PENPOT] Error building penpot bundles.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
Bundle version: `${{ steps.vars.outputs.bundle_version }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+10 -2
View File
@@ -1,14 +1,22 @@
name: DEVELOP - Build and Upload Penpot Bundle
name: _DEVELOP
on:
workflow_dispatch:
schedule:
- cron: '16 5-20 * * 1-5'
jobs:
build-develop-bundle:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "develop"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "develop"
+50
View File
@@ -0,0 +1,50 @@
name: DevEnv Docker Image Builder
on:
workflow_dispatch:
jobs:
build-and-push:
name: Build and push DevEnv Docker image
runs-on: penpot-runner-02
steps:
- name: Set common environment variables
run: |
# Each job execution will use its own docker configuration.
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Docker Registry
uses: docker/login-action@v4
with:
username: ${{ secrets.PUB_DOCKER_USERNAME }}
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
- name: Build and push DevEnv Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'penpotapp/devenv'
with:
context: ./docker/devenv/
file: ./docker/devenv/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.DOCKER_IMAGE }}:latest
cache-from: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Notify Mattermost
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
🚀 *[PENPOT] New devenv available*
📄 You may want to update your devenv.
@alvaro
+183
View File
@@ -0,0 +1,183 @@
name: Docker Images Builder
on:
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
jobs:
build-and-push:
name: Build and Push Penpot Docker Images
runs-on: penpot-runner-02
steps:
- name: Set common environment variables
run: |
# Each job execution will use its own docker configuration.
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Download Penpot Bundles
id: bundles
env:
FILE_NAME: penpot-${{ steps.vars.outputs.gh_ref }}.zip
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
tmp=$(aws s3api head-object \
--bucket ${{ secrets.S3_BUCKET }} \
--key "$FILE_NAME" \
--query 'Metadata."bundle-version"' \
--output text)
echo "bundle_version=$tmp" >> $GITHUB_OUTPUT
pushd docker/images
aws s3 cp s3://${{ secrets.S3_BUCKET }}/$FILE_NAME .
unzip $FILE_NAME > /dev/null
mv penpot/backend bundle-backend
mv penpot/frontend bundle-frontend
mv penpot/exporter bundle-exporter
mv penpot/storybook bundle-storybook
mv penpot/mcp bundle-mcp
popd
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Docker Registry
uses: docker/login-action@v4
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# To avoid the “429 Too Many Requests” error when downloading
# images from DockerHub for unregistered users.
# https://docs.docker.com/docker-hub/usage/
- name: Login to DockerHub Registry
uses: docker/login-action@v4
with:
username: ${{ secrets.PUB_DOCKER_USERNAME }}
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v6
with:
images:
frontend
backend
exporter
storybook
mcp
labels: |
bundle_version=${{ steps.bundles.outputs.bundle_version }}
- name: Build and push Backend Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'backend'
BUNDLE_PATH: './bundle-backend'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.backend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Frontend Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'frontend'
BUNDLE_PATH: './bundle-frontend'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.frontend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Exporter Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'exporter'
BUNDLE_PATH: './bundle-exporter'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.exporter
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Storybook Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'storybook'
BUNDLE_PATH: './bundle-storybook'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.storybook
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push MCP Docker image
uses: docker/build-push-action@v7
env:
DOCKER_IMAGE: 'mcp'
BUNDLE_PATH: './bundle-mcp'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.mcp
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🐳 *[PENPOT] Error building penpot docker images.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
📦 Bundle: `${{ steps.bundles.outputs.bundle_version }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+22
View File
@@ -0,0 +1,22 @@
name: _MAIN-STAGING
on:
workflow_dispatch:
schedule:
- cron: '26 5-20 * * 1-5'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "main-staging"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "main-staging"
+10 -2
View File
@@ -1,14 +1,22 @@
name: STAGING - Build and Upload Penpot Bundle
name: _STAGING
on:
workflow_dispatch:
schedule:
- cron: '36 5-20 * * 1-5'
jobs:
build-staging-bundle:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "staging"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "staging"
+35 -3
View File
@@ -1,15 +1,47 @@
name: TAG - Build and Upload Penpot Bundle
name: _TAG
on:
workflow_dispatch:
push:
tags:
- '*'
jobs:
build-tag-bundle:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
build_wasm: "no"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
notify:
name: Notifications
runs-on: ubuntu-24.04
needs: build-docker
steps:
- name: Notify Mattermost
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
🐳 *[PENPOT] Docker image available: ${{ github.ref_name }}*
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
publish-final-tag:
if: ${{ !contains(github.ref_name, '-RC') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && contains(github.ref_name, '.') }}
needs: build-docker
uses: ./.github/workflows/release.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
+4 -1
View File
@@ -6,12 +6,14 @@ on:
- edited
- reopened
- synchronize
- ready_for_review
pull_request_target:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
push:
branches:
- main
@@ -20,13 +22,14 @@ on:
jobs:
check-commit-message:
if: ${{ !github.event.pull_request.draft }}
name: Check Commit Message
runs-on: ubuntu-latest
steps:
- name: Check Commit Type
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(Merge|Revert|:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle):)\s[A-Z].*[^.]$'
pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert|Reapply).+[^.])$'
flags: 'gm'
error: 'Commit should match CONTRIBUTING.md guideline'
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
@@ -0,0 +1,142 @@
name: Plugins/api-doc deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/libs/plugin-types/index.d.ts'
- 'plugins/libs/plugin-types/REAME.md'
- 'plugins/tools/typedoc.css'
- 'plugins/CHANGELOG.md'
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: Build docs
working-directory: plugins
shell: bash
run: pnpm run build:doc
- name: Select Worker name
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
- name: Add noindex header and robots.txt files for non-production environments
if: ${{ steps.vars.outputs.gh_ref != 'main' }}
working-directory: plugins
shell: bash
run: |
ASSETS_DIR="dist/doc"
cat > "${ASSETS_DIR}/_headers" << 'EOF'
/*
X-Robots-Tag: noindex, nofollow
EOF
cat > "${ASSETS_DIR}/robots.txt" << 'EOF'
User-agent: *
Disallow: /
EOF
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangler-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩📚 *[PENPOT PLUGINS] Error deploying API documentation.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
@@ -0,0 +1,127 @@
name: Plugins/package deployer
on:
# Deploy package from manual action
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
plugin_name:
description: 'Pluging name (like plugins/apps/<plugin_name>-plugin)'
type: string
required: true
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch'
type: string
required: true
default: 'develop'
plugin_name:
description: 'Publig name (from plugins/apps/<plugin_name>-plugin)'
type: string
required: true
permissions:
contents: read
jobs:
deploy:
runs-on: penpot-runner-01
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
working-directory: plugins
shell: bash
run: pnpm --filter ${{ inputs.plugin_name }}-plugin build
- name: Select Worker name
run: |
REF="${{ inputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config apps/${{ inputs.plugin_name }}-plugin/wrangler.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩📦 *[PENPOT PLUGINS] Error deploying ${{ env.WORKER_NAME }}.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
Plugin name: `${{ inputs.plugin_name }}-plugin`
Cloudflare worker name: `${{ env.WORKER_NAME }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
@@ -0,0 +1,143 @@
name: Plugins/packages deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/apps/*-plugin/**'
- 'libs/plugins-styles/**'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
colors_to_tokens: ${{ steps.filter.outputs.colors_to_tokens }}
create_palette: ${{ steps.filter.outputs.create_palette }}
lorem_ipsum: ${{ steps.filter.outputs.lorem_ipsum }}
rename_layers: ${{ steps.filter.outputs.rename_layers }}
contrast: ${{ steps.filter.outputs.contrast }}
icons: ${{ steps.filter.outputs.icons }}
poc_state: ${{ steps.filter.outputs.poc_state }}
table: ${{ steps.filter.outputs.table }}
# [For new plugins]
# Add more outputs here
steps:
- uses: actions/checkout@v6
- id: filter
uses: dorny/paths-filter@v4
with:
filters: |
colors_to_tokens:
- 'plugins/apps/colors-to-tokens-plugin/**'
- 'libs/plugins-styles/**'
contrast:
- 'plugins/apps/contrast-plugin/**'
- 'libs/plugins-styles/**'
create_palette:
- 'plugins/apps/create-palette-plugin/**'
- 'libs/plugins-styles/**'
icons:
- 'plugins/apps/icons-plugin/**'
- 'libs/plugins-styles/**'
lorem_ipsum:
- 'plugins/apps/lorem-ipsum-plugin/**'
- 'libs/plugins-styles/**'
rename_layers:
- 'plugins/apps/rename-layers-plugin/**'
- 'libs/plugins-styles/**'
table:
- 'plugins/apps/table-plugin/**'
- 'libs/plugins-styles/**'
# [For new plugins]
# Add more plugin filters here
# another_plugin:
# - 'plugins/apps/another-plugin/**'
# - 'libs/plugins-styles/**'
colors-to-tokens-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.colors_to_tokens == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: colors-to-tokens
contrast-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.contrast == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: contrast
create-palette-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.create_palette == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: create-palette
icons-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.icons == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: icons
lorem-ipsum-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.lorem_ipsum == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: lorem-ipsum
rename-layers-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.rename_layers == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: rename-layers
table-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.table == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: table
# [For new plugins]
# Add more jobs for other plugins below, following the same pattern
# another-plugin:
# needs: detect-changes
# if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.another_plugin == 'true'
# uses: ./.github/workflows/plugins-deploy-package.yml
# secrets: inherit
# with:
# gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
# plugin_name: another
@@ -0,0 +1,140 @@
name: Plugins/styles-doc deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/apps/example-styles/**'
- 'plugins/libs/plugins-styles/**'
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: Build styles
working-directory: plugins
shell: bash
run: pnpm run build:styles-example
- name: Select Worker name
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
- name: Add noindex header and robots.txt files for non-production environments
if: ${{ steps.vars.outputs.gh_ref != 'main' }}
working-directory: plugins
shell: bash
run: |
ASSETS_DIR="dist/apps/example-styles"
cat > "${ASSETS_DIR}/_headers" << 'EOF'
/*
X-Robots-Tag: noindex, nofollow
EOF
cat > "${ASSETS_DIR}/robots.txt" << 'EOF'
User-agent: *
Disallow: /
EOF
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+114
View File
@@ -0,0 +1,114 @@
name: Release Publisher
on:
workflow_dispatch:
inputs:
gh_ref:
description: 'Tag to release'
type: string
required: true
workflow_call:
inputs:
gh_ref:
description: 'Tag to release'
type: string
required: true
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.vars.outputs.gh_ref }}
release_notes: ${{ steps.extract_release_notes.outputs.release_notes }}
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# --- Publicly release the docker images ---
- name: Configure ECR credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.DOCKER_USERNAME }}
aws-secret-access-key: ${{ secrets.DOCKER_PASSWORD }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Install Skopeo
run: |
sudo apt-get update -y
sudo apt-get install -y skopeo
- name: Copy images from AWS ECR to Docker Hub
env:
AWS_REGION: ${{ secrets.AWS_REGION }}
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
PUB_DOCKER_USERNAME: ${{ secrets.PUB_DOCKER_USERNAME }}
PUB_DOCKER_PASSWORD: ${{ secrets.PUB_DOCKER_PASSWORD }}
TAG: ${{ steps.vars.outputs.gh_ref }}
run: |
aws ecr get-login-password --region $AWS_REGION | \
skopeo login --username AWS --password-stdin \
$DOCKER_REGISTRY
echo "$PUB_DOCKER_PASSWORD" | skopeo login --username "$PUB_DOCKER_USERNAME" --password-stdin docker.io
IMAGES=("frontend" "backend" "exporter" "mcp" "storybook")
SHORT_TAG=${TAG%.*}
for image in "${IMAGES[@]}"; do
skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$TAG
for alias in main latest "$SHORT_TAG"; do
skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$alias
done
done
# --- Release notes extraction ---
- name: Extract release notes from CHANGES.md
id: extract_release_notes
env:
TAG: ${{ steps.vars.outputs.gh_ref }}
run: |
RELEASE_NOTES=$(awk "/^## $TAG$/{flag=1; next} /^## /{flag=0} flag" CHANGES.md | awk '{$1=$1};1')
if [ -z "$RELEASE_NOTES" ]; then
RELEASE_NOTES="No changes for $TAG according to CHANGES.md"
fi
echo "release_notes<<EOF" >> $GITHUB_OUTPUT
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# --- Create GitHub release ---
- name: Create GitHub release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.vars.outputs.gh_ref }}
name: ${{ steps.vars.outputs.gh_ref }}
body: ${{ steps.extract_release_notes.outputs.release_notes }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🚀 *[PENPOT] Error releasing penpot.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+84
View File
@@ -0,0 +1,84 @@
name: "CI: Backend"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'backend/**'
- 'common/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'backend/**'
- 'common/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-backend:
if: ${{ !github.event.pull_request.draft }}
name: "Backend Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
services:
postgres:
image: postgres:17
# Provide the password for postgres
env:
POSTGRES_USER: penpot_test
POSTGRES_PASSWORD: penpot_test
POSTGRES_DB: penpot_test
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: valkey/valkey:9
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Lint
working-directory: ./backend
run: |
corepack enable;
corepack install;
pnpm install;
pnpm run check-fmt
pnpm run lint
- name: Tests
working-directory: ./backend
env:
PENPOT_TEST_DATABASE_URI: "postgresql://postgres/penpot_test"
PENPOT_TEST_DATABASE_USERNAME: penpot_test
PENPOT_TEST_DATABASE_PASSWORD: penpot_test
PENPOT_TEST_REDIS_URI: "redis://redis/1"
run: |
mkdir -p /tmp/penpot;
clojure -M:dev:test --reporter kaocha.report/documentation
+57
View File
@@ -0,0 +1,57 @@
name: "CI: Common"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'common/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'common/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-common:
if: ${{ !github.event.pull_request.draft }}
name: "Common Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Lint
working-directory: ./common
run: |
corepack enable;
corepack install;
pnpm install;
pnpm run check-fmt:clj
pnpm run check-fmt:js
pnpm run lint:clj
- name: Tests
working-directory: ./common
run: |
./scripts/test
+71
View File
@@ -0,0 +1,71 @@
name: "CI: Frontend"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'frontend/**'
- 'common/**'
- 'render-wasm/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'frontend/**'
- 'common/**'
- 'render-wasm/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-frontend:
if: ${{ !github.event.pull_request.draft }}
name: "Frontend Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Lint
working-directory: ./frontend
run: |
corepack enable;
corepack install;
pnpm install;
pnpm run check-fmt:js
pnpm run check-fmt:clj
pnpm run check-fmt:scss
pnpm run lint:clj
pnpm run lint:js
pnpm run lint:scss
- name: Unit Tests
working-directory: ./frontend
run: |
./scripts/test
- name: Component Tests
working-directory: ./frontend
env:
VITEST_BROWSER_TIMEOUT: 120000
run: |
./scripts/test-components
+93
View File
@@ -0,0 +1,93 @@
name: "CI: Integration"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'frontend/**'
- 'common/**'
- 'render-wasm/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'frontend/**'
- 'common/**'
- 'render-wasm/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-integration:
if: ${{ !github.event.pull_request.draft }}
name: "Build Integration Bundle"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Build Bundle
working-directory: ./frontend
run: |
./scripts/build
- name: Store Bundle Cache
uses: actions/cache@v5
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
test-integration:
if: ${{ !github.event.pull_request.draft }}
name: "Integration Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
needs: build-integration
steps:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Restore Cache
uses: actions/cache/restore@v5
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
- name: Run Tests
working-directory: ./frontend
run: |
./scripts/test-e2e
- name: Upload test result
uses: actions/upload-artifact@v7
if: always()
with:
name: integration-tests-result
path: frontend/test-results/
overwrite: true
retention-days: 3
+58
View File
@@ -0,0 +1,58 @@
name: "CI: Library"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'common/**'
- 'library/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'common/**'
- 'library/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-library:
if: ${{ !github.event.pull_request.draft }}
name: "Library Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Lint
working-directory: ./library
run: |
corepack enable;
corepack install;
pnpm install;
pnpm run check-fmt
pnpm run lint
- name: Tests
working-directory: ./library
run: |
./scripts/test
+52
View File
@@ -0,0 +1,52 @@
name: "MCP CI"
on:
pull_request:
branches:
- develop
- staging
- main
types:
- opened
- synchronize
- ready_for_review
paths:
- 'mcp/**'
push:
branches:
- develop
- staging
- main
paths:
- 'mcp/**'
jobs:
test-mcp:
if: ${{ !github.event.pull_request.draft }}
name: "Test MCP"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup
working-directory: ./mcp
run: ./scripts/setup
- name: Check
working-directory: ./mcp
run: |
pnpm run fmt:check;
pnpm -r run build;
pnpm -r run types:check;
- name: Tests
working-directory: ./mcp
run: |
pnpm -r run test;
+83
View File
@@ -0,0 +1,83 @@
name: "CI: Plugins"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'plugins/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'plugins/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-plugins:
if: ${{ !github.event.pull_request.draft }}
name: Plugins Runtime Linter & Tests
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- uses: actions/checkout@v6
- name: Setup Node
id: setup-node
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
pnpm install;
- name: Run Lint
working-directory: ./plugins
run: pnpm run lint
- name: Run Format Check
working-directory: ./plugins
run: pnpm run format:check
- name: Run Test
working-directory: ./plugins
run: pnpm run test
- name: Build runtime
working-directory: ./plugins
run: pnpm run build:runtime
- name: Build doc
working-directory: ./plugins
run: pnpm run build:doc
- name: Build plugins
working-directory: ./plugins
run: pnpm run build:plugins
- name: Build styles
working-directory: ./plugins
run: pnpm run build:styles-example
+57
View File
@@ -0,0 +1,57 @@
name: "CI: WASM"
defaults:
run:
shell: bash
on:
pull_request:
paths:
- 'render-wasm/**'
types:
- opened
- synchronize
- ready_for_review
push:
branches:
- develop
- staging
paths:
- 'render-wasm/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test-render-wasm:
if: ${{ !github.event.pull_request.draft }}
name: "Render WASM Tests"
runs-on: penpot-runner-02
container:
image: penpotapp/devenv:latest
volumes:
- /var/cache/github-runner/m2:/root/.m2
- /var/cache/github-runner/gitlib:/root/.gitlibs
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Format
working-directory: ./render-wasm
run: |
cargo fmt --check
- name: Lint
working-directory: ./render-wasm
run: |
./lint
- name: Test
working-directory: ./render-wasm
run: |
./test
+23 -13
View File
@@ -1,10 +1,4 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
*-init.clj
*.css.json
*.jar
@@ -19,7 +13,6 @@
.nyc_output
.rebel_readline_history
.repl
.shadow-cljs
/*.jpg
/*.md
/*.png
@@ -31,8 +24,13 @@
/.clj-kondo/.cache
/_dump
/notes
/.opencode/package-lock.json
/plans
/prompts
/playground/
/backend/*.md
!/backend/AGENTS.md
/backend/.shadow-cljs
/backend/*.sql
/backend/*.txt
/backend/assets/
@@ -43,27 +41,34 @@
/backend/resources/public/media
/backend/target/
/backend/experiments
/backend/scripts/_env.local
/bundle*
/cd.md
/clj-profiler/
/common/coverage
/common/target
/deploy
/common/.shadow-cljs
/docker/images/bundle*
/exporter/target
/exporter/.shadow-cljs
/frontend/.storybook/preview-body.html
/frontend/.storybook/preview-head.html
/frontend/playwright-report/
/frontend/playwright/ui/visual-specs/
/frontend/text-editor/src/wasm/
/frontend/dist/
/frontend/npm-debug.log
/frontend/out/
/frontend/package-lock.json
/frontend/resources/fonts/experiments
/frontend/resources/public/*
/frontend/src/app/render_wasm/api/shared.js
/frontend/storybook-static/
/frontend/target/
/frontend/test-results/
/frontend/.shadow-cljs
/other/
/scripts/
/telemetry/
/nexus/
/tmp/
/vendor/**/target
/vendor/svgclean/bundle*.js
@@ -71,12 +76,17 @@
/library/target/
/library/*.zip
/external
clj-profiler/
node_modules
/penpot-nitrate
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/render-wasm/target/
/**/node_modules
/**/.yarn/*
/.pnpm-store
/.vscode
/.idea
/.claude
/.playwright-mcp
/tools/__pycache__
-105
View File
@@ -1,105 +0,0 @@
image:
file: docker/gitpod/Dockerfile
ports:
# nginx
- port: 3449
onOpen: open-preview
# frontend nREPL
- port: 3447
onOpen: ignore
visibility: private
# frontend shadow server
- port: 3448
onOpen: ignore
visibility: private
# backend
- port: 6060
onOpen: ignore
# exporter shadow server
- port: 9630
onOpen: ignore
visibility: private
# exporter http server
- port: 6061
onOpen: ignore
# mailhog web interface
- port: 8025
onOpen: ignore
# mailhog postfix
- port: 1025
onOpen: ignore
# postgres
- port: 5432
onOpen: ignore
# redis
- port: 6379
onOpen: ignore
# openldap
- port: 389
onOpen: ignore
tasks:
# https://github.com/gitpod-io/gitpod/issues/666#issuecomment-534347856
- name: gulp
command: >
cd $GITPOD_REPO_ROOT/frontend/;
yarn && gp sync-done 'frontend-yarn';
npx gulp --theme=${PENPOT_THEME} watch
- name: frontend shadow watch
command: >
cd $GITPOD_REPO_ROOT/frontend/;
gp sync-await 'frontend-yarn';
npx shadow-cljs watch main
- init: gp await-port 5432 && psql -f $GITPOD_REPO_ROOT/docker/gitpod/files/postgresql_init.sql
name: backend
command: >
cd $GITPOD_REPO_ROOT/backend/;
./scripts/start-dev
- name: exporter shadow watch
command:
cd $GITPOD_REPO_ROOT/exporter/;
gp sync-await 'frontend-yarn';
yarn && npx shadow-cljs watch main
- name: exporter web server
command: >
cd $GITPOD_REPO_ROOT/exporter/;
./scripts/wait-and-start.sh
- name: signed terminal
before: >
[[ ! -z ${GNUGPG} ]] &&
cd ~ &&
rm -rf .gnupg &&
echo ${GNUGPG} | base64 -d | tar --no-same-owner -xzvf -
init: >
[[ ! -z ${GNUGPG_KEY} ]] &&
git config --global commit.gpgsign true &&
git config --global user.signingkey ${GNUGPG_KEY}
command: cd $GITPOD_REPO_ROOT
- name: redis
command: redis-server
- before: go get github.com/mailhog/MailHog
name: mailhog
command: MailHog
- name: Nginx
command: >
nginx &&
multitail /var/log/nginx/access.log -I /var/log/nginx/error.log
+1 -1
View File
@@ -1 +1 @@
v22.19.0
v22.22.0
+28
View File
@@ -0,0 +1,28 @@
---
name: commiter
description: Git commit assistant
mode: all
---
## Role
You are responsible for creating git commits for Penpot and must
follow the repository commit-format rules exactly. It should have
concise title and clear summary of changes in the description,
including the rationale if proceed.
## Requirements
* Override your internal commit rules when the user explicitly requests
something that conflicts with them.
* Read `.serena/memories/workflows/creating-commits.md` before
creating any commit and follow the commit guidelines strictly.
* Keep the description (commit body) with maximum line length of 80
characters. Use manual line breaks to wrap text before it exceeds
this limit.
* Use `git commit -s` so the commit includes the required
`Signed-off-by` line.
* Do not guess or hallucinate git author information (Name or
Email). Never include the `--author` flag in git commands unless
specifically instructed by the user for a unique case; assume the
local environment is already configured.
+25
View File
@@ -0,0 +1,25 @@
---
name: Engineer
description: Senior Full-Stack Software Engineer
mode: primary
---
## Role
You are a high-autonomy Senior Full-Stack Software Engineer working on Penpot, an
open-source design tool. You have full permission to navigate the codebase, modify files,
and execute commands to fulfill your tasks. Your goal is to solve complex technical tasks
with high precision while maintaining a strong focus on maintainability and performance.
## Before Start
**Read `AGENTS.md` file and project structure and how the memory system works**
## Requiremens
* Before writing code, analyze the task in depth and describe your plan. If the task is
complex, break it down into atomic steps.
* Do **not** touch unrelated modules unless the task explicitly requires it.
* Only reference functions, namespaces, or APIs that actually exist in the
codebase. Verify their existence before citing them. If unsure, search first.
* Be concise and autonomous — avoid unnecessary explanations.
+60
View File
@@ -0,0 +1,60 @@
---
name: Planner
description: Software architect for planning and analysis only
mode: primary
permission:
edit: ask
---
## Role
You are a Senior Software Architect working on Penpot, an open-source design
tool. Your sole responsibility is planning and analysis — you do NOT write,
modify any code.
You help users understand the codebase, design solutions, and create detailed
implementation plans that other agents or developers can execute. Document
everything they need to know: which files to touch for each task, code, testing,
docs they might need to check, how to test it. Give them the whole plan as
bite-sized tasks. DRY. YAGNI. TDD. Frequent commits.
Do **not** suggest commit messages or commit names anywhere in your plans or
responses — committing is the developer's responsibility.
Assume they are a skilled developer, but know almost nothing about our toolset
or problem domain. Assume they don't know good test design very well.
## Requirements
* Analyze the codebase architecture and identify affected modules.
* Read `AGENTS.md` file and project structure and how the memory system works and how to
navigate and read relevant information conventions.
* Break down complex features or bugs into atomic, actionable steps.
* Propose solutions with clear rationale, trade-offs, and sequencing.
* Identify risks, edge cases, and testing considerations.
Save plans to: plans/YYYY-MM-DD-<plan-one-line-title>.md
## Constraints
* You are **read-only** — never create, edit, or delete files.
* You do **not** run builds, tests, linters, or any commands that modify state.
* You do **not** create git commits or interact with version control.
* You do **not** execute shell commands beyond read-only searches (`rg`, `ls`,
`find`, `cat`).
* Your output is a structured plan or analysis, ready for handoff to an
engineer agent or developer.
## Output format
When producing a plan, structure it as:
1. **Context** — What is the problem or feature request?
2. **Affected modules** — Which parts of the codebase are involved?
3. **Approach** — Step-by-step implementation plan with file paths and
function names where applicable.
4. **Risks & considerations** — Edge cases, performance implications, breaking
changes.
5. **Testing strategy** — How to verify the implementation works correctly.
+56
View File
@@ -0,0 +1,56 @@
---
name: Prompt Assistant
description: Refines and improves prompts for maximum clarity and effectiveness
mode: all
---
## Role
You are an expert Prompt Engineer with strong knowledge of
penpot. Your sole responsibility is to take a prompt provided by the
user and transform it into the most effective, clear, and
well-structured version possible — ready to be used with any AI model.
## Requirements
* You do NOT execute tasks. You do NOT write code. You only design and refine prompts
* Read `AGENTS.md` file and project structure and how the memory system works and how to
navigate and read relevant information conventions.
* Analyze the original prompt: identify its intent, target audience, ambiguities, missing
context, and structural weaknesses
* Ask clarifying questions if the intent is unclear or if critical information is missing
(e.g. target model, expected output format, tone, constraints). Keep questions concise
and grouped
* Rewrite the prompt using prompt engineering best practices
## Prompt Engineering Principles
Apply these techniques when refining prompts:
- **Be specific and explicit**: Replace vague instructions with precise ones.
- **Set the context**: Include background information the model needs to
perform well.
- **Specify the output format**: State the desired structure, length, tone,
or format (e.g. bullet list, JSON, step-by-step).
- **Add constraints**: Include what the model should avoid or not do.
- **Use examples** (few-shot): When applicable, suggest adding examples to
anchor the model's behaviour.
- **Break down complexity**: Split multi-step tasks into clear numbered steps.
- **Avoid ambiguity**: Remove pronouns and references that could be
misinterpreted.
- **Chain of thought**: For reasoning tasks, include "Think step by step."
## Constraints
- Do NOT execute the prompt yourself.
- Do NOT answer the question inside the prompt.
- Do NOT add unnecessary verbosity — prompts should be as short as they can
be while remaining complete.
- Always preserve the user's original intent.
## Output
Refined Prompt: The improved, ready-to-use prompt. Print it for
immediate use and save it to
prompts/YYYY-MM-DD-N-<prompt-one-line-title>.md for future use.
+85
View File
@@ -0,0 +1,85 @@
---
name: backport-commit
description: Port changes from a specific Git commit to the current branch by manually applying the diff, avoiding cherry-pick when it would introduce complex conflicts.
---
# Backport Commit
Port changes from a specific Git commit to the current branch by manually
applying the diff, avoiding `git cherry-pick` when it would introduce
complex conflicts.
## When to Use
Use this skill whenever the user asks to backport a commit, especially when:
- The commit touches multiple modules or files with significant divergence
- `git cherry-pick` is explicitly ruled out ("do not use cherry-pick")
- The target commit is old enough that conflicts are likely
- The commit introduces both source changes AND new files (tests, etc.)
- You need full control over how each hunk is applied
## Workflow
### 1. Identify the target commit
```bash
# Verify the commit exists and understand what it does
git log --oneline -1 <commit-sha>
# Get the full diff (including new/deleted files)
git show <commit-sha>
# Capture the original commit message for later reuse
git log --format='%B' -1 <commit-sha>
```
### 2. Identify affected modules
From the file paths in the diff, determine which Penpot modules are affected
(frontend, backend, common, render-wasm, etc.) and read their `AGENTS.md`
files **before** making any changes. If a module has no `AGENTS.md`, skip
that step — verify with `ls <module>/AGENTS.md` first.
### 3. Read the current state of each affected file
For every file the diff touches, read the current version on disk to understand
context and ensure correct placement before editing.
### 4. Apply changes manually (the core of this approach)
Process every hunk in the diff using the appropriate tool:
| Diff action | Tool to use |
|-------------|-------------|
| Modify existing file | `edit` — use enough surrounding context in `oldString` to uniquely match the location |
| Add new file | `write` — include proper license header and namespace conventions matching project style |
| Delete file | `bash rm <path>` |
| Rename/move file | `bash mv <old> <new>`, then apply any content changes with `edit` |
> **Tip:** Group nearby hunks from the same file into a single `edit` call.
> Use separate calls when hunks are far apart to keep `oldString` short and
> unambiguous.
Repeat until **all** hunks in the diff are ported.
### 5. Validate
Run **lint**, **check-fmt**, and **tests** for every affected module (see each
module's `AGENTS.md` for the exact commands). If the formatter auto-fixes
indentation, verify the logic is still semantically correct. All checks must
pass before moving on.
### 6. Commit
Ask the `commiter` sub-agent to create a commit. Stage all relevant files
(exclude unrelated untracked files) and provide the original commit message as
a reference, adapting it as needed for the target branch context.
## Key Principles
- **Context matters** — always read files before editing; never guess
indentation or surrounding code
- **Lint + format + test** — never skip validation before committing
- **Preserve intent** — keep the original commit message meaning; the
`commiter` agent handles formatting
+210
View File
@@ -0,0 +1,210 @@
---
name: bat-cat
description: A cat clone with syntax highlighting, line numbers, and Git integration - a modern replacement for cat.
homepage: https://github.com/sharkdp/bat
metadata: {"clawdbot":{"emoji":"🦇","requires":{"bins":["bat"]},"install":[{"id":"brew","kind":"brew","formula":"bat","bins":["bat"],"label":"Install bat (brew)"},{"id":"apt","kind":"apt","package":"bat","bins":["bat"],"label":"Install bat (apt)"}]}}
---
# bat - Better cat
`cat` with syntax highlighting, line numbers, and Git integration.
## Quick Start
### Basic usage
```bash
# View file with syntax highlighting
bat README.md
# Multiple files
bat file1.js file2.py
# With line numbers (default)
bat script.sh
# Without line numbers
bat -p script.sh
```
### Viewing modes
```bash
# Plain mode (like cat)
bat -p file.txt
# Show non-printable characters
bat -A file.txt
# Squeeze blank lines
bat -s file.txt
# Paging (auto for large files)
bat --paging=always file.txt
bat --paging=never file.txt
```
## Syntax Highlighting
### Language detection
```bash
# Auto-detect from extension
bat script.py
# Force specific language
bat -l javascript config.txt
# Show all languages
bat --list-languages
```
### Themes
```bash
# List available themes
bat --list-themes
# Use specific theme
bat --theme="Monokai Extended" file.py
# Set default theme in config
# ~/.config/bat/config: --theme="Dracula"
```
## Line Ranges
```bash
# Show specific lines
bat -r 10:20 file.txt
# From line to end
bat -r 100: file.txt
# Start to specific line
bat -r :50 file.txt
# Multiple ranges
bat -r 1:10 -r 50:60 file.txt
```
## Git Integration
```bash
# Show Git modifications (added/removed/modified lines)
bat --diff file.txt
# Show decorations (Git + file header)
bat --decorations=always file.txt
```
## Output Control
```bash
# Output raw (no styling)
bat --style=plain file.txt
# Customize style
bat --style=numbers,changes file.txt
# Available styles: auto, full, plain, changes, header, grid, numbers, snip
bat --style=header,grid,numbers file.txt
```
## Common Use Cases
**Quick file preview:**
```bash
bat file.json
```
**View logs with syntax highlighting:**
```bash
bat error.log
```
**Compare files visually:**
```bash
bat --diff file1.txt
bat file2.txt
```
**Preview before editing:**
```bash
bat config.yaml && vim config.yaml
```
**Cat replacement in pipes:**
```bash
bat -p file.txt | grep "pattern"
```
**View specific function:**
```bash
bat -r 45:67 script.py # If function is on lines 45-67
```
## Integration with other tools
**As pager for man pages:**
```bash
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
man grep
```
**With ripgrep:**
```bash
rg "pattern" -l | xargs bat
```
**With fzf:**
```bash
fzf --preview 'bat --color=always --style=numbers {}'
```
**With diff:**
```bash
diff -u file1 file2 | bat -l diff
```
## Configuration
Create `~/.config/bat/config` for defaults:
```
# Set theme
--theme="Dracula"
# Show line numbers, Git modifications and file header, but no grid
--style="numbers,changes,header"
# Use italic text on terminal
--italic-text=always
# Add custom mapping
--map-syntax "*.conf:INI"
```
## Performance Tips
- Use `-p` for plain mode when piping
- Use `--paging=never` when output is used programmatically
- `bat` caches parsed files for faster subsequent access
## Tips
- **Alias:** `alias cat='bat -p'` for drop-in cat replacement
- **Pager:** Use as pager with `export PAGER="bat"`
- **On Debian/Ubuntu:** Command may be `batcat` instead of `bat`
- **Custom syntaxes:** Add to `~/.config/bat/syntaxes/`
- **Performance:** For huge files, use `bat --paging=never` or plain `cat`
## Common flags
- `-p` / `--plain`: Plain mode (no line numbers/decorations)
- `-n` / `--number`: Only show line numbers
- `-A` / `--show-all`: Show non-printable characters
- `-l` / `--language`: Set language for syntax highlighting
- `-r` / `--line-range`: Only show specific line range(s)
## Documentation
GitHub: https://github.com/sharkdp/bat
Man page: `man bat`
Customization: https://github.com/sharkdp/bat#customization
+124
View File
@@ -0,0 +1,124 @@
---
name: create-pr
description: Create or update a GitHub PR following Penpot conventions, with a concise engineer-focused description
---
# Pull Request (Create or Update)
Create or update a GitHub PR with proper title format and a concise description that explains reasoning, not implementation details.
## When to Use
- Opening a new pull request
- Updating an existing PR's title or description
- The user asks to create or update a PR
- Code changes are ready and committed
## Conventions
All PR conventions (title format, body structure, writing principles, what NOT to include) are documented in `mem:workflow/creating-prs`. This skill covers the procedure only.
## Workflow A: Creating a New PR
### A1. Verify Prerequisites
```bash
git branch --show-current
git log --oneline main..HEAD
```
### A2. Check if Branch is Pushed
```bash
BRANCH=$(git branch --show-current)
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
echo "Branch is pushed, proceeding with PR creation"
else
echo "ERROR: Branch '$BRANCH' is not pushed to remote. Please push the branch first."
exit 1
fi
```
**If the branch is not pushed, STOP here and ask the user to push it. The LLM does not have push permissions.**
### A3. Create PR Body
Write the body to `/tmp/pr-body.md` using the template from `mem:workflow/creating-prs` (Description Body section):
```bash
cat > /tmp/pr-body.md << 'EOF'
**Note:** This PR was created with AI assistance as part of the Penpot MCP self-improvement initiative.
## What
<one paragraph: the problem or feature, user-facing impact>
## Why
<root cause or motivation, why this change was necessary>
## How
<high-level approach, key technical decisions>
EOF
```
### A4. Create the PR
```bash
gh pr create --base main --project "Main" --title "<title>" --body-file /tmp/pr-body.md
```
---
## Workflow B: Updating an Existing PR
Use this when a PR already exists and you need to change its title or description (or both).
### B1. Find the PR Number
```bash
BRANCH=$(git branch --show-current)
gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number'
```
If the result is empty, there is no open PR for this branch — use Workflow A instead.
If multiple PRs match, pick the first one (typically there should only be one).
### B2. Determine What to Update
- **Title only:** use `--title "<new title>"`
- **Description (body) only:** write to `/tmp/pr-body.md` and use `--body-file /tmp/pr-body.md`
- **Both title and description:** supply both flags
### B3. Prepare Description (if updating)
Use the same template from `mem:workflow/creating-prs`:
```bash
cat > /tmp/pr-body.md << 'EOF'
**Note:** This PR was created with AI assistance as part of the Penpot MCP self-improvement initiative.
## What
<one paragraph: the problem or feature, user-facing impact>
## Why
<root cause or motivation, why this change was necessary>
## How
<high-level approach, key technical decisions>
EOF
```
### B4. Update the PR
```bash
gh pr edit <NUMBER> --title "<title>" --body-file /tmp/pr-body.md
```
Omit either flag if only one field is being updated.
**The LLM has permissions to edit PRs. No push required.**
+194
View File
@@ -0,0 +1,194 @@
---
name: fd-find
description: A fast and user-friendly alternative to 'find' - simple syntax, smart defaults, respects gitignore.
homepage: https://github.com/sharkdp/fd
metadata: {"clawdbot":{"emoji":"📂","requires":{"bins":["fd"]},"install":[{"id":"brew","kind":"brew","formula":"fd","bins":["fd"],"label":"Install fd (brew)"},{"id":"apt","kind":"apt","package":"fd-find","bins":["fd"],"label":"Install fd (apt)"}]}}
---
# fd - Fast File Finder
User-friendly alternative to `find` with smart defaults.
## Quick Start
### Basic search
```bash
# Find files by name
fd pattern
# Find in specific directory
fd pattern /path/to/dir
# Case-insensitive
fd -i pattern
```
### Common patterns
```bash
# Find all Python files
fd -e py
# Find multiple extensions
fd -e py -e js -e ts
# Find directories only
fd -t d pattern
# Find files only
fd -t f pattern
# Find symlinks
fd -t l
```
## Advanced Usage
### Filtering
```bash
# Exclude patterns
fd pattern -E "node_modules" -E "*.min.js"
# Include hidden files
fd -H pattern
# Include ignored files (.gitignore)
fd -I pattern
# Search all (hidden + ignored)
fd -H -I pattern
# Maximum depth
fd pattern -d 3
```
### Execution
```bash
# Execute command on results
fd -e jpg -x convert {} {.}.png
# Parallel execution
fd -e md -x wc -l
# Use with xargs
fd -e log -0 | xargs -0 rm
```
### Regex patterns
```bash
# Full regex search
fd '^test.*\.js$'
# Match full path
fd --full-path 'src/.*/test'
# Glob pattern
fd -g "*.{js,ts}"
```
## Time-based filtering
```bash
# Modified within last day
fd --changed-within 1d
# Modified before specific date
fd --changed-before 2024-01-01
# Created recently
fd --changed-within 1h
```
## Size filtering
```bash
# Files larger than 10MB
fd --size +10m
# Files smaller than 1KB
fd --size -1k
# Specific size range
fd --size +100k --size -10m
```
## Output formatting
```bash
# Absolute paths
fd --absolute-path
# List format (like ls -l)
fd --list-details
# Null separator (for xargs)
fd -0 pattern
# Color always/never/auto
fd --color always pattern
```
## Common Use Cases
**Find and delete old files:**
```bash
fd --changed-before 30d -t f -x rm {}
```
**Find large files:**
```bash
fd --size +100m --list-details
```
**Copy all PDFs to directory:**
```bash
fd -e pdf -x cp {} /target/dir/
```
**Count lines in all Python files:**
```bash
fd -e py -x wc -l | awk '{sum+=$1} END {print sum}'
```
**Find broken symlinks:**
```bash
fd -t l -x test -e {} \; -print
```
**Search in specific time window:**
```bash
fd --changed-within 2d --changed-before 1d
```
## Integration with other tools
**With ripgrep:**
```bash
fd -e js | xargs rg "pattern"
```
**With fzf (fuzzy finder):**
```bash
vim $(fd -t f | fzf)
```
**With bat (cat alternative):**
```bash
fd -e md | xargs bat
```
## Performance Tips
- `fd` is typically much faster than `find`
- Respects `.gitignore` by default (disable with `-I`)
- Uses parallel traversal automatically
- Smart case: lowercase = case-insensitive, any uppercase = case-sensitive
## Tips
- Use `-t` for type filtering (f=file, d=directory, l=symlink, x=executable)
- `-e` for extension is simpler than `-g "*.ext"`
- `{}` in `-x` commands represents the found path
- `{.}` strips the extension
- `{/}` gets basename, `{//}` gets directory
## Documentation
GitHub: https://github.com/sharkdp/fd
Man page: `man fd`
+227
View File
@@ -0,0 +1,227 @@
---
name: gh-issue-from-pr
description: Create a user-facing GitHub issue from a PR, separating the WHAT from the HOW, with correct milestone, project, labels, and issue type.
---
# Skill: gh-issue-from-pr
Create a GitHub issue that captures the **WHAT** (user-facing feature or
bug) from an existing PR that describes the **HOW** (implementation).
Used when the project board needs an issue as the primary changelog/release unit.
## When to Use
- Create a tracking issue from a PR for changelog purposes
- Extract the user-facing problem/feature from a PR's implementation details
- Assign milestone, project, labels, and issue type to a new issue derived from a PR
## Prerequisites
- `gh` CLI authenticated (`gh auth status`)
- Permission to create issues and edit PRs in the target repository
## Workflow
### 1. Understand the PR
```bash
gh pr view <PR_NUMBER> --repo penpot/penpot \
--json title,body,author,labels,baseRefName,mergedAt,state,milestone
```
Identify:
- **WHAT** — user-facing problem or feature. Goes into the issue.
Describe symptoms and impact, not internal mechanisms.
- **HOW** — implementation details. These belong in the PR, not the issue.
### 2. Determine metadata
| Field | Source | Rule |
|-------|--------|------|
| **Title** | PR title | Rewrite from user perspective. Strip leading emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`). Focus on observable behavior. Use imperative mood. Use the `issue-title` skill to generate this. |
| **Labels** | PR labels | Copy `community contribution` if present. Skip `bug` and `enhancement` (redundant with Issue Type). Skip workflow labels (`backport candidate`, `team-qa`). |
| **Milestone** | PR milestone | **Always copy what's on the PR.** Fetch with: `gh pr view <PR_NUMBER> --json milestone --jq '.milestone.title'` If the PR has no milestone, create the issue without one. |
| **Project** | Always `Main` | Penpot uses the `Main` project (number 8) for all issues. |
| **Body** | PR's user-facing section | Extract steps to reproduce or feature description. Omit internal details. Use templates below. |
| **Issue Type** | PR labels / title | Map: `bug` label or `:bug:` title → `Bug`. `enhancement` label or `:sparkles:` title → `Enhancement`. Feature/epic → `Feature`. Default → `Task`. |
### 3. Write the issue body
**Bug template:**
```markdown
### Description
<what breaks, what the user experiences>
### Steps to reproduce
1. <step 1>
2. <step 2>
### Expected behavior
<what should happen instead>
### Affected versions
<version>
```
**Enhancement template:**
```markdown
### Description
<what the user can now do that they couldn't before>
### Use case
<why this is useful, who benefits>
### Affected versions
<version>
```
### 4. Create the issue
Write the body to a temp file to avoid shell quoting issues:
```bash
cat > /tmp/issue-body.md << 'ISSUE_BODY'
<body content here>
ISSUE_BODY
```
Create:
```bash
gh issue create \
--repo penpot/penpot \
--title "<Title>" \
--label "community contribution" \ # only if PR has this label
--milestone "<milestone>" \
--project "Main" \
--body-file /tmp/issue-body.md
```
Output: `https://github.com/penpot/penpot/issues/<NUMBER>`
### 5. Assign to the PR author
Assign the issue to the PR author so they're responsible for it:
```bash
AUTHOR=$(gh pr view <PR_NUMBER> --repo penpot/penpot --json author --jq '.author.login')
gh issue edit <ISSUE_NUMBER> --repo penpot/penpot --add-assignee "$AUTHOR"
```
### 6. Set the Issue Type
`gh issue create` can't set the Issue Type directly. Use GraphQL.
Get the issue's GraphQL node ID:
```bash
ISSUE_ID=$(gh api graphql -f query='
query { repository(owner: "penpot", name: "penpot") {
issue(number: <ISSUE_NUMBER>) { id }
}}' --jq '.data.repository.issue.id')
```
Issue Type IDs for the Penpot repo:
| Type | ID |
|------|----|
| Bug | `IT_kwDOAcyBPM4AX5Nb` |
| Enhancement | `IT_kwDOAcyBPM4B_IQN` |
| Feature | `IT_kwDOAcyBPM4AX5Nf` |
| Task | `IT_kwDOAcyBPM4AX5NY` |
| Question | `IT_kwDOAcyBPM4B_IQj` |
| Docs | `IT_kwDOAcyBPM4B_IQz` |
Set it:
```bash
gh api graphql -f query='
mutation {
updateIssue(input: {
id: "'"$ISSUE_ID"'"
issueTypeId: "<TYPE_ID>"
}) {
issue { number issueType { name } }
}
}'
```
### 7. Verify
```bash
gh issue view <ISSUE_NUMBER> --repo penpot/penpot \
--json title,milestone,projectItems,labels \
--jq '{title, milestone: .milestone.title, projects: [.projectItems[].title], labels: [.labels[].name]}'
gh api graphql -f query='
query { repository(owner: "penpot", name: "penpot") {
issue(number: <ISSUE_NUMBER>) { issueType { name } }
}}' --jq '.data.repository.issue.issueType.name'
```
### 8. Link the PR to the issue
Append `Closes #<ISSUE_NUMBER>` to the PR body:
```bash
gh pr view <PR_NUMBER> --repo penpot/penpot --json body --jq '.body' > /tmp/pr-body.md
printf "\n\nCloses #<ISSUE_NUMBER>\n" >> /tmp/pr-body.md
gh pr edit <PR_NUMBER> --repo penpot/penpot --body-file /tmp/pr-body.md
# Verify
gh pr view <PR_NUMBER> --repo penpot/penpot --json body \
--jq '.body | test("Closes #<ISSUE_NUMBER>")'
```
**Note:** If the PR is already merged, `Closes` won't auto-close the issue
— it only creates the "Development" sidebar link. This is the desired
behavior since the issue is a tracking artifact.
### 9. Clean up
```bash
rm -f /tmp/issue-body.md /tmp/pr-body.md
```
## Label rules
| PR has | Issue gets |
|--------|-----------|
| `community contribution` | `community contribution` |
| `bug`, `enhancement` | *(skip — redundant with Issue Type)* |
| `backport candidate` | *(skip — workflow label)* |
| `team-qa` | *(skip — workflow label)* |
## Issue Type mapping
| PR label(s) / title prefix | Issue Type |
|----------------------------|-----------|
| `bug` or `:bug:` | Bug |
| `enhancement` or `:sparkles:` or `:tada:` | Enhancement |
| Feature / epic | Feature |
| Documentation | Docs |
| None of the above | Task |
## Key Principles
- **Issue = WHAT, PR = HOW.** Never put implementation details in the
issue body. The issue is for users, QA, and changelog readers.
- **Copy the milestone from the PR.** Don't guess based on branch names.
If the PR has no milestone, create the issue without one.
- **Set Issue Type via GraphQL** — `gh issue create` can't set it.
- **Link via PR body** — `Closes #<NUMBER>` creates the "Development"
sidebar link automatically.
- **One issue per PR** — even if a PR fixes multiple things, create a
single issue that summarizes the overall change.
- **Community attribution:** if the PR has the `community contribution`
label or the author is not a core team member, add the label to the issue.
+123
View File
@@ -0,0 +1,123 @@
---
name: issue-title
description: Derive a clear, well-formatted title for a GitHub issue from its description body, using descriptive present-tense for bugs and imperative mood for features, always including the "where" (location in the UI/module).
---
# Skill: issue-title
Derive a concise, descriptive title for a GitHub issue based on its body
content. Use **descriptive present tense for bugs** (e.g. "Plugin API
crashes when setting text fills") and **imperative mood for features** (e.g.
"Add customizable dash and gap controls"). No emoji or type prefixes
(`feat:`, `bug:`, `feature:`, etc.).
Can be used both when **creating a new issue** and when **updating an
existing one** that has a vague or outdated title.
## When to Use
- Creating a new issue and need a well-formatted title from the draft body
- An existing issue has a vague, outdated, or auto-generated title (e.g.
`[PENPOT FEEDBACK]: ...`, `feature: ...`)
- The current title doesn't reflect the actual content of the description
- The title is missing the "where" (which part of the UI/module is affected)
## Prerequisites
- `gh` CLI authenticated (`gh auth status`)
## Workflow
### 1. Get the issue body
For an **existing issue**, fetch it:
```bash
gh issue view <NUMBER> --repo penpot/penpot --json title,body
```
For a **new issue**, read the draft body from wherever it was provided
(Taiga link, user report, discussion, etc.).
### 2. Read the body and derive a title
Extract the core problem or request from the description. Distinguish between
bug reports and feature requests:
**Bug titles (descriptive, present tense):**
Describe the symptom as it appears to the user. Format:
`[Where] [present-tense verb] when [condition]`
- *"Plugin API crashes when setting text fills"*
- *"Canvas renders glitches when zooming quickly"*
- *"French Canada locale falls back to French (fr) translations"*
- *"Text layer content is not deleted when WebGL render is enabled"*
Do **not** start bug titles with "Fix" or any imperative verb. The title
should state what's broken, not command a fix.
**Feature / Enhancement titles (imperative mood):**
Command what should be built. Format:
`[Imperative verb] [what] in/on [where]`
- *"Add customizable dash and gap length controls to dashed strokes in the sidebar"*
- *"Show user, timestamp, and hash in the workspace history panel like git commits"*
- *"Validate shape on add-object to catch malformed inputs early"*
**Universal rules (both types):**
- **Include the "where"** — specify the UI location or module (e.g.
"in the sidebar", "in the workspace history panel", "on the stroke
options")
- **No prefixes** — strip `bug:`, `feature:`, `feat:`, `:bug:`, `:sparkles:`,
`[PENPOT FEEDBACK]`, etc.
- **No emoji** — plain text only
- **Be specific** — prefer concrete detail over generality. If the
description mentions two related problems, capture both.
**Examples:**
| Original / draft title | Type | New title |
|---|---|---|
| `[PENPOT FEEDBACK]: WebGL` | Bug | `Canvas renders glitches when zooming quickly — text appears distorted and nodes have background-colored rectangles` |
| `bug: flatten-nested-tokens-json uses $type instead of $value as the DTCG token/group discriminator` | Bug | `Token import fails when group-level type inheritance is used — parser misidentifies groups as tokens` |
| `feature: Dashed stroke customization` | Feature | `Add customizable dash and gap length controls to dashed strokes in the sidebar` |
| `feature: Add more detail to history of actions` | Feature | `Show user, timestamp, and hash in the workspace history panel like git commits` |
### 3. Apply the title
**If updating an existing issue:**
```bash
gh issue edit <NUMBER> --repo penpot/penpot --title "<NEW TITLE>"
```
**If creating a new issue:**
```bash
gh issue create --repo penpot/penpot --title "<NEW TITLE>" --body "<BODY>"
```
### 4. Confirm
For updates, the command returns the issue URL. Verify by optionally fetching
again:
```bash
gh issue view <NUMBER> --repo penpot/penpot --json title
```
## Key Principles
- **Bug titles describe the symptom** — present tense, 3rd person:
"crashes", "fails", "shows", "is cut off", "does not load". Do not
start with "Fix" or "Bug:".
- **Feature titles use imperative mood** — command form: "Add", "Show",
"Use", "Validate", "Support", "Toggle".
- **Always include the "where"** — a title like "Crashes when zooming"
is too vague; "Canvas crashes when zooming quickly" is clear.
- **No prefixes, no emoji** — strip all type labels and decorative
characters from the title.
- **Derive from the body, not the current title** — the body contains
the real detail; the current title may be auto-generated or stale.
- **Two problems → cover both** — if the description has two distinct
but related issues, capture both in the title joined by "and".
+112
View File
@@ -0,0 +1,112 @@
---
name: jq-json-processor
description: Process, filter, and transform JSON data using jq - the lightweight and flexible command-line JSON processor.
homepage: https://jqlang.github.io/jq/
metadata: {"clawdbot":{"emoji":"🔍","requires":{"bins":["jq"]},"install":[{"id":"brew","kind":"brew","formula":"jq","bins":["jq"],"label":"Install jq (brew)"},{"id":"apt","kind":"apt","package":"jq","bins":["jq"],"label":"Install jq (apt)"}]}}
---
# jq JSON Processor
Process, filter, and transform JSON data with jq.
## Quick Examples
### Basic filtering
```bash
# Extract a field
echo '{"name":"Alice","age":30}' | jq '.name'
# Output: "Alice"
# Multiple fields
echo '{"name":"Alice","age":30}' | jq '{name: .name, age: .age}'
# Array indexing
echo '[1,2,3,4,5]' | jq '.[2]'
# Output: 3
```
### Working with arrays
```bash
# Map over array
echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '.[].name'
# Output: "Alice" "Bob"
# Filter array
echo '[1,2,3,4,5]' | jq 'map(select(. > 2))'
# Output: [3,4,5]
# Length
echo '[1,2,3]' | jq 'length'
# Output: 3
```
### Common operations
```bash
# Pretty print JSON
cat file.json | jq '.'
# Compact output
cat file.json | jq -c '.'
# Raw output (no quotes)
echo '{"name":"Alice"}' | jq -r '.name'
# Output: Alice
# Sort keys
echo '{"z":1,"a":2}' | jq -S '.'
```
### Advanced filtering
```bash
# Select with conditions
jq '[.[] | select(.age > 25)]' people.json
# Group by
jq 'group_by(.category)' items.json
# Reduce
echo '[1,2,3,4,5]' | jq 'reduce .[] as $item (0; . + $item)'
# Output: 15
```
### Working with files
```bash
# Read from file
jq '.users[0].name' users.json
# Multiple files
jq -s '.[0] * .[1]' file1.json file2.json
# Modify and save
jq '.version = "2.0"' package.json > package.json.tmp && mv package.json.tmp package.json
```
## Common Use Cases
**Extract specific fields from API response:**
```bash
curl -s https://api.github.com/users/octocat | jq '{name: .name, repos: .public_repos, followers: .followers}'
```
**Convert CSV-like data:**
```bash
jq -r '.[] | [.name, .email, .age] | @csv' users.json
```
**Debug API responses:**
```bash
curl -s https://api.example.com/data | jq '.'
```
## Tips
- Use `-r` for raw string output (removes quotes)
- Use `-c` for compact output (single line)
- Use `-S` to sort object keys
- Use `--arg name value` to pass variables
- Pipe multiple jq operations: `jq '.a' | jq '.b'`
## Documentation
Full manual: https://jqlang.github.io/jq/manual/
Interactive tutorial: https://jqplay.org/
+120
View File
@@ -0,0 +1,120 @@
---
name: nrepl-eval
description: Evaluate Clojure code via nREPL using the standalone tools/nrepl-eval.mjs CLI tool.
---
# nREPL Eval
Evaluate Clojure (or ClojureScript) code via a running nREPL server using
`tools/nrepl-eval.mjs` — a standalone CLI application.
Session state (defs, in-ns, etc.) persists across invocations via a stored
session ID, so you can build up state incrementally.
## Usage
```bash
node tools/nrepl-eval.mjs [options] [<code>]
```
The tool is also executable directly:
```bash
./tools/nrepl-eval.mjs [options] [<code>]
```
## Options
| Flag | Description | Default |
|------|-------------|---------|
| `-p, --port PORT` | nREPL server port | `6064` |
| `-H, --host HOST` | nREPL server host | `127.0.0.1` |
| `-t, --timeout MS` | Timeout in milliseconds | `120000` |
| `--reset-session` | Discard stored session and start fresh | — |
| `-e, --last-error` | Evaluate `*e` to retrieve the last exception | — |
| `-h, --help` | Show help message | — |
## When to Use
Use this tool when you need to:
1. **Evaluate Clojure code** during development — test functions, inspect
state, or run experiments against a running Clojure process.
2. **Verify that edited files compile** — require namespaces with `:reload`
to pick up changes.
3. **Inspect the last exception** after a failed evaluation — use `-e` to
print the error stored in `*e`.
## Workflow
### 1. Session management
Sessions are persisted to `/tmp/penpot-nrepl-session-<host>-<port>`. State
carries across calls automatically:
```bash
./tools/nrepl-eval.mjs '(def x 42)'
./tools/nrepl-eval.mjs 'x'
# => 42
```
Reset the session to start fresh:
```bash
./tools/nrepl-eval.mjs --reset-session '(def x 0)'
```
### 2. Evaluate code
**Single expression (inline) — uses default port 6064:**
```bash
./tools/nrepl-eval.mjs '(+ 1 2 3)'
```
**Multiple expressions via heredoc (recommended — avoids escaping issues):**
```bash
./tools/nrepl-eval.mjs <<'EOF'
(def x 10)
(+ x 20)
EOF
```
**Override with a different port:**
```bash
./tools/nrepl-eval.mjs -p 7888 '(+ 1 2 3)'
```
### 3. Inspect last exception
After code throws an error, retrieve the full exception details:
```bash
./tools/nrepl-eval.mjs -e
```
## Common Patterns
**Require a namespace with reload:**
```bash
./tools/nrepl-eval.mjs "(require '[my.namespace :as ns] :reload)"
```
**Test a function:**
```bash
./tools/nrepl-eval.mjs "(ns/my-function arg1 arg2)"
```
**Long-running operation with custom timeout:**
```bash
./tools/nrepl-eval.mjs -t 300000 "(long-running-fn)"
```
## Key Principles
- **Default port is 6064** — just pass code directly, no `-p` needed when
your nREPL server is on 6064. Use `-p <PORT>` for a different port.
- **Always use `:reload`** when requiring namespaces to pick up file changes.
- **Session is reused** across invocations — defs, in-ns, and var bindings
persist. Use `--reset-session` to clear.
- **Do not start any server** — the tool connects to an existing nREPL
server, it is not the agent's responsibility to start the nREPL server
(assume the server is already running on the specified port).
+150
View File
@@ -0,0 +1,150 @@
---
name: ripgrep
description: Blazingly fast text search tool - recursively searches directories for regex patterns with respect to gitignore rules.
homepage: https://github.com/BurntSushi/ripgrep
metadata: {"clawdbot":{"emoji":"🔎","requires":{"bins":["rg"]},"install":[{"id":"brew","kind":"brew","formula":"ripgrep","bins":["rg"],"label":"Install ripgrep (brew)"},{"id":"apt","kind":"apt","package":"ripgrep","bins":["rg"],"label":"Install ripgrep (apt)"}]}}
---
# ripgrep (rg)
Fast, smart recursive search. Respects `.gitignore` by default.
## Quick Start
### Basic search
```bash
# Search for "TODO" in current directory
rg "TODO"
# Case-insensitive search
rg -i "fixme"
# Search specific file types
rg "error" -t py # Python files only
rg "function" -t js # JavaScript files
```
### Common patterns
```bash
# Whole word match
rg -w "test"
# Show only filenames
rg -l "pattern"
# Show with context (3 lines before/after)
rg -C 3 "function"
# Count matches
rg -c "import"
```
## Advanced Usage
### File type filtering
```bash
# Multiple file types
rg "error" -t py -t js
# Exclude file types
rg "TODO" -T md -T txt
# List available types
rg --type-list
```
### Search modifiers
```bash
# Regex search
rg "user_\d+"
# Fixed string (no regex)
rg -F "function()"
# Multiline search
rg -U "start.*end"
# Only show matches, not lines
rg -o "https?://[^\s]+"
```
### Path filtering
```bash
# Search specific directory
rg "pattern" src/
# Glob patterns
rg "error" -g "*.log"
rg "test" -g "!*.min.js"
# Include hidden files
rg "secret" --hidden
# Search all files (ignore .gitignore)
rg "pattern" --no-ignore
```
## Replacement Operations
```bash
# Preview replacements
rg "old_name" --replace "new_name"
# Actually replace (requires extra tool like sd)
rg "old_name" -l | xargs sed -i 's/old_name/new_name/g'
```
## Performance Tips
```bash
# Parallel search (auto by default)
rg "pattern" -j 8
# Skip large files
rg "pattern" --max-filesize 10M
# Memory map files
rg "pattern" --mmap
```
## Common Use Cases
**Find TODOs in code:**
```bash
rg "TODO|FIXME|HACK" --type-add 'code:*.{rs,go,py,js,ts}' -t code
```
**Search in specific branches:**
```bash
git show branch:file | rg "pattern"
```
**Find files containing multiple patterns:**
```bash
rg "pattern1" | rg "pattern2"
```
**Search with context and color:**
```bash
rg -C 2 --color always "error" | less -R
```
## Comparison to grep
- **Faster:** Typically 5-10x faster than grep
- **Smarter:** Respects `.gitignore`, skips binary files
- **Better defaults:** Recursive, colored output, line numbers
- **Easier:** Simpler syntax for common tasks
## Tips
- `rg` is often faster than `grep -r`
- Use `-t` for file type filtering instead of `--include`
- Combine with other tools: `rg pattern -l | xargs tool`
- Add custom types in `~/.ripgreprc`
- Use `--stats` to see search performance
## Documentation
GitHub: https://github.com/BurntSushi/ripgrep
User Guide: https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md
+110
View File
@@ -0,0 +1,110 @@
---
name: taiga
description: Fetch information from Taiga public API for the Penpot project (id 345963) — issues, user stories, and tasks, without authentication.
metadata: {"clawdbot":{"requires":{"bins":["python3"]}}}
---
# Taiga API Skill
Fetch information from Taiga public API for the **Penpot** project
(project id: `345963`, slug: `penpot`).
**No authentication required** — only public project data is accessed.
## Prerequisites
- `python3` — the `tools/taiga.py` CLI script is self-contained (stdlib only)
## Quick Start
The easiest way is to use the bundled Python script:
```bash
# Pass a Taiga URL directly
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
# Or use "<type> <ref>" syntax
python3 tools/taiga.py us 14128
python3 tools/taiga.py task 13648
# Add --json for raw output
python3 tools/taiga.py --json issue 13714
# See full usage
python3 tools/taiga.py --help
```
## URL Pattern Reference
Taiga web URLs follow these patterns:
| Type | Web URL Pattern |
|------|----------------|
| Issue | `https://tree.taiga.io/project/penpot/issue/<REF>` |
| User Story | `https://tree.taiga.io/project/penpot/us/<REF>` |
| Task | `https://tree.taiga.io/project/penpot/task/<REF>` |
To extract the **type** and **ref** from a URL:
- `issue/13714` → type=`issue`, ref=`13714`
- `us/14128` → type=`us`, ref=`14128`
- `task/13648` → type=`task`, ref=`13648`
## Python Script Reference
The `tools/taiga.py` script wraps the Taiga API into a single convenient CLI
with sensible defaults.
### Usage
```
python3 tools/taiga.py <taiga-url>
python3 tools/taiga.py <type> <ref>
python3 tools/taiga.py [--json] <taiga-url>
python3 tools/taiga.py [--json] <type> <ref>
```
### Examples
```bash
# By URL (recommended — no need to think about type/ref)
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
# By type and ref
python3 tools/taiga.py us 14128
python3 tools/taiga.py task 13648
# Raw JSON output
python3 tools/taiga.py --json issue 13714
```
### Output
The script prints a clean, structured summary:
```text
User Story #11964 — 🔴 [DESIGN TOKENS] Typography Composite Input
================================
Status: Defining
Milestone: design-systems-sprint-26
Points: 3 role(s)
Assignee: Natacha Menjibar
Author: Natacha Menjibar
Created: 2025-09-01
Tags: iop-design-tokens
URL: https://tree.taiga.io/project/penpot/us/11964
================================
<full description text, unmodified>
```
The fields section includes type-specific information:
- **Issues:** Status, Type ID, Severity ID, Priority ID
- **User Stories:** Status, Milestone, Points
- **Tasks:** Status, Milestone, Parent US
## Reference
- API docs: https://docs.taiga.io/api.html
- Taiga instance: https://tree.taiga.io
- API base: https://api.taiga.io/api/v1
- Penpot project id: `345963`
- Penpot project slug: `penpot`
+630
View File
@@ -0,0 +1,630 @@
---
name: update-changelog
description: Update the project CHANGES.md with issues from a given GitHub milestone, with correct categorization and references.
---
# Skill: update-changelog
Update `CHANGES.md` with entries for all issues and PRs in a given GitHub
milestone. Each entry references the user-facing issue (not the PR) as the
primary link, with the fix PR inline on the same line.
## When to Use
- Before a new release, to populate the changelog with all fixed issues
- When new issues are added to an existing milestone and the changelog needs
to be refreshed
- To ensure every entry follows the correct format for the changelog
## Prerequisites
- `gh` CLI authenticated (`gh auth status`)
- Python 3.8+
- `tools/gh.py` helper script available
## Workflow
### 1. Determine the target version
The version is typically a semver string like `2.15.3`. Confirm with the user
if not specified.
### 2. Fetch all issues in the milestone
Use the helper script. It uses GraphQL for efficient single-pass fetching
(closing PRs are included in the same query — no N+1):
```bash
# All closed issues (default)
python3 tools/gh.py issues "2.16.0"
# Include open issues too
python3 tools/gh.py issues "2.16.0" --state all
# Exclude entries that should not go in the changelog
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
```
**Exclusion rules (issue-level):**
- `no changelog` label — Chore/refactor work that doesn't need a changelog entry
- `release blocker` label — Blocked issues not yet ready for changelog
- `Task` issue type — Internal chores are not user-facing; automatically excluded by `gh.py`. Use `--include-tasks` to override.
- **Rejected project status** — Issues with a "Rejected" status in the "Main" project board are automatically excluded by `gh.py`. This project-level status (independent of the GitHub issue `state`) indicates the issue was rejected from the release. Use `--include-rejected` to override.
**Exclusion rules (PR-level):**
In addition to issue-level exclusions, PRs with these labels should be
excluded regardless of their linked issue's labels:
- `release blocker` — PR is part of a pending release blocker batch
- `no issue required` — Trivial fix not tracked as an issue
The script outputs JSON with each entry containing `number`, `title`, `state`,
`issue_type`, `labels`, `closing_prs` (the PRs that fix each issue), and
`project_status` (the "Main" project board status, e.g. "Done", "Rejected",
or `null` if not tracked in a project).
### 3. Identify missing entries (optional)
If updating from an existing `CHANGES.md`, find issues in the milestone that
are NOT yet referenced in the changelog:
```bash
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog" --compare CHANGES.md
```
This returns a filtered JSON array with only the missing issues.
> **Note:** The `--compare` flag checks **issues** only (via issue number
> references in the changelog). To find merged **PRs** not yet referenced,
> use the milestone PR cross-reference described in step 10 below.
### 4. Fetch additional PR details when needed
When you need more context for specific PRs (e.g. to find the PR author for
community contribution attribution, or to read the PR body for
"Fixes/Closes #NNN" patterns):
```bash
# One or more PR numbers
python3 tools/gh.py prs 9179 9204 9311
# From a file
python3 tools/gh.py prs --file prs.txt
# From stdin
cat prs.txt | python3 tools/gh.py prs --stdin
```
The `prs` command also supports listing all PRs in a milestone in one call:
```bash
# All merged PRs in a milestone (default)
python3 tools/gh.py prs --milestone "2.16.0"
# All states (merged, open, closed)
python3 tools/gh.py prs --milestone "2.16.0" --state all
```
The `prs` command returns JSON with `number`, `title`, `body`, `state`,
`merged_at`, `author`, `labels`, and `closing_issues`. PRs are fetched in
batches of 50 via GraphQL to stay within API limits (milestone mode uses
paginated GraphQL on the milestone's `pullRequests` connection).
You can also list all PRs in a milestone in a single call:
```bash
# All merged PRs in a milestone (default)
python3 tools/gh.py prs --milestone "2.16.0"
# All states (merged, open, closed)
python3 tools/gh.py prs --milestone "2.16.0" --state all
# Open PRs only
python3 tools/gh.py prs --milestone "2.16.0" --state open
```
The milestone path uses paginated GraphQL on the milestone's `pullRequests`
connection (100 per page), avoiding one-by-one fetches.
### 5. Categorize entries — strictly by issue type, never by labels or emoji
Use the **Issue Type** field (GitHub's native issue type, exposed as
`issue_type` in the `gh.py` JSON output) to determine which section an entry
belongs to.
> **⚠️ CRITICAL: Never use labels or title emoji prefixes for categorization.**
> Labels like `bug` and `enhancement`, as well as title prefixes like `:bug:`
> and `:sparkles:`, are frequently inaccurate, missing, or contradictory to the
> actual issue type. The `issue_type` field from `gh.py` is the single source
> of truth.
| `issue_type` value | Changelog section |
|--------------------|-------------------|
| `Bug` | `### :bug: Bugs fixed` |
| `Feature` or `Enhancement` | `### :sparkles: New features & Enhancements` |
| `Task` | **Exclude** — internal chores are not user-facing |
| `null` (not set) | Check labels as a fallback: `bug` label → bugs, otherwise enhancements |
The `gh.py` issues command already includes `issue_type` in every entry's
output. **No separate GraphQL query is needed.**
**Community contribution attribution:** If the issue or its fix PR has the
`community contribution` label, add an attribution `(by @<github_username>)`
on the changelog entry line, **before** the GitHub issue/PR references.
The attribution should reference the **PR author**, not the issue author.
The `prs` subcommand includes the `author` field — use that:
```bash
python3 tools/gh.py prs <PR_NUMBER> | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['author'])"
```
Placement in the entry line:
```markdown
- Fix description of the bug (by @username) [#<ISSUE>](...) (PR: [#<PR>](...))
```
**Only closed issues are included.** An issue must have `state: "closed"` to
appear in the changelog. Open/unresolved issues are omitted, even if they are
tracked in the milestone.
**Pairing rules:**
| Pattern | Changelog format |
|---------|-----------------|
| Closed issue + one or more PRs fix it | Primary link = issue, PR inline comma-separated |
| PR exists with no linked issue | If a corresponding closed issue exists in the same milestone, link the issue. Otherwise, skip the entry (the issue must be the changelog unit). |
| Closed issue with no fix PR in milestone | Link the issue directly, without a PR reference. |
> **False-positive associations:** A PR may incorrectly claim to close an issue
> from a different context (e.g., a very old PR referencing a modern issue, or a
> cross-project reference). If the PR title and issue title are clearly unrelated,
> or the PR was created years before the issue, treat it as a data glitch and
> skip it. PR [#3](https://github.com/penpot/penpot/pull/3) (ancient License PR
> claiming to close a plugin API issue) is a known example.
### 5a. ⚠️ Verify PR merge status before writing
A closed issue may list closing PRs that were **closed without merging**
(e.g., a community PR that was superseded by another). The changelog must
only reference **merged** PRs. Verify before writing:
```bash
# Collect all PR numbers from the candidate entries and check them
python3 tools/gh.py prs <ALL_PR_NUMBERS> | python3 -c "
import json, sys
for pr in json.load(sys.stdin):
if pr['state'] != 'MERGED':
print(f'WARNING: #{pr[\"number\"]} is {pr[\"state\"]} (not merged)')
"
```
If a closing PR is closed-unmerged, find the actual merged PR that
superseded it:
1. Check the issue's closing PRs list for other PRs (there may be multiple)
2. Look for other PRs with similar titles or descriptions referencing the same issue
3. Inspect the closed PR's conversation timeline for a pointer to the replacement
Replace the reference in the changelog entry with the correct merged PR number.
### 6. Read the current CHANGES.md
Read the top of `CHANGES.md` to understand the existing format and find the
insertion point (newest version goes at the top, after the `# CHANGELOG`
header).
Key format rules from the existing file:
```markdown
## <VERSION>
### :bug: Bugs fixed
- Fix description of the bug [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
- Fix another bug (by @contributor) [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
### :sparkles: New features & Enhancements
- Add new feature description [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
```
Format details:
- Entries start with `- ` followed by a short description in imperative mood
- Primary link is **always the issue** (user-facing artifact)
- PR references are inline on the same line: `(PR: [#<N>](<url>))`
If an issue has multiple fix PRs, they are comma-separated:
`(PR: [#<N>](<url>), [#<M>](<url>))`
- The description should describe the fix/feature from the user's perspective
- Community contributions get `(by @<username>)` **before** the issue link
- Sections are separated by a blank line between the last entry and the next
section title
- Only include a section if there are entries for it
- When an entry already exists in an earlier version section, it must be removed
from the current version to avoid duplicates
### 7. Build the description text
Derive the description from the issue title, not the PR title. Strip leading
emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`) and focus on the
user-facing behavior.
Examples:
| Issue title | Changelog description |
|-------------|----------------------|
| `Plugin API token methods fail with schema validation error on PRO` | `Fix Plugin API token methods failing with schema validation error on PRO` |
| `Comment content is not sanitized before rendering, enabling stored XSS` | `Sanitize comment content on rendering` |
| `Custom uploaded font family names are not sanitized` | `Sanitize font family names on custom uploaded fonts` |
### 8. Insert the section into CHANGES.md
Insert the new version section right after the `# CHANGELOG` header (before
the previous version entry). Use the `edit` tool with enough context to make
a unique match.
### 9. Verify
Read the top of `CHANGES.md` and confirm:
- The version header is correct
- Every entry has a GitHub link
- Entries with a fix PR have the PR sub-line
- The section ordering is correct (newest first)
- Formatting matches the surrounding entries
### 10. Cross-reference milestone PRs against the changelog
Issues can be fixed by PRs that aren't in the milestone, and merged PRs in
the milestone may not close any tracked issue. After writing, run a full
cross-reference to catch gaps:
```bash
# List all merged PRs in the milestone
python3 tools/gh.py prs --milestone "<MILESTONE>" --state merged > /tmp/milestone-prs.json
# Extract PR numbers from the changelog section
python3 -c "
import json, re
with open('CHANGES.md') as f:
content = f.read()
# Extract the version section (adjust regex to match the actual version)
match = re.search(r'## <MILESTONE> \(Unreleased\)\n(.*?)(?:\n## |\Z)', content, re.DOTALL)
section = match.group(1)
# Collect all PR numbers referenced
changelog_prs = set()
for m in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/pull/\d+\)', section):
changelog_prs.add(int(m))
# Collect all milestone PRs (filtered)
with open('/tmp/milestone-prs.json') as f:
milestone_prs = json.load(f)
milestone_merged = {pr['number'] for pr in milestone_prs}
# PRs in milestone but not in changelog
missing = sorted(milestone_merged - changelog_prs)
print(f'Milestone merged PRs: {len(milestone_merged)}')
print(f'Changelog referenced PRs: {len(changelog_prs)}')
print(f'PRs in milestone but NOT in changelog: {len(missing)}')
for num in missing:
pr = next(p for p in milestone_prs if p['number'] == num)
print(f' #{num} {pr[\"title\"][:80]}')
"
```
For each missing PR found, decide whether it should be added to the
changelog or is legitimately excluded (check its labels).
Also verify that no closed-unmerged PRs remain in the changelog:
```bash
python3 tools/gh.py prs --milestone "<MILESTONE>" --state all | python3 -c "
import json, sys
data = json.load(sys.stdin)
closed = [p for p in data if p['state'] == 'CLOSED']
if closed:
print('WARNING: CLOSED (unmerged) PRs in milestone:')
for p in closed:
print(f' #{p[\"number\"]} {p[\"title\"][:80]}')
"
```
**Post-edit audit checklist:**
- ✅ All referenced PRs are merged (no closed-unmerged artifacts)
- ✅ Every merged milestone PR is either in the changelog or excluded by label
- ✅ PR and issue counts are internally consistent
- ✅ No false-positive PR-to-issue associations
## Version section template
```markdown
## <VERSION>
### :bug: Bugs fixed
- <fix description> [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
- <fix description> (by @contributor) [#<ISSUE>](https://github.com/penpot/penpot/issues/<ISSUE>) (PR: [#<PR>](https://github.com/penpot/penpot/pull/<PR>))
```
### 11. Generate anomaly report and save to CHANGES-ISSUES.md
After all edits and cross-referencing are complete, generate a structured
anomaly report and save it to `CHANGES-ISSUES.md` (overwriting if exists).
This provides a persistent record of any discrepancies between the milestone
and the changelog.
Run this self-contained script:
```bash
python3 << 'PYEOF'
import json, re, subprocess, sys
MILESTONE = "<MILESTONE>"
CHANGES_MD = "CHANGES.md"
OUTPUT = "CHANGES-ISSUES.md"
# Fetch milestone issues (all states)
result = subprocess.run(
["python3", "tools/gh.py", "issues", MILESTONE, "--state", "all"],
capture_output=True, text=True)
all_issues = json.loads(result.stdout)
issue_by_num = {i['number']: i for i in all_issues}
# Fetch milestone PRs (all states)
result = subprocess.run(
["python3", "tools/gh.py", "prs", "--milestone", MILESTONE, "--state", "all"],
capture_output=True, text=True)
all_prs = json.loads(result.stdout)
pr_by_num = {p['number']: p for p in all_prs}
# Read changelog
with open(CHANGES_MD) as f:
content = f.read()
m = re.search(rf'## {MILESTONE} \(Unreleased\)\n(.*?)(?:\n## |\Z)', content, re.DOTALL)
section = m.group(1) if m else ""
# Collect issue and PR references from the changelog section
changelog_issues = set()
for num in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/issues/\d+\)', section):
changelog_issues.add(int(num))
for num in re.findall(r'\[Github #(\d+)\]', section):
changelog_issues.add(int(num))
changelog_prs = set()
for num in re.findall(r'\[#(\d+)\]\(https://github\.com/penpot/penpot/pull/\d+\)', section):
changelog_prs.add(int(num))
for num in re.findall(r'PR:\[(\d+)\]', section):
changelog_prs.add(int(num))
# Determine valid (non-excluded) milestone issues
EXCLUDED_LABELS = {'release blocker', 'no changelog'}
EXCLUDED_ISSUE_TYPES = {'Task'}
EXCLUDED_PROJECT_STATUS = {'Rejected'}
valid_issues = []
for issue in all_issues:
labels = set(issue.get('labels', []))
if issue.get('state') != 'CLOSED': continue
if issue.get('issue_type') in EXCLUDED_ISSUE_TYPES: continue
if issue.get('project_status') in EXCLUDED_PROJECT_STATUS: continue
if EXCLUDED_LABELS & labels: continue
valid_issues.append(issue)
valid_nums = {i['number'] for i in valid_issues}
# --- Gather anomalies ---
anomalies = []
# Type 1: Entries in changelog that should be excluded
for num in sorted(changelog_issues):
issue = issue_by_num.get(num)
if issue is None:
anomalies.append({
'type': 'should_remove',
'severity': 'HIGH',
'number': num,
'title': '',
'reason': 'Issue not found in milestone (deleted or moved)'
})
continue
labels = set(issue.get('labels', []))
reasons = []
if issue.get('state') != 'CLOSED':
reasons.append(f'state is "{issue["state"]}" (should be CLOSED)')
if 'release blocker' in labels:
reasons.append('has "release blocker" label')
if 'no changelog' in labels:
reasons.append('has "no changelog" label')
if issue.get('issue_type') == 'Task':
reasons.append(f'issue_type is Task (internal chore)')
if issue.get('project_status') == 'Rejected':
reasons.append('project_status is Rejected')
if reasons:
anomalies.append({
'type': 'should_remove',
'severity': 'MEDIUM' if issue.get('issue_type') == 'Task' else 'HIGH',
'number': num,
'title': issue.get('title', '')[:80],
'reason': '; '.join(reasons)
})
# Type 2: Valid issues not in changelog
for num in sorted(valid_nums - changelog_issues):
issue = issue_by_num[num]
info = {
'type': 'missing',
'severity': 'MEDIUM',
'number': num,
'title': issue['title'][:80],
'issue_type': issue['issue_type'],
'closing_prs': issue.get('closing_prs', []),
'note': ''
}
# Check for duplicate (same PR as existing entry)
existing = []
for pr_num in issue.get('closing_prs', []):
for cl_num in changelog_issues:
cl_issue = issue_by_num.get(cl_num)
if cl_issue and pr_num in cl_issue.get('closing_prs', []):
existing.append(f'#{cl_num}')
if existing:
info['note'] = f'DUPLICATE: same PR as existing entry(ies): {", ".join(existing)}'
# Check closing PRs not merged
unmerged = []
for pr_num in issue.get('closing_prs', []):
pr = pr_by_num.get(pr_num)
if pr is None:
unmerged.append(f'#{pr_num} (unknown)')
elif pr.get('state') != 'MERGED':
unmerged.append(f'#{pr_num} (state={pr["state"]})')
if unmerged:
info['note'] = (info['note'] + '; ' if info['note'] else '') + f'Closing PRs not merged: {", ".join(unmerged)}'
anomalies.append(info)
# Type 3: PRs in changelog that are not merged
for pr_num in sorted(changelog_prs):
pr = pr_by_num.get(pr_num)
if pr is None:
anomalies.append({
'type': 'unmerged_pr',
'severity': 'HIGH',
'number': pr_num,
'title': '',
'reason': 'PR not found in milestone PR list'
})
elif pr.get('state') != 'MERGED':
anomalies.append({
'type': 'unmerged_pr',
'severity': 'HIGH',
'number': pr_num,
'title': pr.get('title', '')[:80],
'reason': f'state={pr["state"]} (should be MERGED)'
})
# --- Write report to CHANGES-ISSUES.md ---
with open(OUTPUT, 'w') as f:
f.write(f'# Changelog Anomaly Report — {MILESTONE}\n\n')
f.write(f'Generated: {__import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M UTC")}\n\n')
f.write('---\n\n')
# Summary
n_remove = sum(1 for a in anomalies if a['type'] == 'should_remove')
n_missing = sum(1 for a in anomalies if a['type'] == 'missing')
n_pr = sum(1 for a in anomalies if a['type'] == 'unmerged_pr')
f.write(f'## Summary\n\n')
f.write(f'- **Issues to remove from changelog:** {n_remove}\n')
f.write(f'- **Valid issues missing from changelog:** {n_missing}\n')
f.write(f'- **Unmerged PRs referenced:** {n_pr}\n')
f.write(f'- **Total anomalies:** {len(anomalies)}\n\n')
if not anomalies:
f.write('✅ No anomalies found. The changelog is fully consistent with the milestone.\n\n')
else:
# Type 1
if n_remove:
f.write(f'## Issues to Remove\n\n')
f.write('These entries are in the changelog but should be excluded based on current issue metadata.\n\n')
for a in anomalies:
if a['type'] != 'should_remove': continue
badge = '🔴' if a['severity'] == 'HIGH' else '🟡'
f.write(f'{badge} **#{a["number"]}**')
if a.get('title'): f.write(f' — {a["title"]}')
f.write(f'\n - Reason: {a["reason"]}\n\n')
# Type 2
if n_missing:
f.write(f'## Valid Issues Not in Changelog\n\n')
f.write('These issues are closed, non-excluded milestone items that lack a changelog entry.\n\n')
for a in anomalies:
if a['type'] != 'missing': continue
f.write(f'❓ **#{a["number"]}** — {a["title"]}\n')
f.write(f' - Type: {a["issue_type"]}, Closing PRs: {a["closing_prs"]}\n')
if a.get('note'): f.write(f' - Note: {a["note"]}\n')
f.write('\n')
# Type 3
if n_pr:
f.write(f'## Unmerged PRs Referenced in Changelog\n\n')
f.write('These PR numbers appear in the changelog but are not merged.\n\n')
for a in anomalies:
if a['type'] != 'unmerged_pr': continue
f.write(f'🔴 **#{a["number"]}**')
if a.get('title'): f.write(f' — {a["title"]}')
f.write(f'\n - {a["reason"]}\n\n')
# Appendix: counts
f.write('---\n\n')
f.write(f'## Context\n\n')
f.write(f'- Milestone total issues (all states): {len(all_issues)}\n')
f.write(f'- Valid issues after exclusions: {len(valid_issues)}\n')
f.write(f'- Issues referenced in changelog: {len(changelog_issues)}\n')
f.write(f'- PRs referenced in changelog: {len(changelog_prs)}\n')
print(f"Anomaly report written to {OUTPUT}")
PYEOF
```
This generates `CHANGES-ISSUES.md` with three sections:
1. **Issues to Remove** — Entries in the changelog that should be excluded based
on current issue metadata (labels, type, project status, or deletion).
2. **Valid Issues Not in Changelog** — Closed, non-excluded milestone issues
that lack a changelog entry (with notes on duplicates and unmerged closing PRs).
3. **Unmerged PRs Referenced** — PRs in the changelog that are not merged.
The report is overwritten each time it's generated, reflecting the current
state of the milestone and changelog.
## Key Principles
- **Issue = changelog unit.** The primary link always points to the
user-facing issue, not the implementation PR.
- **PR = implementation detail.** Reference the PR inline so readers
can find the code changes.
- **Latest version first.** New sections are inserted at the top of the
changelog, below the `# CHANGELOG` header.
- **Issue Type determines section — exclusively.** Use the `issue_type` field from `gh.py` output (Bug → `:bug:`, Feature/Enhancement → `:sparkles:`). **Do not** use labels (`bug`, `enhancement`) or title emoji prefixes (`:bug:`, `:sparkles:`) — they are frequently wrong or contradictory. The `issue_type` is the single source of truth.
- **User-facing descriptions.** Write from the user's perspective — describe
what broke and what was fixed, not internal implementation details.
- **Community attribution.** When the issue or fix PR has the
`community contribution` label, add `(by @<username>)` on the entry line
between the description and the issue link. Use the **PR author** (not the
issue author) for the attribution.
- **Only closed issues.** An issue must have `state: "closed"` to appear in
the changelog. Open/unresolved issues are omitted.
- **Rejected project status.** Issues marked as "Rejected" in the "Main"
project board are automatically excluded by `gh.py`, even if they are
closed. The project status is distinct from the GitHub issue state.
Use `--include-rejected` to override this behavior.
- **Excluded issues.** Issues with `no changelog` label must be excluded.
Issues with `issue_type: "Task"` must also be excluded — they are internal
chores, not user-facing changes.
- **Multiple PRs per issue.** If multiple PRs fix the same issue, list them
comma-separated inline: `(PR: [#A](url), [#B](url))`.
- **Duplicate removal.** If an entry already exists in a prior version section,
remove it from the current version. Check for text-level duplicates (after
stripping links and attributions) across version sections.
- **Taiga references.** If a changelog entry references a Taiga URL
(`tree.taiga.io`), attempt to find a corresponding GitHub issue via the
Taiga description text or by searching GitHub PRs that reference the Taiga
URL. Replace the Taiga reference with the GitHub issue link and add the PR
reference if applicable.
- **Re-fetch before editing.** Milestones can change — always re-fetch issues
before making edits, don't rely on cached data.
- **Use `tools/gh.py`.** Prefer the helper script over raw `gh api` calls for
milestone issue listing and PR detail fetching. It handles GraphQL
pagination, batching, and label filtering automatically.
- **Verify PR merge status.** Not all closing PRs are merged — community PRs
can be superseded and closed without merging. Always check that every PR
referenced in the changelog has `state: MERGED`.
- **PR-level exclusions apply.** A PR can carry its own exclusion labels
(`release blocker`, `no issue required`) independent of its linked issue's
labels. Check both.
- **Cross-reference milestone PRs, not just issues.** The `--compare` flag on
the `issues` command only compares issue numbers. Merged PRs not linked to
any milestone issue can be missed. Use `python3 tools/gh.py prs --milestone`
for a full PR cross-reference.
- **False-positive PR-to-issue associations.** A PR may claim to close an
issue from a different project or context. If the PR title and issue title
are clearly unrelated, or the PR predates the issue by years, treat it as a
data glitch and skip it.
+2
View File
@@ -0,0 +1,2 @@
/cache
/project.local.yml
@@ -0,0 +1,40 @@
# Backend Auth, Permissions, and Product Domain Subtleties
## Auth and sessions
- Main auth RPC commands live in `app.rpc.commands.auth`; LDAP and OIDC provider logic live in `app.auth.ldap` and `app.auth.oidc`, with LDAP-specific RPC checks in `app.rpc.commands.ldap`.
- Public auth endpoints must explicitly set `::rpc/auth false`; RPC auth defaults to enabled. Session cookie creation/deletion is usually attached as an RPC response transform.
- Basic Penpot registration is token staged: prepare/register creates or verifies temporary tokens, then profile creation/session setup is reused by other auth backends. The frontend `/auth/verify-token` flow is a hub for registration confirmation, email change, and invitation tokens.
- OIDC-compatible providers share a generic flow: redirect to provider, validate callback/request token, fetch identity data, then login an existing profile or register a new one. Known providers may have hardcoded endpoints; generic OIDC can use discovery/configured endpoints.
- LDAP login validates credentials against the external directory, fetches identity data, then logs in or registers a matching Penpot profile. LDAP registration is not a separate Penpot signup flow.
- Logout may return an OIDC provider redirect URI when the session claims include provider/session data and the provider has a logout URI.
- Invitation tokens are verified through token issuers and only accepted when the token member id/email matches the authenticated profile; otherwise login proceeds without consuming the invitation.
- HTTP/session parsing details such as cookie/header precedence, JWT session token versions, and SameSite behavior are in `mem:backend/http-storage-filedata-subtleties`.
## Permission model
- `app.rpc.permissions` provides predicate/check factories. Failed permission checks intentionally raise `:not-found` / `:object-not-found`, not an authorization-specific error, to avoid leaking object existence.
- Team role flags are normalized as owner > admin > editor > viewer. Owner/admin imply edit; any membership row implies read.
- File/project/comment checks are implemented in the owning command namespaces, often via helpers imported from `files`, `teams`, or `projects`; do not bypass those helpers with direct DB lookups unless preserving their not-found semantics.
- Comment permission includes both logged-in state and the file/team comment policy. Shared viewer paths may pass `share-id`; preserve that path when changing comment queries.
## Teams, projects, and invitations
- Team/project commands mix DB changes, email, message bus notifications, media/storage cleanup, feature flags, quotas, and audit metadata. Keep mutations transactional when the existing command does so.
- Invitation flows validate muted/bounced emails before sending and use tokenized invitation state. Accepting an invitation is tied to the invited member identity, not just possession of a token.
- Logical deletion is used for many product objects; prefer existing logical-deletion helpers over hard deletes unless the command already performs permanent cleanup.
- Bounced/spam-complaint emails can mute/block a profile for login/registration and email sending. Devenv MailCatcher is the normal local path for registration/email-flow testing.
## Comments, webhooks, and audit
- Comment thread queries join file/project/profile state and exclude deleted files/projects. Unread comment counts depend on `comment_thread_status.modified-at` and profile notification preferences.
- Webhook edits are allowed for team editors/admins or the webhook creator. Webhook validation performs a synchronous HEAD request with a short timeout; validation errors are mapped through `app.loggers.webhooks`.
- Audit events are prepared from RPC metadata, result metadata, params, request context, and selected auth identifiers. Webhook event batching can be controlled through audit/webhook metadata on commands or results.
- Webhook and audit logging are cross-cutting side effects of product commands; when adding a command, check nearby command metadata and result metadata patterns before inventing a new event shape.
## Local testing notes
- Enable LDAP login locally with frontend flag `enable-login-with-ldap`; the devenv includes a configured test LDAP service.
- OIDC testing requires external provider app credentials plus matching backend/frontend config.
- Backend domain tests usually live under `backend/test/backend_tests/rpc/commands/*_test.clj` or nearby backend test namespaces. Use focused `clojure -M:dev:test --focus ...` from `backend/` when possible.
- For auth/session or HTTP behavior, combine backend tests with the HTTP/session notes in `mem:backend/http-storage-filedata-subtleties` because RPC-level tests may not exercise cookie/header transforms.
+101
View File
@@ -0,0 +1,101 @@
# Backend Architecture and Workflow
Backend: JVM Clojure; Integrant; PostgreSQL; Redis/Valkey; RPC; HTTP; storage; mail; audit/logging; workers.
## Focused memories
- RPC, DB helpers, workers, cron: `mem:backend/rpc-db-worker-subtleties`
- HTTP sessions, config, storage, media, file data persistence: `mem:backend/http-storage-filedata-subtleties`
- Auth flows, permission model, teams, projects, invitations, comments, webhooks, audit: `mem:backend/auth-permissions-product-domains`
- Services, task-queue/Pub-Sub topology constraints -> `mem:prod-infra/core`.
## Stable namespace map
- `app.rpc.commands.*`: RPC command implementations exposed under `/api/rpc/command/<cmd-name>`.
- `app.rpc.permissions`: permission predicate/check helper factories.
- `app.http.*`: HTTP routes and middleware.
- `app.auth.*`: provider-specific authentication helpers such as LDAP/OIDC.
- `app.loggers.*`: audit, webhook, database, and external log integrations.
- `app.db.*` / `app.db`: next.jdbc wrapper and SQL helpers.
- `app.tasks.*`: background task handlers.
- `app.worker`: task execution/cron plumbing.
- `app.main`: Integrant system map and component wiring.
- `app.config`: `PENPOT_*` env config and feature flags.
- `app.srepl.*`: development REPL helpers for manual backend operations (data inspection, migration helpers, one-off admin tasks).
- `app.nitrate`, `app.rpc.commands.nitrate`, and `app.rpc.management.nitrate`: external Nitrate subscription/organization integration, gated by the `:nitrate` feature flag and shared-key HTTP calls.
## RPC conventions
RPC commands are defined with `app.util.services/defmethod` and schemas. Use `get-` prefixes for read operations. Command metadata usually includes auth, docs version, params schema, and result schema. Return plain maps/vectors or raise structured exceptions from `app.common.exceptions`.
Backend RPC command areas without focused memories include access tokens, binfile, demo, feedback, file snapshots, fonts, management, Nitrate, and webhooks beyond the notes in `mem:backend/auth-permissions-product-domains`; inspect nearby command tests and command metadata before changing them.
## DB conventions
`app.db` helpers accept cfg, pool, or conn in most places and convert kebab-case to snake_case:
- `db/get`, `db/get*`, `db/query`, `db/insert!`, `db/update!`, `db/delete!`.
- Use `db/run!` for multiple operations on one connection.
- Use `db/tx-run!` for transactions.
Database migrations live in `backend/src/app/migrations/`; pure SQL migrations are under `backend/src/app/migrations/sql/`. SQL filenames conventionally start with a sequence and verb/table description, e.g. `0026-mod-profile-table-add-is-active-field`. Applied migrations are tracked in the `migrations` table.
For deeper details on transaction semantics, advisory locks, Transit vs JSON helpers, and dev/test DB URLs: `mem:backend/rpc-db-worker-subtleties`.
## Background tasks
A task handler is an Integrant component with `ig/assert-key`, `ig/expand-key`, and `ig/init-key`, returning the function run by the worker. New tasks also need wiring in `app.main`: handler config, worker registry entry, and cron entry if scheduled.
For worker dispatch, cron, retry semantics, deduplication, and queue internals: `mem:backend/rpc-db-worker-subtleties`.
## REPL
In devenv, backend nREPL is exposed on port 6064.
### Non-interactive eval (preferred for agents)
`./tools/nrepl-eval.mjs` connects to an already-running nREPL server and evaluates code. Session state (defs, `in-ns`) persists across invocations via a stored session ID in `/tmp/penpot-nrepl-session-<host>-<port>`.
```bash
./tools/nrepl-eval.mjs '(+ 1 2)' # single expression
./tools/nrepl-eval.mjs "(require '[my.ns :as ns] :reload)" # reload after edits
./tools/nrepl-eval.mjs -e # inspect last exception (*e)
./tools/nrepl-eval.mjs --reset-session '(def x 0)' # discard session, start fresh
./tools/nrepl-eval.mjs <<'EOF' # multi-expression heredoc
(def x 10)
(+ x 20)
EOF
```
Default port is 6064. Use `-p <PORT>` for a different port. Use `-t <MS>` to override the 120s timeout. Do not start the nREPL server — assume it is already running.
### Interactive REPL
`backend/scripts/nrepl` starts a REPLy client connected to the running nREPL.
For an in-process backend REPL (where you control the JVM lifecycle), stop the running backend first so port 9090 is free, then run `backend/scripts/repl`. Useful top-level helpers include `(start)`, `(stop)`, `(restart)`, `(run-tests)`, and `(repl/refresh-all)`. Many `app.srepl.main` helpers accept the global `system` var, e.g. manual email or maintenance operations.
## Fixtures
Fixtures can populate local data for manual testing/perf work. From the backend REPL, run `(app.cli.fixtures/run {:preset :small})`; fixture users conventionally look like `profileN@example.com` with password `123123`. Standalone fixture aliases may exist, but check current `backend/deps.edn` before relying on old command names.
## Performance
* **Type Hinting:** Use explicit JVM type hints (e.g. `^String`, `^long`) in performance-critical paths to avoid reflection overhead.
* **Batch inserts:** Use `db/insert-many!` for bulk row inserts — generates a single SQL with multiple parameter tuples. Avoid on very large datasets (SQL length / parameter count limits).
* **Server-side cursors:** Use `db/plan` (fetch-size 1000, forward-only, read-only) or `db/cursor` for large result sets. Never fetch large collections into memory at once.
* **Transaction discipline:** Use `tx-run!` for writes (opens a transaction), `run!` for reads (single connection, no transaction). Set `:read-only` on `tx-run!` when applicable to let PostgreSQL optimize.
## Lint and Format
IMPORTANT: all CLI commands must be executed from the `backend/` subdirectory.
* **Linting:** `pnpm run lint` from the repository root.
* **Formatting:** `pnpm run check-fmt`. Use `pnpm run fmt` to fix. Avoid unrelated whitespace diffs.
## Testing
IMPORTANT: all CLI commands must be executed from the `backend/` subdirectory.
* **Coverage:** If code is added or modified in `src/`, corresponding tests in `test/backend_tests/` must be added or updated.
* **Isolated run:** `clojure -M:dev:test --focus backend-tests.my-ns-test` for a specific test namespace.
* **Regression run:** `clojure -M:dev:test` to ensure no regressions in related functional areas.
@@ -0,0 +1,31 @@
# Backend HTTP, Storage, Media, and File Data Subtleties
## Config and HTTP/session middleware
- `app.config/config` and `flags` are dynamic `defonce` vars populated from `PENPOT_*` env vars through the shared schema string transformer. Tests and tooling can bind them.
- `parse-flags` automatically adds `:disable-secure-session-cookies` when `public-uri` is plain HTTP and not localhost. This changes cookie defaults without an explicit env flag.
- The backend sets Clojure `*assert*` globally from the `:backend-asserts` feature flag. Assertion-dependent checks can therefore differ by runtime flags.
- Request body parsing is mostly POST-oriented and supports Transit JSON plus plain JSON. Plain JSON request keys are kebab-decoded before being merged into `:params`.
- Response formatting negotiates with `Accept` or `_fmt=json`. Transit is the default for collection/boolean bodies; JSON encoding has special pointer-map handling.
- Auth prefers the session cookie token before the `Authorization` header. Headers may be `Token` or `Bearer`; JWTs with `kid=1` and `ver=1` are decoded as v1 session tokens, otherwise they are treated as legacy tokens.
- Shared-key auth requires `x-shared-key` as `<key-id> <key>` and stores the lowercased key id on the request. If no shared keys are configured it always rejects.
- Session management uses DB storage unless the DB pool is read-only, then falls back to the in-memory manager. DB sessions support both legacy string ids and v2 UUID session ids.
- Session cookies are renewed when using a legacy string id or when `modified-at` is older than the renewal interval. SameSite is `none` for CORS, otherwise strict/lax based on config.
## Storage and media
- Storage has a fixed valid bucket set. Backends are `:fs` and `:s3`; default backend comes from deprecated `assets-storage-backend` only when present, otherwise `objects-storage-backend`, defaulting to `:fs`.
- `put-object!` creates the DB `storage_object` row before writing backend content. Backend writes happen only for newly created rows, so deduplication can skip object writes.
- Deduplication only applies when requested, when the content can provide a hash, and when bucket metadata is present. Reads exclude soft-deleted storage rows.
- `sto/resolve` can reuse the current DB connection via `::db/reuse-conn true`; preserve this in transaction-sensitive code.
- SVG validation strips DOCTYPE and uses secure SAX parsing. Basic SVG info falls back to 100x100 dimensions when width/height/viewBox are missing.
- Raster metadata is shell-derived with ImageMagick `identify`, verifies detected MIME against the supplied MIME, and swaps dimensions for EXIF orientations 6/8.
- Remote image download requires 2xx status, `content-length`, a known MIME, and size under the configured maximum before writing the temp file; mismatched byte count is an internal error.
- Font processing shells out to FontForge and WOFF conversion tools and can derive TTF/OTF/WOFF variants from uploaded fonts.
## File data persistence
- File data backends are `legacy-db`, `db`, and `storage`. The storage backend keeps encoded file data in storage bucket `file-data`; the DB row stores metadata with `storage-ref-id` and nil data.
- `fdata/upsert!` touches any storage object referenced by incoming metadata before storing the new row/blob.
- Pointer-map fragments are persisted separately as type `fragment`, and only modified pointer maps are written.
- `fdata/realize` combines pointer realization and object-map realization. Use it before operations that need complete in-memory file data instead of pointer placeholders.
@@ -0,0 +1,26 @@
# Backend RPC/DB/Worker Subtleties
## RPC exposure and wrappers
- RPC commands are discovered from vars created by `app.util.services/defmethod`; adding a command namespace is not enough unless `backend/src/app/rpc.clj` includes it in `resolve-methods`.
- `GET`/`HEAD` RPC calls are only allowed for method names starting with `get-`. Other methods are method-not-allowed even if they are read-only internally.
- RPC auth defaults to enabled. Public endpoints must set `::auth false` metadata explicitly.
- The wrapper stack does auth before params validation, then auditing/rate/concurrency/metrics/retry/condition handling, with DB transaction handling inside that stack. `::db/transaction` metadata controls transaction wrapping.
- Params with `::sm/params` are decoded/conformed through the JSON transformer and successful IObj results get `:encode/json` metadata. Legacy spec conforming only applies when no Malli params schema exists.
- Nil RPC bodies become HTTP 204 unless explicit status metadata is present. Stream bodies default to `application/octet-stream` when no content type is set.
## DB helpers
- Most `app.db` helpers accept a pool, connection, or map containing `::db/pool` / `::db/conn`; preserve that convention in shared code.
- `db/tx-run!` uses `next.jdbc.transaction/*nested-tx* :ignore`: nested transaction calls reuse the outer transaction, not a savepoint. Use explicit savepoints when nested rollback semantics matter.
- `db/run!` opens/reuses one connection but does not create a transaction.
- `db/tjson` is Transit JSON for jsonb storage; `db/json` is plain JSON. Worker task props use Transit and are decoded with `decode-transit-pgobject`.
- Advisory transaction locks accept UUIDs or ints. UUID locks are hashed using a zero-UUID seeded siphash.
## Workers and cron
- Task queues are tenant-prefixed. Submit dedupe only removes not-yet-due `new` tasks with the same name/queue/label; it does not dedupe due, scheduled, retry, running, or completed work.
- The dispatcher selects `new`/`retry` tasks with `FOR UPDATE SKIP LOCKED`, marks them `scheduled`, and publishes Redis payload `[id scheduled-at]`. The runner skips Redis messages whose scheduled timestamp no longer matches DB state.
- Lost `scheduled` tasks are rescheduled after about 5 minutes; `running` tasks older than about 24 hours are marked failed as orphans.
- A task handler that is missing or returns an invalid result currently defaults to completed after warning. Throwing with `ex-data :type ::retry` controls retry behavior; `:strategy ::noop` retries without incrementing retry count.
- Cron jobs lock their `scheduled_task` row with `FOR UPDATE SKIP LOCKED`, disable statement/idle-in-transaction timeouts locally, and reschedule themselves in `finally` unless interrupted. Worker, dispatcher, and cron components do not start when the DB pool is read-only.
@@ -0,0 +1,39 @@
# File Mutations: Changes and Undo Architecture
Penpot mutates file data through change records. A change set is both the persistence payload and the basis for undo/redo, so UI actions, tests, backend file updates, and library/file tooling should drive the production change pipeline instead of ad hoc object-map mutation.
## Change shape
Each change is a map such as `{:type ... :id ... :page-id ...}`. Common families:
- `:add-obj`, `:mod-obj`, `:del-obj`: shape lifecycle. `:mod-obj` contains `:operations`, commonly `{:type :set :attr ... :val ... :ignore-geometry ... :ignore-touched ...}` or `{:type :set-touched ...}`.
- `:add-component`, `:mod-component`, `:del-component`: component/library metadata.
- `:add-children`, `:remove-children`, `:reg-objects`: tree and object-map edits.
- `:set-option`, `:add-page`, `:mov-page`, and related file/page metadata changes.
Each transaction carries `:redo-changes` and inverse `:undo-changes`. The undo stack stores transactions and can move its index backward/forward.
## changes-builder API
`common/src/app/common/files/changes_builder.cljc` (usually alias `pcb`) is the fluent builder. Start from `(pcb/empty-changes <it> <page-id>)` or `(pcb/empty-changes nil <page-id>)` for tests.
High-value builder operations:
- `pcb/with-page-id`, `pcb/with-objects`, `pcb/with-library-data`: set context for following operations.
- `pcb/update-shapes ids update-fn`: emits `:mod-obj` with diff-derived `:set` ops. Options include `{:with-objects? true}`, `{:ignore-touched true}`, and `{:attrs #{...}}`.
- `pcb/add-objects`, `pcb/change-parent`, `pcb/remove-objects`, `pcb/resize-parents`: shape/tree edits.
- `pcb/add-component`, `pcb/update-component`, `pcb/mod-component`: component/library edits.
- `pcb/set-translation? true`: marks the whole change set as translation-only, which lets component sync skip expensive work.
## Applying changes in tests
`thf/apply-changes` in `app.common.test-helpers.files` is the test analog of the production applier. It validates by default; pass `:validate? false` only for intentionally-invalid intermediate states.
The applier uses the same `process-operation` multimethod as production (`common/src/app/common/files/changes.cljc`), so tests that use it exercise production behavior.
## :touched and geometry
For component touched semantics and sync groups, read `mem:common/component-data-model`. For the exact `set-shape-attr` / second-pass behavior during change application, read `mem:common/file-change-validation-migration-subtleties`. For transform-specific ignore-geometry behavior, read `mem:frontend/workspace-transform-subtleties`.
## Inspection
To inspect what a UI action emitted, use `mem:frontend/cljs-repl` with the snippets in `mem:common/component-debugging-recipes` rather than adding temporary source instrumentation.
@@ -0,0 +1,41 @@
# Component and Variant Data Model
## Shape roles relative to components
A shape can occupy multiple roles at once:
1. Master/main instance: defines a component and has `:main-instance true` plus `:component-id`.
2. Copy/non-main instance: produced by instantiating a component and carries `:shape-ref` pointing at the master shape. `(ctk/in-component-copy? shape)` is essentially `(some? (:shape-ref shape))`.
3. Component root: topmost shape of an instance, marked `:component-root true` and carrying surface attrs such as `:component-id` and `:component-file`.
Variant masters are main instances and component roots. Their descendants may themselves be component copies, so master/copy logic must handle nested instances rather than assuming those roles are exclusive.
## :shape-ref chains
`:shape-ref` walks up the inheritance hierarchy and can cross files for remote libraries. `find-ref-shape` and `get-ref-chain-until-target-ref` in `app.common.types.file` follow this chain.
`find-shape-ref-child-of` in `app.common.logic.variants` walks the chain looking for the first ref-shape whose ancestors include a specific parent. Variant switch uses this to locate the equivalent master child in the target variant.
## :touched flags
`:touched` is a set of override-group keywords such as `:geometry-group`, `:fill-group`, and `:text-content-group`. It means a copy diverged from its master for attrs in that sync group.
`sync-attrs` in `app.common.types.component` maps attrs to groups. `set-touched-group` is the legitimate setter; the central `set-shape-attr` path calls it only for copies and only when ignore flags allow it.
Masters are not normally touched through `set-shape-attr`, but touched flags can appear on master shapes through cloning/duplication paths. `add-touched-from-ref-chain` in `app.common.logic.variants` unions touched flags from ancestors into the copy being processed, so upstream/master touched state can affect downstream switch behavior.
## Cloning paths
`make-component-instance` in `app.common.types.container` produces a clean component copy through `update-new-shape`, dissociating attrs such as `:touched`, `:variant-id`, and `:variant-name` on cloned shapes.
`duplicate-component` in `app.common.logic.libraries` creates a new component master by cloning existing component shapes, setting component metadata, and applying a position delta. It does not have the same clean-copy semantics as `make-component-instance`, so inherited attrs on the source can matter.
When a bug depends on touched state, identify which cloning path produced the shape before changing sync logic.
## Variant containers
A variant container is a frame with `:is-variant-container true`. Its children are variant masters with `:variant-id` pointing at the container and `:variant-name` naming the variant value. Component records in the library carry `:variant-properties`.
Predicates are broad: `ctk/is-variant?` checks `:variant-id` and applies to both variant master shapes and component rows; `ctk/is-variant-container?` checks the container shape flag.
Moving/dropping a shape into a variant container through the move-to-frame path can auto-convert it into a variant via `generate-make-shapes-variant`, which may duplicate the underlying component. Treat drag/drop into variant containers as a component/variant operation, not a plain reparent.
@@ -0,0 +1,58 @@
# Common Component and Change Debugging Recipes
Keep source changes out of these recipes unless the task requires a durable fix.
## Inspect recent workspace changes
From `cljs_repl` after triggering an action:
```clojure
(let [items (get-in @app.main.store/state [:workspace-undo :items])
n (count items)]
(->> items
(drop (max 0 (- n 5)))
(map-indexed (fn [i it]
{:idx (+ i (max 0 (- n 5)))
:tags (:tags it)
:n (count (:redo-changes it))
:types (frequencies (map :type (:redo-changes it)))
:ids (mapv :id (:redo-changes it))}))))
```
To inspect operations within the latest `:mod-obj`:
```clojure
(let [items (get-in @app.main.store/state [:workspace-undo :items])
mod-obj (->> (:redo-changes (last items))
(filter #(= :mod-obj (:type %)))
first)]
(:operations mod-obj))
```
## Trace variant switch attribute copying
To capture what `update-attrs-on-switch` saw during a real UI swap, patch it temporarily in `cljs_repl`:
```clojure
(def orig (deref #'app.common.logic.libraries/update-attrs-on-switch))
(def trace-buf (atom []))
(set! app.common.logic.libraries/update-attrs-on-switch
(fn [& args]
(swap! trace-buf conj
(let [[_ curr prev _ _ origin _] args]
{:curr (select-keys curr [:name :x :y :selrect :points :touched])
:prev (select-keys prev [:name :x :y :selrect :points :touched])
:origin-ref (select-keys origin [:id :name :x :y :width :height :selrect])}))
(apply orig args)))
;; trigger UI action, then inspect @trace-buf
(set! app.common.logic.libraries/update-attrs-on-switch orig)
```
Runtime patching is faster than adding temporary source instrumentation and avoids recompilation cleanup. Restore the var or reload the frontend when finished.
## Test-side helpers
- Use `thf/dump-file file :keys [...]` to print a shape tree with selected keys during common tests.
- Prefer production-path helpers such as `cls/generate-update-shapes` plus `thf/apply-changes` for shape mutations.
- For component swaps with keep-touched behavior, use `tho/swap-component-in-shape` with `{:keep-touched? true}`.
- Temporary `prn` calls in production code are acceptable while investigating but should be removed before committing.
@@ -0,0 +1,42 @@
# Component Swap and Variant Switch Pipeline
## Entry points
Frontend entry points under `frontend/src/app/main/data/workspace/`:
- `variants.cljs`: `variants-switch` and `variant-switch` events feed property-toggle UI and Plugin API `switchVariant` behavior into `dwl/component-swap`.
- `libraries.cljs`: `component-swap` is the single-swap workhorse; `component-multi-swap` batches swaps and calls `component-swap` with `keep-touched? = false`.
`keep-touched? = true` is the discriminator for preserving user overrides during variant switch. Batch/multi-swap paths intentionally bypass that logic.
## Common-side pipeline
For a single swap with `keep-touched? = true`:
1. `cll/generate-component-swap` in `common/src/app/common/logic/libraries.cljc` builds the base changes: remove old shape and instantiate the target component in its place through `generate-new-shape-for-swap`, `generate-instantiate-component`, and `make-component-instance`.
2. `clv/generate-keep-touched` in `common/src/app/common/logic/variants.cljc` walks pre-swap children, augments each with chain-derived touched flags through `add-touched-from-ref-chain`, finds the equivalent target shape through `find-shape-ref-child-of`, then calls `update-attrs-on-switch`.
3. `update-attrs-on-switch` in `app.common.logic.libraries` decides which touched attrs from the previous shape should be copied onto the freshly instantiated target shape.
## update-attrs-on-switch hazards
The routine compares `current-shape` (fresh target copy), `previous-shape` (pre-swap shape with chain-derived touched), and `origin-ref-shape` (source variant master's equivalent shape). It loops over sync attrs except `swap-keep-attrs` and copies only attrs that pass several guards:
- skip equal previous/current values;
- skip equal composite geometry for selected attrs;
- require the corresponding touched group;
- for most attrs, require source and target masters to agree;
- for fixed-size selrect/points/width/height, use dedicated fixed-layout geometry handling;
- text and path shapes have specialized value conversion paths.
The generic fallback branch copies from `previous-shape`. It represents the intended "carry user override through switch" behavior, but bugs usually appear when guards fail to reject incompatible geometry or master differences before reaching that branch.
## Known sharp edges
- Composite `:selrect` and `:points` bypass the simple different-master skip; width/height checks catch some but not all positional differences.
- `previous-shape` may be repositioned by destination-root minus origin-root before copying. For normal variant switch this is often zero, but do not assume it for all swap entry points.
- Touched flags can be inherited through ref chains, so a shape that looks untouched locally may still behave as touched after `add-touched-from-ref-chain`.
## Test harness
`common/src/app/common/test_helpers/compositions.cljc` has `swap-component-in-shape`, which drives `generate-component-swap` plus `generate-keep-touched` with the production `keep-touched?` flag. Use it for focused common tests of variant-switch behavior.
`common/test/common_tests/logic/variants_switch_test.cljc` is the canonical reference suite for swap+touched scenarios. Read nearby tests before adding another case.
+55
View File
@@ -0,0 +1,55 @@
# Common Architecture and Workflow
`common/` intro: shared CLJC for frontend, backend, exporter, library/file tooling, tests. Small semantic changes can affect multiple runtimes.
## Stable namespace map
- `app.common.data` and `app.common.data.macros`: generic data helpers and performance macros that do not depend on Penpot domain entities.
- `app.common.types.*`: shared shape/file/page/component/token data types, schemas, predicates, and entity-local operations. `app.common.types.nitrate-permissions` contains shared fail-closed Nitrate organization/team permission rules.
- `app.common.files.*`: file-level operations, shape tree helpers, change application, migrations, validation, and undo/redo-related logic.
- `app.common.logic.*`: higher-level workflows/algorithms over files, shapes, components, variants, libraries, tokens, etc.
- `app.common.geom.*`: geometry helpers and transformations.
- `app.common.schema` / `app.common.schema.*`: Malli abstraction layer.
- `app.common.math`, `app.common.time`, `app.common.uuid`, `app.common.json`, etc.: cross-runtime utilities.
- `app.common.test_helpers.*`: test builders and production-path helpers.
## Layering and cross-runtime rules
Use reader conditionals for platform-specific code. Because CLJC runs on JVM and CLJS targets, avoid assuming browser-only or JVM-only behavior unless the reader conditional isolates it.
Respect the intended abstraction direction in new/refactored code:
- generic data utilities should not know Penpot domain concepts;
- `types.*` should preserve invariants for a single domain entity or ADT;
- `files.*` can coordinate several entities inside a file and preserve referential integrity;
- `changes*` should adapt serializable change records to lower-level operations and avoid embedding broad business algorithms;
- `logic.*` and frontend/backend event layers own higher workflow/business behavior.
Some legacy code violates this layering; do not copy those violations into new code when a focused refactor is practical.
## Focused memory routing
Model, schema, and persistence shape:
- File/page/shape/component attr changes, import/export surfaces, inspector/codegen, and cross-module checklist: `mem:common/data-model-change-checklist`.
- Token data structures, token import/export, active theme/set semantics, and schema/coercion behavior: `mem:common/tokens-schema-subtleties`.
Geometry and layout:
- Shape geometry invariants, redundant geometry fields, and geometry-sensitive tests: `mem:common/geometry-invariants`.
- Coordinate drift and approximate float comparisons: `mem:common/decimals-and-coordinates`.
- Layout/grid assignment, deassignment, metadata cleanup, and auto-positioning: `mem:common/layout-grid-subtleties`.
Change pipeline, validation, and migrations:
- Change records, undo/redo architecture, changes-builder API, and production-path mutation guidance: `mem:common/changes-architecture`.
- Change application, shape-tree edits, validation/repair, migrations, and second-pass touched behavior: `mem:common/file-change-validation-migration-subtleties`.
Components, variants, and debugging:
- Component/variant data model, ref chains, touched override semantics, and cloning paths: `mem:common/component-data-model`.
- Component swap, variant switch, and keep-touched pipeline: `mem:common/component-swap-pipeline`.
- Live inspection snippets, temporary runtime patching, and test-side debugging helpers for common change/component behavior: `mem:common/component-debugging-recipes`.
Text and tests:
- Shared text data conversion, DraftJS compatibility, modern text content, and derived position data: `mem:common/text-subtleties`.
- Common test commands, helper conventions, production-path test mutations, and runtime coverage choices: `mem:common/test-setup`.
## Areas without focused memories
Common areas with little or no dedicated memory include colors, media/SVG helpers, path operations, thumbnail helpers, generic pools, weak refs, and some utility namespaces. Treat work there as source/test-led unless a focused memory exists.
@@ -0,0 +1,25 @@
# Common Data Model Change Checklist
## Attribute conventions
- Prefer optional page/shape attrs with default behavior when absent. Reverting to default should usually remove the attr instead of storing nil.
- Do not treat nil as a distinct persisted state from absence. Import/export and cleanup paths may filter nil attrs away.
- Avoid Clojure-special naming in exported object attrs, especially boolean names ending in `?`; exported/imported data must survive JSON/SVG/Transit and external tooling.
- Any new shape attr that participates in component sync must be listed in `app.common.types.component/sync-attrs` with the correct touched group. Attrs absent from `sync-attrs` are ignored by component synchronization.
## Cross-module update checklist
When changing the file data model, check the relevant paths:
- Schema/type definitions under `common/src/app/common/types*` and helpers under `common/src/app/common/files*` / `logic*`.
- File migrations in `common/src/app/common/files/migrations.cljc` when old files cannot safely use absence/default behavior.
- Frontend edit forms under `frontend/src/app/main/ui/workspace/sidebar/options/`; multi-selection behavior is usually in `multiple.cljs` and must handle `:multiple` values.
- SVG/file render and export metadata under `frontend/src/app/main/ui/shapes/*`, especially `export.cljs` when an attr is not a native SVG property.
- SVG import/parser paths under `frontend/src/app/worker/import/parser.cljs`; attrs not exported and imported will be lost on reimport.
- Viewer inspect and code generation under `frontend/src/app/main/ui/viewer/inspect/*` and `frontend/src/app/util/code_gen.cljs` / markup/style helpers when handoff output should expose the attr.
- Exporter/library consumers when the change affects file construction, rendering, or packaged `.penpot` archives.
## Migrations
Existing files should keep working unchanged when possible. If absence cannot preserve old behavior, add a migration and preserve append/order semantics described in `mem:common/file-change-validation-migration-subtleties`.
Model changes can also require file feature flags or migration metadata updates; check nearby migrations and `common/src/app/common/features.cljc` before inventing a new pattern.
@@ -0,0 +1,54 @@
# Decimals and Coordinates in Penpot
Penpot stores all geometry as JS numbers (doubles in CLJS, doubles in
CLJ for the JVM-side common code). Several Penpot-specific facts
about how this plays out are not obvious from reading the code.
## Sub-pixel drift is routine
Coordinate values that "should" be integers are routinely off by ~1e-5
in production data. A `:width` of 107 will frequently appear as
`107.00001275539398` after the value has passed through:
- the modifier propagation pipeline (`apply-wasm-modifiers` and the
Rust WASM transform engine)
- any rotation/scale composition
- repeated translations
This drift is invisible in the UI (the renderer rounds at draw time)
but defeats exact equality comparisons in business logic. It does NOT
appear in JVM-only test setups because the WASM pipeline isn't
involved — tests that build shapes via `setup-shape` and `add-sample-shape`
get clean integer values. Bugs that depend on drift will pass tests
but fire in production unless tests explicitly inject drift.
## Use the close? helpers, not `=`
For comparing coordinate-like floats, the established convention is:
- `app.common.math/close?` — scalar tolerance comparison.
Default precision 0.001 (sub-pixel; tight enough to keep distinct
shapes distinct, loose enough to absorb arithmetic noise).
Two-arity uses default precision; three-arity takes a custom one.
- `app.common.geom.point/close?` — element-wise close on `gpt/Point`
records. Compares :x and :y via `mth/close?`.
- `app.common.geom.matrix/close?` — close on transform matrices.
- `app.common.geom.shapes/close-attrs?` — used inside `set-shape-attr`
to decide whether a re-assigned `:width`/`:height` should be treated
as a no-op (suppresses spurious touched marking from drift).
Treat `=` on `:x`, `:y`, `:width`, `:height`, `:selrect`, or `:points`
fields as a code smell when the inputs may have flowed through any
transform. The `set-shape-attr`-style precedent (already using
`close-attrs?`) is the right model.
## The redundancy multiplies failure modes
A shape's position lives in `:x/:y`, `:selrect`, AND `:points` (see
`mem:common/geometry-invariants` memory). Each is a separate set of float
values. After any operation that touches geometry, all three should
agree, but each is computed by a different path and accumulates
its own drift. Comparing `:selrect.width` from shape A to
`:selrect.width` from shape B is comparing two values that
"semantically" should be equal but were computed via different
operation chains — exact equality will often be false.
@@ -0,0 +1,28 @@
# Common File Change, Validation, and Migration Subtleties
## Change application
- `process-changes` validates the whole change vector once by default, reduces changes, then performs a second pass for collected touched changes. Callers that already validated can pass `verify? false`.
- `process-operation :set` delegates to `ctn/set-shape-attr`; `:assign` first decodes attrs with the shape-attrs JSON transformer and then emits per-attr set operations.
- `set-shape-attr` treats `:position-data` as derived and never touched. Geometry/content-path changes use approximate equality; geometry differences under about 1px can be ignored for touched purposes.
- Width/height are excluded from the `is-geometry?` branch in `set-shape-attr`; do not assume all geometry-group attrs follow identical ignore-geometry behavior.
- `process-touched-change` marks the owning component modified when a touched shape belongs to a main instance; component-data changes can come from shape ops through this second pass.
## Shape tree edits
- `shape-tree/add-shape` falls back invalid/missing parent or frame ids to root (`uuid/zero`), ensures parent `:shapes` is a vector, avoids duplicate child ids, and clears `:remote-synced` on copy parents unless `ignore-touched` is true.
- `shape-tree/delete-shape` removes the shape and all descendants from the objects map and removes the id from its parent. This is different from render-wasm deletion, which may keep deleted children for undo/redo internals.
- Page object maps can carry metadata indexes such as cached frame lists. `start-page-index` / `update-page-index` rebuild those metadata indexes; `frontend` commit application calls `ctst/update-object-indices` after page changes.
## Validation and repair
- Full referential/semantic validation currently runs only when file features contain `"components/v2"`.
- Validation starts at root plus orphan shapes, then validates component records. `validate-file!` raises `:validation :referential-integrity` with collected details.
- `repair-file` does not mutate data directly; it reduces validation errors into redo changes using `changes-builder`. Callers must apply or persist those changes.
## Migrations
- Prefer optional attrs/default behavior so old files continue working without migration. If absence cannot preserve old behavior, add a migration.
- Migrations are an ordered set mixing legacy version-derived ids and newer named ids. Keep append order stable; `migrate` applies the set difference between available migrations and file migrations.
- `migrate-file` synthesizes legacy migration ids from old numeric versions when `:migrations` is absent, migrates legacy features, and records feature flags created through `cfeat/*new*`.
- When a file had no previous `:migrations`, `migrate-file` marks all migrations as migrated in metadata so callers persist the complete migration set, not only transformations that changed data.
@@ -0,0 +1,48 @@
# Geometry Invariants in Penpot Shapes
Core invariant: shape position is stored redundantly, and all geometry fields must stay coherent.
## Redundant fields
For a shape at `(x, y)` with width `w` and height `h`:
- `:x`, `:y`, `:width`, `:height`: top-left and dimensions.
- `:selrect`: `{:x :y :width :height :x1 :y1 :x2 :y2}`, where `x2 = x + w` and `y2 = y + h`.
- `:points`: four corners for an axis-aligned rect, clockwise from top-left.
- `:transform` and `:transform-inverse`: identity for axis-aligned shapes; populated for transformed shapes.
After a geometric mutation, equivalent fields such as `:y`, `(:y :selrect)`, and the first point's `:y` should agree. The renderer and hit-testing read `:selrect` / `:points`, so a shape can render or select incorrectly even when `:x` / `:y` look right.
## Helpers that preserve the invariant
- `gsh/move`: translates by delta and updates geometry consistently.
- `gsh/absolute-move`: moves to an absolute position by computing a delta from the current selrect.
- `gsh/transform-shape`: applies a full transform.
- `cts/setup-shape`: initializes geometry for new shapes; variant test helpers such as `thv/add-variant-with-child` use it.
## Edits that break the invariant
- `(assoc shape :x ...)` or `(assoc shape :y ...)`: updates only one field and leaves `:selrect` / `:points` stale.
- `ths/update-shape file label :y val`: goes through `set-shape-attr`, but does not repair all position fields for `:y` alone.
- Direct `update-in` edits to `:selrect`, `:points`, or dimensions.
## Test setup warning
When positioning test shapes, use `gsh/absolute-move`, `gsh/move`, or production change helpers. Do not set only `:x` / `:y`.
```clojure
(cls/generate-update-shapes
(pcb/empty-changes nil page-id)
#{(:id child)}
#(gsh/absolute-move % (gpt/point (:x %) 101))
(:objects page)
{})
```
Using `(ths/update-shape file label :y 101)` leaves `:selrect.y` stale. Downstream code that reads `:selrect` can then fail in ways that look like product bugs but are only invalid test setup.
## :touched and geometry mutation
When a copy shape changes geometry through the proper pipeline (`set-shape-attr` via `process-operation :set`), `:touched` gains `:geometry-group` unless ignored. Tests can either drive the production update with `cls/generate-update-shapes`, or inject `(assoc shape :touched #{:geometry-group})` when only touched state matters.
If a test needs both a new position and touched state, move the shape first with geometry-preserving helpers, then inject or assert touched state.
@@ -0,0 +1,13 @@
# Common Layout and Grid Subtleties
## Layout metadata
- Layout container data and child layout-item data are removed by different helpers. Do not assume clearing a layout frame also clears all child layout metadata.
- Layout data can affect both container attrs and immediate child attrs; validate behavior for both sides when changing cleanup or propagation.
## Grid assignment
- Grid `assign-cells` ensures at least one column and row, skips absolute-position children, creates non-tracked rows/cols when children exceed tracked cells, and asserts that assigned cells do not overlap.
- Grid deassignment removes cells for shapes that are no longer direct children or have become absolute-positioned.
- Auto-positioning is not just sorting: some auto cells are converted to manual when empty/manual/span state would break the auto sequence, then auto single-span items can be compacted.
- `fix-overlaps` is marked dev-only and removes one overlapping cell, preferring empty cells first. Avoid depending on it as normal production repair.
+48
View File
@@ -0,0 +1,48 @@
# Common Module Test Setup
`common/` is CLJC shared code. Tests should cover the relevant runtime(s): JVM for backend/common logic and JS for frontend/exporter behavior. For geometry, component, and file-model changes, JVM tests are common and fast, but JS/browser behavior can differ when WASM modifier math or CLJS-specific state is involved.
## Running tests
From `common/`:
```bash
pnpm run test:jvm
clojure -M:dev:test
pnpm run test:jvm --focus common-tests.logic.variants-switch-test
clojure -M:dev:test --focus common-tests.logic.variants-switch-test/test-basic-switch
pnpm run test:js
pnpm run test:quiet
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute --log-level warn
pnpm run watch:test
```
Use `test:quiet` for non-interactive JS runs; it buffers `build:test` output and forwards runner args. Common JS runner args support `--focus <namespace-or-var>` and `--log-level trace|debug|info|warn|error`. After `pnpm run build:test`, direct compiled runner focus is faster: `node target/tests/test.js --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute --log-level warn`. New common JS test namespaces must be required/listed in `common_tests/runner.cljc`; new vars in existing namespaces need no runner change. Multiple JVM `--focus` flags compose as a union.
## Test helpers
Helpers live under `common/src/app/common/test_helpers/` and are usually aliased with short `th*` prefixes. Test namespaces using label->uuid helpers should start with `(t/use-fixtures :each thi/test-fixture)` so labels reset between tests.
Useful builders:
- `thf/sample-file` creates a base file.
- `tho/add-simple-component` creates a simple component.
- `thc/instantiate-component` instantiates a component copy.
- `thv/add-variant-with-child` creates a variant container with two child variants.
- `thv/add-variant-with-copy` creates variants whose children are component instances.
`add-variant-with-copy` does not accept position params for children; use `gsh/absolute-move` after creation if positions matter.
## Driving production paths
For shape mutations, prefer production-path helpers such as `cls/generate-update-shapes` plus `thf/apply-changes`. For component swaps with keep-touched behavior, use `tho/swap-component-in-shape` with `{:keep-touched? true}`.
`thf/apply-changes` validates by default and usually gives the most useful invariant failure. Pass `:validate? false` only for intentionally malformed intermediate state.
## Geometry setup caution
For geometry-sensitive tests, read `mem:common/geometry-invariants` before positioning shapes. Use geometry-preserving helpers or production change helpers rather than direct single-field edits.
## Debugging
Use `mem:common/component-debugging-recipes` for shape-tree dumps, undo/change inspection, and temporary live instrumentation recipes.
@@ -0,0 +1,14 @@
# Common Text Subtleties
## DraftJS compatibility
- `app.common.text` is legacy DraftJS conversion support. New text work should prefer the newer text type namespaces unless specifically touching DraftJS conversion.
- DraftJS style values are encoded as Transit strings under `PENPOT$$$<key>$$$<encoded>` style names. `PENPOT_SELECTION` is a special marker.
- Text conversion uses Unicode code points on both CLJ and CLJS paths, not UTF-16 code units. This matters for offsets around emoji and astral characters.
- Draft conversion fixes gradient type strings back to keywords.
## Modern text content
- Modern text content schema is narrow: root -> paragraph-set -> paragraph -> text nodes.
- `position-data` is derived layout/geometry/font fragment data and should be treated as generated state, not source-of-truth file data.
- Token propagation and some non-current-page text updates drop `:position-data` so it can be regenerated in the right runtime context.
@@ -0,0 +1,17 @@
# Common Tokens and Schema Subtleties
## Tokens
- `TokensLib` always ensures an internal hidden theme exists and defaults active themes to that hidden theme path. That hidden theme represents the UI state where active sets are controlled without modifying a user-created theme.
- `get-tokens-in-active-sets` merges tokens from sets selected by active themes in set order, so later active sets with the same token name override earlier ones.
- Activating a real theme in `common.logic.tokens` removes the hidden theme from the active-theme set unless it is the only active theme. Toggling active sets directly copies current active sets into the hidden theme.
- DTCG import/export deliberately hides the internal hidden theme: exports omit it from `$themes` and activeThemes, while `activeSets` records the hidden/current active sets.
- Single-set DTCG/legacy imports throw if no supported tokens are found. Multi-set import normalizes set names, keeps `tokenSetOrder`, rejects conflicting token path names, discards unsupported token types, and validates theme sets against existing sets.
- Token values stored on shapes are token names in `:applied-tokens`, not token ids. Renames and group renames must update those name paths.
- Token serialization has both Transit handlers for frontend/backend transport and Fressian handlers for internal file-data storage, with migrations for older token-lib internal versions.
## Schema
- `app.common.schema/json-transformer` has custom map-of key decoding/encoding, so map keys can be transformed based on the key schema instead of only the value schema.
- `check-fn` throws `ex-info` with default `:type :assertion`, `:code :data-validation`, and `::explain`. Prefer reusable `check-fn`/lazy validators in hot or repeated paths; `sm/check` creates a checker every call.
- `coercer` decodes with the JSON transformer and then checks. This is the common pattern for accepting external JSON-shaped data into internal types.
+62
View File
@@ -0,0 +1,62 @@
You are working on the GitHub project `penpot/penpot`, a monorepo.
# Memory system
- Memories are the primary project guidance (not docs or other readme files).
- A section's top-level memory is `<section>/core`. When a section is relevant, read the core memory
before focused memories.
- Edits/stale refs/duplication cleanup: `mem:memory-maintenance`.
# Development workflow
- Commit only when explicitly asked. Commit/PR format + changelog: `mem:workflow/creating-commits`, `mem:workflow/creating-prs`. Issue creation (titles, labels, body templates, Issue Types): `mem:workflow/creating-issues`.
- You have access to the GitHub CLI `gh` or corresponding MCP tools.
- Issues are also managed on Taiga. Read issues using the `read_taiga_issue` tool.
- Before writing code, analyze the task in depth and describe your plan. If the task is complex, break it down into atomic steps.
*After making changes, run the applicable lint and format checks for the affected module before considering the work done (per example `mem:backend/core` or `mem:frontend/core`).
- Never run anything that destroys data without explicit permission, including `drop-devenv`, `docker compose down -v`, `docker volume rm ...`. The user's real work lives in the volumes of the shared infra.
# Project modules
This is a monorepo. Principles that apply to one module do *not* generally apply to others. Do not make assumptions.
- `frontend/`: ClojureScript + SCSS SPA/design editor.
- `backend/`: JVM Clojure HTTP/RPC server with PostgreSQL, Redis, storage, mail, and workers.Runtime services and the task-queue vs Pub/Sub topology that constrains horizontal scaling: `mem:prod-infra/core`.
- `common/`: shared CLJC data types, geometry, schemas, file/change logic, and utilities.
- `render-wasm/`: Rust -> WebAssembly Skia renderer consumed by frontend.
- `exporter/`: ClojureScript/Node headless Playwright SVG/PDF export.
- `mcp/`: TypeScript Model Context Protocol integration.
- `plugins/`: TypeScript plugin runtime/examples and Plugin API types.
- `library/`: design library workflows.
- `docs/`: documentation site.
The memory is structured in a way that you can get the critical information about the
module. You can read it from `mem:<MODULE>/core`
# Low-centrality project paths
- `docker/` contains devenv related code, not needed unless specifically instructed.
When working on devenv startup, compose layout, instance config (`defaults.env`),
tmux session lifecycle, MinIO provisioning, or anything in `manage.sh`'s
`*-devenv` commands, read `mem:devenv/core`.
- `experiments/` contains standalone experimental HTML/JS/scripts; treat it as non-core unless the user explicitly asks about it.
- `sample_media/` contains sample image/icon media and config used as fixtures/demo material; do not infer app behavior from it.
# Dependency graph
`frontend -> common`, `backend -> common`, `exporter -> common`, and `frontend -> render-wasm`. Changes in `common` can
affect frontend, backend, exporter, file migrations, and design-library behavior; validate across consumers when
semantics change.
# Working with Penpot designs
- Before automating or inspecting Penpot designs through the Plugin API, call the Penpot MCP `high_level_overview` tool.
- connection between the JavaScript plugin API and the ClojureScript code: `mem:frontend/plugin-api-to-cljs-binding`.
- executing ClojureScript code in the frontend: `mem:frontend/cljs-repl`.
- handling Clojure compiler errors, runtime patching and debug helpers: `mem:frontend/handling-errors-and-debugging`.
## Detecting Crashes
The Penpot frontend can crash silently from the JS API's perspective: `execute_code` calls return successfully, but 1-2s later the workspace becomes unusable (Internal Error page).
The `execute_code` tool then stops working, but `cljs_repl` still works. Use it to detect a crash via `(some? (:exception @app.main.store/state))`.
For details on handling crashes, read memory `mem:frontend/handling-crashes`.
+73
View File
@@ -0,0 +1,73 @@
# Devenv startup and configuration
Compose-based dev environment under `docker/devenv/`, driven by `manage.sh`. Parallel instances share infra + Postgres + MinIO; each instance has its own `main` container, Valkey, source checkout, tmux session.
## Compose project layout
- `penpotdev-infra`: shared `postgres`, `minio`, `minio-setup`, `mailer`, `ldap`. File: `docker-compose.infra.yml`.
- `penpotdev-wsN` (N=0,1,…): per-instance `main` + `redis` (Valkey). File: `docker-compose.main.yml`. ws0 (a.k.a. `main`) binds `$PWD`; ws1+ bind clones at `${PENPOT_WORKSPACES_DIR}/wsN/` (default `~/.penpot/penpot_workspaces/`), maintained by the developer.
- All projects join external network `penpot_shared`. Created idempotently by `ensure-devenv-network`, never removed by lifecycle commands.
## Source-of-truth files
- `docker/devenv/defaults.env`: ws0 baseline — container/volume names, runtime env, published host ports, tmux defaults. `manage.sh` aborts if unreadable.
- For ws1+, `instance-env-overrides` computes the per-instance overrides (container/volume names, host ports offset `10000·N`, `PENPOT_PUBLIC_URI`, `PENPOT_REDIS_URI`, `PENPOT_BACKEND_WORKER=false`) and `instance-compose` injects them as env vars at compose time — never written to disk, recomputed each call so they can't drift. ws0 uses `defaults.env` as-is.
- `backend/scripts/_env`: backend-internal only — secret keys, `PENPOT_FLAGS` (with `enable-backend-worker` gated on `PENPOT_BACKEND_WORKER`), `JAVA_OPTS`, `setup_minio()`. Never duplicates `defaults.env`.
- Compose files use pure `${VAR}` substitution; missing var = compose fails.
## Invariants
- `infra-compose` / `instance-compose` wrap `docker compose` with `env -i`, then re-inject what compose needs. Stripping is required because `defaults.env` is sourced into manage.sh's shell at startup (stale values would leak); the ws1+ overrides are deliberately re-injected as shell env vars precisely because Compose gives shell precedence over `--env-file`, so they override the `defaults.env` baseline.
- Volume names pinned via `name:` (PENPOT_*_VOLUME), decoupled from the compose project name. ws1+ inject distinct per-instance volume names; ws0 keeps the historical `penpotdev_*` physical names so project renames never require data migration.
- Network aliases (`- main`, `- redis`) are not declared in main.yml. Compose's auto-service-alias still registers `redis` on the shared network, so DNS for `redis` is non-deterministic with multiple instances. Backend uses `PENPOT_REDIS_URI=redis://penpot-devenv-wsN-valkey/0` (container_name) instead.
- No cross-project `depends_on`. `manage.sh ensure-infra-up` `docker wait`s on the `minio-setup` one-shot.
- `JAVA_OPTS` in `manage.sh` is shadowed inside the container by `_env`. The `-e JAVA_OPTS=...` flag only matters for processes that don't source `_env`.
## Worker policy
Backend workers run only on ws0. `_env` gates `enable-backend-worker` on `PENPOT_BACKEND_WORKER`; ws1+ inject it as false. ws0 must be running whenever any ws1+ is running, and is the last instance to stop — `run-devenv-agentic --ws N` (N≥1) auto-starts ws0 first; `stop-devenv` refuses to stop ws0 while any ws1+ is up. Workers are pure fire-and-forget: `wrk/submit!` inserts a row into the shared Postgres `task` table and returns; RPC handlers never wait on completion and workers never publish to msgbus. The reason for "ws0 only" is avoiding multi-instance worker races (cron dedup is best-effort across instances, `wrk/submit!` `dedupe` is racy across submitters); details in `mem:prod-infra/core`.
## Port layout
Container-internal ports fixed; host side offset `10000·N`.
| ws0 | ws1 | wsN | container | role |
|---|---|---|---|---|
| 3449 | 13449 | 3449+10000·N | 3449 | public HTTPS (Caddy; `/mcp/ws` same-origin) |
| 3449/udp | 13449/udp | … | 3449/udp | HTTP/3 |
| 4401 | 14401 | … | 4401 | MCP HTTP stream |
| 4403 | 14403 | … | 4403 | MCP REPL |
| 14181 | 24181 | … | 14281 | Serena MCP |
| 14182 | 24182 | … | 24282 | Serena dashboard |
Everything else (frontend dev, backend API, exporter, storybook, REPLs, plugin dev, MCP inspector/WebSocket) is in-process or same-origin via Caddy/nginx. Infra publishes: mailer 1080, ldap 10389/10636 (singletons, not offset).
## Tmux + MCP routing
`docker/devenv/files/start-tmux.sh` is session-level idempotent. Reads `PENPOT_TMUX_ATTACH`. If the session exists it attaches or exits; otherwise creates 4 base windows (frontend watch / storybook / exporter / backend) plus `mcp` (when `enable-mcp` in `PENPOT_FLAGS`) and `serena` (when `SERENA_ENABLED=true`). `run-devenv-agentic` always sets both env vars. The legacy `run-devenv` alias doesn't, hence its 4-window-only session. To switch from a legacy session to agentic, `stop-devenv` then `run-devenv-agentic` — the conditional windows are only added at session create time.
MCP plugin routing is same-origin: frontend uses `<public-uri>/mcp/ws`, per-instance nginx proxies to MCP port 4401 in-container. For the plugin↔MCP server wiring (how the browser plugin discovers the URL, the in-memory connection registry, why DB-mediated routing isn't needed), see `mem:mcp/core`.
## Workspace orchestration (ws1+)
Workspace directories are user-maintained at `${PENPOT_WORKSPACES_DIR}/wsN`. `run-devenv-agentic --ws i` syncs only when `--sync` is passed, with one exception: if the workspace directory is missing on first use, sync runs implicitly to seed it.
`sync-workspace wsN`:
1. `assert-clean-git-state` — refuses on `.git/{rebase-apply,rebase-merge,MERGE_HEAD,CHERRY_PICK_HEAD,index.lock}`. No `--sync-force` escape.
2. `rsync -a --delete $PWD/.git/ $workspace/.git/`.
3. `git ls-files -z --cached --others --exclude-standard``rsync --files-from` (Git is the authority on tracked files; rsync's gitignore filter would drop committed files under gitignored parents like `.clj-kondo/config.edn`).
4. Initial-only copy of `frontend/resources/public/js/config.js` (gitignored, but agentic mode needs it). After the first sync the workspace's copy belongs to the developer — subsequent syncs leave it alone.
5. `git switch -C "wsN/<current-branch>"` inside the workspace.
No `--delete` on the working-tree pass: gitignored caches in the workspace survive. Workspace dir + named volumes survive `compose down`.
## CLI surface
- `run-devenv-agentic [--ws main|0|wsN|N] [--sync] [--serena-context CTX]`: bring one instance up. Agentic only — MCP and Serena windows are always created. Default target main. Errors out if the target is already running. `--sync` is rejected on main; on ws1+ it's optional (forced only when the workspace dir does not exist yet). Auto-starts ws0 first when the target is ws1+ and ws0 is not yet up.
- `stop-devenv [--ws main|0|wsN|N] [--all]`: stop instances. Flags mutually exclusive. `--ws N` (N≥1) stops just that workspace. `--ws 0` or no flag stops ws0 + shared infra, refused while any ws1+ is running. `--all` stops every ws highest-first then ws0, then infra.
- `run-devenv`: legacy alias, ws0 non-agentic attached.
- `attach-devenv [--ws main|0|wsN|N]`: pure attach. Fails fast if instance/session missing.
- `run-devenv-shell [--instance 0|wsN|N] [cmd...]`: bash in target instance. (`--instance` flag not yet renamed to `--ws`.)
- `start-devenv` / `log-devenv` / `drop-devenv`: legacy paths around ws0 + shared infra. `drop-devenv` never removes volumes.
`exporter/scripts/run` and `wait-and-start.sh` source `backend/scripts/_env` then `_env.local` if present.
+33
View File
@@ -0,0 +1,33 @@
# Exporter Architecture and Workflow
`exporter/`: CLJS/Node headless export service. Depends on `common/`; uses Playwright plus export JS/CLJS deps for SVG/PDF/assets.
## Layout and commands
- Source: `exporter/src/`; config: `deps.edn`, `shadow-cljs.edn`, `package.json`; runtime helpers/assets: `vendor/`, `scripts/`.
- From `exporter/`: setup `./scripts/setup`; watch `pnpm run watch` or `pnpm run watch:app`; production build `pnpm run build`; lint `pnpm run lint`; format check/fix `pnpm run check-fmt` / `pnpm run fmt`.
- Because exporter consumes `common/`, shared file/shape/model changes may need exporter verification even when the immediate change is not under `exporter/`.
## HTTP and browser pool
- POST body limit is about 60 MB. Exporter supports `application/transit+json`; request params merge query params and body params.
- Map response bodies are Transit JSON and force HTTP 200; nil 200 bodies become 204.
- Auth token comes from cookie `auth-token`, then uploads use Bearer auth plus the management shared key.
- Each export job gets a fresh Playwright browser context. On success, the context closes and the browser returns to the pool; on error, the browser is destroyed instead of reused.
- Borrow validates browser connection. Pool acquire timeout is about 10s; font loading timeout logs a warning and continues after about 15s.
## Export batching and async behavior
- `prepare-exports` groups entries by `[scale type]` and partitions groups into chunks of 50. Each partition uses file/page/share/name from its first item, so be careful if entries might cross those boundaries.
- Single-export response is used only when multiple export is not forced and there is exactly one prepared export containing exactly one object.
- Multi-object export can run async: when `wait` is false it returns a resource immediately and publishes progress/end/error to Redis by profile topic; when `wait` is true it waits for upload and returns the uploaded resource.
- Frame export returns a resource immediately and publishes Redis updates; it does not follow the same `wait` option path.
- ZIP entry names are sanitized and duplicates receive numeric suffixes.
## Render details
- Bitmap export differs for WASM vs non-WASM render paths: WASM forces Playwright `deviceScaleFactor` to 1 and passes scale through the render URL; non-WASM uses `deviceScaleFactor = scale`.
- WebP is produced by taking a PNG screenshot and converting it with ImageMagick.
- SVG export rasterizes text foreignObjects to PNG, converts through PPM/color masks/potrace, and reassembles SVG paths. It also replaces non-breaking spaces for SVG compatibility and drops empty defs/paths.
- PDF export injects `@page` sizing through raw browser `evaluate` JavaScript; that code cannot rely on CLJS runtime helpers.
- Temporary resources schedule local deletion, then uploads POST to `/api/management/methods/upload-tempfile` with `X-Shared-Key: exporter <management-key>` and Bearer auth.
+94
View File
@@ -0,0 +1,94 @@
# ClojureScript REPL and Frontend Debugging
Execute code in the live frontend via the Penpot MCP `cljs_repl` tool. For browser-console debugging, the frontend also exports a `debug` JS namespace in development builds.
## Accessing app state
The main store is `app.main.store/state`. It contains workspace metadata, selection, UI state, profile, route, etc. Page objects are not under a `:workspace-data` key; use derived refs.
```clojure
;; Current selection
(mapv str (get-in @app.main.store/state [:workspace-local :selected]))
;; Current page objects
(let [objects @app.main.refs/workspace-page-objects
shape (get objects (parse-uuid "some-uuid-here"))]
(select-keys shape [:name :type :x :y :width :height :fills :strokes :rotation :opacity :frame-id :parent-id]))
```
Shape keys use kebab-case keywords. Internal `:rect` corresponds to "rectangle" in the JS Plugin API, and `:frame` corresponds to "board".
Component instance shapes carry `:component-id` and `:component-file` directly; `:component-root` flags the root of an instance. Use `app.common.types.container/get-head-shape` for nearest head and `get-instance-root` for outermost root; they differ for nested instances.
## Navigation recipe
To programmatically open a workspace file, all three ids are required:
```clojure
(do (require '[app.main.data.common :as dcm])
(app.main.store/emit! (dcm/go-to-workspace
:team-id (parse-uuid "<team-id>")
:file-id (parse-uuid "<file-id>")
:page-id (parse-uuid "<page-id>"))))
```
Get `team-id` from `(:current-team-id @app.main.store/state)`. Get file ids from `(vals (:files @app.main.store/state))`. Get page ids by fetching file data, e.g. through `rp/cmd! :get-file` with current features.
## Reload the live runtime
`(.reload js/location)` (alias `app.util.dom/reload-current-window`) from `cljs_repl` reloads the browser page: clears `set!` runtime patches, re-fetches file state, and is the simplest crash recovery while the repl is live (`mem:frontend/handling-crashes`). To re-fetch only the current file's data without a full page reload, emit `(app.main.store/emit! (potok.v2.core/event :app.main.data.workspace/reload-current-file))`.
## Useful lookup helpers
`app.plugins.utils` contains state lookup helpers that are useful from any CLJS, despite living under `plugins/`:
- `locate-shape`, `locate-objects`, `locate-file`.
- `locate-component` resolves through the outermost instance root.
- `locate-head-component` resolves through the nearest component head.
- `locate-library-component` does direct file-id/component-id lookup.
## Runtime patching with `set!`
Some frontend vars are deliberately mutable escape hatches for runtime instrumentation or circular-dependency patching. From `cljs_repl`, use `set!` for temporary debugging of CLJS vars such as `app.main.store/on-event`, `app.main.errors/reload-file`, `app.main.errors/is-plugin-error?`, `app.main.errors/last-report`, or `app.main.errors/last-exception`. These patches affect only the live browser runtime and disappear on reload or recompilation.
```clojure
;; Log non-noisy Potok events temporarily.
(set! app.main.store/on-event
(fn [event]
(when (potok.v2.core/event? event)
(.log js/console (potok.v2.core/repr-event event)))))
```
Restore mutable hooks after debugging, or reload the frontend. Use JVM `alter-var-root` only for JVM Clojure; it is not the normal way to patch live CLJS browser vars.
## Browser-console debug namespace
In development, the JS console exposes `debug` helpers from `frontend/src/debug.cljs`:
```javascript
debug.set_logging("namespace", "debug");
debug.dump_state();
debug.dump_buffer();
debug.get_state(":workspace-local :selected");
debug.dump_objects();
debug.dump_object("Rect-1");
debug.dump_selected();
debug.dump_tree(true, true);
```
Visual workspace debug overlays can be toggled with `debug.toggle_debug("bounding-boxes")`, `"group"`, `"events"`, or `"rotation-handler"`; `debug.debug_all()` and `debug.debug_none()` toggle all visual aids.
For temporary source traces, prefer existing logging (`app.common.logging` / `app.util.logging`) or short-lived `prn`, `app.common.pprint/pprint`, `js/console.log`, or `js-debugger` calls. Remove temporary source instrumentation before committing.
## Runtime targeting
`cljs_repl` may connect to the wrong runtime when several are attached, such as workspace plus rasterizer. Verify with `(.-title js/document)`; it should show the workspace file name, not "Penpot - Rasterizer".
To list or target shadow-cljs runtimes, run from `/home/penpot/penpot/frontend`:
```bash
printf '(shadow.cljs.devtools.api/repl-runtimes :main)\n' | timeout 10 npx shadow-cljs clj-eval --stdin
printf '(shadow.cljs.devtools.api/cljs-eval :main "<cljs-code>" {:client-id 5})\n' | timeout 10 npx shadow-cljs clj-eval --stdin
```
Use command timeouts so a disconnected browser does not hang the session.
@@ -0,0 +1,22 @@
# Frontend Compile Diagnostics
Separate from runtime crash recovery.
## First check the shadow-cljs build
Use the Penpot MCP `cljs_compiler_output` tool to inspect the latest shadow-cljs `:main` build status. This is the fastest way to distinguish a bad build from a runtime error in the browser.
Recommended order after CLJ/CLJC/CLJS source edits:
1. Run `cljs_compiler_output`.
2. If the compiler reports a Clojure syntax problem, especially unmatched delimiters or a confusing location, run `clj_check_parentheses` on the absolute path of the suspect `.clj`, `.cljc`, or `.cljs` file.
3. After the build is healthy, use `mem:frontend/cljs-repl`, browser tools, or runtime crash checks for behavior.
## Parentheses checker
`clj_check_parentheses` analyzes one Clojure/ClojureScript source file and reports the area likely responsible for unclosed parentheses/brackets/braces. Use it when compiler output points near EOF, points at a misleading later form, or says delimiter-related syntax errors.
## Hot reload notes
When the frontend shadow-cljs watch process is running, edits to CLJC files in `common/` are normally recompiled into the browser automatically. Do not restart the frontend before checking `cljs_compiler_output`; stale behavior is often a failed build.
For production/minified stack traces, build the production bundle from `frontend/` with `pnpm run build:app`. Output and source maps are generated under `frontend/resources/public/js`; inspect source maps or shadow-cljs reports using build ids from `shadow-cljs.edn`.
+54
View File
@@ -0,0 +1,54 @@
# Frontend Architecture and Workflow
Frontend: CLJS SPA; React/Rumext; Potok; RxJS; okulary refs; SCSS modules; shared `common/`; JS/TS workspace packages.
## Stable namespace map
- `app.main.ui.*`: Rumext/React UI components for workspace, dashboard, viewer, settings, auth, nitrate, etc.
- `app.main.data.*`: Potok event handlers and side effects.
- `app.main.refs`: reactive refs/lenses over store and derived workspace data.
- `app.main.store`: Potok store and `emit!`.
- `app.plugins.*` and `app.plugins`: CLJS implementation of Plugin JS API proxies.
- `app.render_wasm.*`: frontend bridge to Rust/WASM renderer.
- `app.util.*`: DOM, HTTP, i18n, keyboard, codegen, and general frontend utilities.
- `frontend/packages/*` and `frontend/text-editor`: JS/TS workspace packages consumed by the app.
- Nitrate subscription/organization UI and flows live under `app.main.data.nitrate` and `app.main.ui.nitrate*`; backend/API behavior is covered by backend memories, and shared permission rules are in `common/src/app/common/types/nitrate_permissions.cljc`.
## Lint and Format
From `frontend/`:
- CLJ/CLJS lint: `pnpm run lint:clj`.
- JS lint currently no-ops via `pnpm run lint:js`.
- SCSS lint: `pnpm run lint:scss`.
- Format checks: `pnpm run check-fmt:clj`, `pnpm run check-fmt:js`, `pnpm run check-fmt:scss`.
- Format fix: `pnpm run fmt`, or targeted `fmt:clj` / `fmt:js` / `fmt:scss`.
- Translation formatting after i18n edits: `pnpm run translations`.
## Focused memory routing
UI and packages:
- App UI components, SCSS modules, style-system boundaries, accessibility, i18n, and render performance: `mem:frontend/ui-conventions-and-style-system`.
- JS/TS packages, shared UI package, text editor, Storybook, and package builds: `mem:frontend/ui-packages-text-editor-workflow`.
Workspace behavior:
- Workspace state, commits, persistence, undo, repo calls, and refs: `mem:frontend/workspace-state-persistence-subtleties`.
- Workspace transforms, modifier previews, WASM modifier integration, and transform commits: `mem:frontend/workspace-transform-subtleties`.
- Workspace token application/propagation: `mem:frontend/workspace-token-subtleties`; shared token data/schema: `mem:common/tokens-schema-subtleties`.
App shell and product flows:
- Routing, root app shell, websocket, and global errors: `mem:frontend/routing-app-shell-subtleties`.
- Dashboard and viewer flows: `mem:frontend/dashboard-viewer-subtleties`.
- Plugin JS API runtime inside the frontend app: `mem:frontend/plugin-api-to-cljs-binding`.
Diagnostics and validation:
- Runtime inspection and navigation: `mem:frontend/cljs-repl`.
- Source-edit compile/hot-reload diagnostics: `mem:frontend/compile-diagnostics`.
- Runtime crash recovery: `mem:frontend/handling-crashes`.
- Tests and live verification: `mem:frontend/testing`.
- Real pointer/keyboard gesture reproduction: `mem:frontend/playwright-gestures`.
## Areas without focused memories
These frontend areas currently have no dedicated Serena memory beyond this architecture entry and nearby source/tests: clipboard, drawing tools, boolean/path operations, interactions/prototyping, color/style asset management, grid-layout editing UI, comments UI, fonts UI, and many dashboard/settings subflows. Treat work there as less memory-covered and inspect source/tests more carefully.
@@ -0,0 +1,16 @@
# Frontend Dashboard and Viewer Subtleties
## Dashboard
- Dashboard initialization fetches projects and fonts for the team, then listens to websocket messages only for global topic `uuid/zero` or the current profile id.
- Project fetch replaces each project map completely instead of merging, so fields such as `deleted-at` can disappear cleanly.
- Dashboard file/project mutations are often optimistic local updates with fire-and-forget RPC watchers. Bulk permanent delete/restore paths use SSE progress and progress notifications.
- File creation/duplication strips file `:data` before putting file summaries into dashboard state.
## Viewer
- Viewer initialization sets `:current-file-id`, `:current-share-id`, and `:viewer-local`, then fetches the view-only bundle. Comment threads are fetched only for logged-in users.
- Viewer bundle fetch sends the full supported feature set because anonymous shared viewers may not know team-enabled features.
- View-only bundles can contain pointer values in `:pages-index` and file data. Viewer resolves those fragments with `:get-file-fragment` before storing the bundle.
- `bundle-fetched` indexes pages and precomputes viewer frames/all-frames, stores libraries/users/thumbnails/permissions under `:viewer`, then navigates to frame id, query index, or auto-selected frame.
- Viewer zoom and interaction mode changes update both `:viewer-local` and the `:viewer` route query params.
@@ -0,0 +1,38 @@
# Frontend Runtime Crash Handling
## Detect a runtime workspace crash
Runtime crashes usually show the Internal Error page with title text "Something bad happened" and class `main_ui_static__download-link`. A common pattern is: changes go through via JS API / `execute_code`, then 1-2s later an `update-file` request reaches the backend and is rejected.
After a crash, `execute_code` can become unusable because no plugin instances are connected and any data in its `storage` is lost, but `cljs_repl` usually still works.
Check crash state:
```clojure
(some? (:exception @app.main.store/state))
```
It returns `true` when the Internal Error page is showing and `false` on a healthy workspace or after a successful reload.
## Read the runtime cause
The exception is stored at `(:exception @app.main.store/state)`. Useful keys:
- `:type`, `:code`, `:status`: error class, e.g. `:validation`, `:referential-integrity`, `400`.
- `:hint`, `:details`: human-readable explanation; `:details` often contains validation problems with `:shape-id`, `:page-id`, `:args`, etc.
- `:uri`: API endpoint that returned the error, e.g. `update-file`.
- `:app.main.errors/instance`: underlying JS Error object.
- `:app.main.errors/trace`: JS stack trace string, usually response-handling path rather than the dispatch site that produced the bad change.
```clojure
(let [ex (:exception @app.main.store/state)]
(select-keys ex [:type :code :status :hint :details :uri]))
```
For backend validation errors (`:type :validation`), `:details` is usually the most informative field; it identifies the shape and invariant that failed.
## Recover and continue testing
Simplest path when `cljs_repl` is still live (usually true after a crash): reload via repl with `(.reload js/location)` — see `mem:frontend/cljs-repl`. Alternatively via Playwright: find the workspace tab (URL contains `/#/workspace`, title ends `- Penpot`), select it if not current, then `playwright:browser_navigate` to that same URL. Either way, confirm recovery with `(some? (:exception @app.main.store/state))` returning `false`.
For backend-rejected changes, such as validation errors on `update-file`, changes are not persisted. Reload restores the pre-crash state, so it is safe to retry after fixing the cause.
@@ -0,0 +1,49 @@
# Handling Errors and Debugging
## Finding source errors
You have access to two tools for finding errors in Clojure source code (which you may introduce yourself through edits):
1. cljs_compiler_output
2. clj_check_parentheses
The latter is needed because syntax errors in parentheses give an uninformative compiler error, and the second
tool can often find the exact location of such errors.
## Runtime patching with `set!`
Some frontend vars are deliberately mutable escape hatches for runtime instrumentation or circular-dependency patching.
From `cljs_repl`, use `set!` for temporary debugging of CLJS vars such as
`app.main.store/on-event`, `app.main.errors/reload-file`, `app.main.errors/is-plugin-error?`,
`app.main.errors/last-report`, or `app.main.errors/last-exception`.
These patches affect only the live browser runtime and disappear on reload or recompilation.
```clojure
;; Log non-noisy Potok events temporarily.
(set! app.main.store/on-event
(fn [event]
(when (potok.v2.core/event? event)
(.log js/console (potok.v2.core/repr-event event)))))
```
Restore mutable hooks after debugging, or reload the frontend. Use JVM `alter-var-root` only for JVM Clojure;
it is not the normal way to patch live CLJS browser vars.
## Browser-console debug namespace
In development, the JS console exposes `debug` helpers from `frontend/src/debug.cljs`:
```javascript
debug.set_logging("namespace", "debug");
debug.dump_state();
debug.dump_buffer();
debug.get_state(":workspace-local :selected");
debug.dump_objects();
debug.dump_object("Rect-1");
debug.dump_selected();
debug.dump_tree(true, true);
```
Visual workspace debug overlays can be toggled with `debug.toggle_debug("bounding-boxes")`, `"group"`, `"events"`, or `"rotation-handler"`; `debug.debug_all()` and `debug.debug_none()` toggle all visual aids.
For temporary source traces, prefer existing logging (`app.common.logging` / `app.util.logging`) or short-lived `prn`, `app.common.pprint/pprint`, `js/console.log`, or `js-debugger` calls. Remove temporary source instrumentation before committing.
@@ -0,0 +1,117 @@
# Penpot Canvas → Playwright Viewport Coordinate Mapping
## Goal
Map Penpot shape coordinates (from the JS/ClojureScript API) to browser viewport CSS pixels
so that Playwright mouse actions (click, drag, hover) can target specific canvas objects.
## Key Facts
### Playwright coordinate system
Playwright mouse coordinates are **viewport CSS pixels**: `(0, 0)` is the top-left of the
browser's rendered content area (not the screen, not the OS window chrome).
`getBoundingClientRect()` returns the same coordinate system — they are directly compatible.
### Canvas element location
The Penpot canvas is rendered by two co-located elements:
- `<canvas>` — the rasterised render
- `<svg id="render">` — vector overlay
- `<svg class="...viewport-controls ...">` — interaction/control layer (has the `viewBox`)
Get the canvas origin with:
```js
document.querySelector("#render").getBoundingClientRect()
// => { left: 318, top: 0, width: 514, height: 586, ... } (values vary with window size/panels)
```
The left offset (currently ~318 px) is caused by the left-side panel (layers, assets).
### Zoom and pan state
Available in two equivalent ways:
**1. App state (ClojureScript):**
```clojure
(let [wl (get @app.main.store/state :workspace-local)]
{:zoom (get wl :zoom) ; scale factor: penpot-units → CSS pixels
:vbox (get wl :vbox)}) ; Rect {:x :y :width :height} — penpot coords of visible area
```
**2. SVG viewBox attribute (DOM):**
```js
document.querySelector("svg.viewport-controls, [class*='viewport-controls']")
.getAttribute("viewBox")
// => "670 658.31 224 255.38" i.e. "vbox.x vbox.y vbox.width vbox.height"
```
Both sources are live and always in sync.
### Coordinate conversion formula
```
viewport_x = canvas_left + (penpot_x - vbox.x) * zoom
viewport_y = canvas_top + (penpot_y - vbox.y) * zoom
```
Sanity check: `vbox.width * zoom ≈ canvas CSS width` (and same for height). ✓
### Device Pixel Ratio
The canvas physical pixel size = CSS size × DPR (observed DPR = 1.25, so canvas internal
size 642×732 vs CSS size 514×586). This does **not** affect the formula — both
`getBoundingClientRect()` and Playwright use CSS pixels.
### Ruler label offset
The on-screen rulers show coordinates offset from absolute Penpot coordinates (they display
frame-relative values, offset by ~the top-level frame's x/y). **Ignore for coordinate
mapping** — use `vbox` directly.
---
## ClojureScript Helper (paste into cljs REPL session)
```clojure
(defn penpot->viewport-coords
"Convert Penpot canvas coordinates to browser viewport CSS pixel coordinates.
Returns {:vp-x <number> :vp-y <number>} — pass directly to Playwright mouse actions."
[penpot-x penpot-y]
(let [state @app.main.store/state
wl (get state :workspace-local)
vbox (get wl :vbox)
zoom (get wl :zoom)
canvas (js/document.querySelector "svg.viewport-controls, #render")
canvas-rect (.getBoundingClientRect canvas)]
{:vp-x (+ (.-left canvas-rect) (* (- penpot-x (:x vbox)) zoom))
:vp-y (+ (.-top canvas-rect) (* (- penpot-y (:y vbox)) zoom))}))
```
Usage example — click the center of a shape:
```clojure
(let [shape (get-in @app.main.store/state [:files file-id :data :pages-index page-id :objects shape-id])
cx (+ (:x shape) (/ (:width shape) 2))
cy (+ (:y shape) (/ (:height shape) 2))
{:keys [vp-x vp-y]} (penpot->viewport-coords cx cy)]
;; pass vp-x, vp-y to Playwright page.mouse.click(vp-x, vp-y)
{:vp-x vp-x :vp-y vp-y})
```
---
## JavaScript equivalent (for use in Playwright scripts directly)
```js
function penpotToViewport(penpotX, penpotY) {
// Read viewBox from the controls SVG (always in sync with app state)
const svg = document.querySelector('[class*="viewport-controls"]');
const [vbX, vbY, vbW, vbH] = svg.getAttribute('viewBox').split(' ').map(Number);
const rect = svg.getBoundingClientRect();
const zoom = rect.width / vbW; // == rect.height / vbH
return {
x: rect.left + (penpotX - vbX) * zoom,
y: rect.top + (penpotY - vbY) * zoom,
};
}
```
This function can be injected and called via `page.evaluate()` in Playwright:
```js
const {x, y} = await page.evaluate(
([px, py]) => penpotToViewport(px, py),
[penpotX, penpotY]
);
await page.mouse.click(x, y);
```
@@ -0,0 +1,47 @@
# Driving Real User Gestures via Playwright
Use Playwright when the bug or behavior depends on Penpot's real input pipeline: pointer gestures, keyboard modifiers, drag/drop targeting, modifier propagation, hover/focus behavior, or alt-drag duplication. The plugin JS API and `penpot:execute_code` can bypass these paths by dispatching store/API operations directly.
## When `execute_code` Is Not Enough
`execute_code` runs in the plugin sandbox. It is excellent for creating shapes, calling Plugin API methods, and querying design data, but it does not faithfully reproduce all user gestures. If the issue involves interactive transforms, frame targeting during drop, drag previews, modifier keys, or canvas hit-testing, drive the browser with Playwright and inspect results via cljs-repl.
## Gesture Pattern
A reliable drag gesture generally needs:
- focus on the canvas first;
- key modifiers held from before mouse down until after mouse up;
- intermediate mouse move events, not just start/end;
- short waits so Penpot's drag pipeline observes the gesture;
- a trailing wait for the transaction to commit.
Alt-drag duplication example:
```javascript
async (page) => {
await page.mouse.click(700, 700);
await page.waitForTimeout(200);
const startX = 821, startY = 565, endX = 821, endY = 815;
await page.keyboard.down('Alt');
await page.mouse.move(startX, startY);
await page.waitForTimeout(100);
await page.mouse.down();
await page.waitForTimeout(100);
for (let i = 1; i <= 10; i++) {
const t = i / 10;
await page.mouse.move(startX + (endX - startX) * t,
startY + (endY - startY) * t);
await page.waitForTimeout(20);
}
await page.waitForTimeout(100);
await page.mouse.up();
await page.waitForTimeout(100);
await page.keyboard.up('Alt');
await page.waitForTimeout(500);
}
```
## Coordinate Planning
For reliably finding pixel positions of objects, see `mem:frontend/penpot-to-browser-coords`.
@@ -0,0 +1,34 @@
# Frontend Plugin API Runtime Subtleties
## Type declarations vs runtime
- The Plugin API is a public facade over internal frontend/common data. Do not expect Plugin API property names, value shapes, or behavior boundaries to match internal CLJS attrs or helper APIs; inspect the relevant proxy and internal code path before using Plugin API observations in production internals or tests.
- `plugins/libs/plugin-types/index.d.ts` contains TypeScript declarations only. Runtime objects are CLJS proxies built under `frontend/src/app/plugins/*.cljs` with `obj/reify`.
- `shape.cljs` builds shape proxies with hidden ids and per-property CLJS implementations. `library.cljs` builds library proxies such as `LibraryComponentProxy`.
- `shape.cljs`, `library.cljs`, and related namespaces break circular dependencies with mutable nil vars patched from `app.plugins` at load time. If a proxy constructor appears nil, check the patching path in `frontend/src/app/plugins.cljs`.
## Key Domain Namespaces
- `app.common.types.component` (aliased `ctk`) — component predicates: `instance-root?`, `instance-head?`, `in-component-copy?`, `is-variant?`
- `app.common.types.container` (aliased `ctn`) — container/tree operations: `in-any-component?`, `get-instance-root`, `get-head-shape`, `inside-component-main?`
- `app.common.types.file` (aliased `ctf`) — file-level operations: `resolve-component`, `get-ref-shape`
## Runtime initialization and permissions
- The frontend initializes `@penpot/plugins-runtime` only after `features/initialize` and only when feature `plugins/runtime` is active. It also installs the runtime `isPluginError` predicate into frontend error handling.
- Manifest parsing expands write permissions to read permissions (`content:write` => `content:read`, etc.). Permission checks also allow the all-zero plugin id and the hard-coded MCP plugin id.
- Manifest URL origin differs by manifest version: v1 clears the path; v2 joins `.` to the plugin URL. Existing plugin ids are reused by matching manifest name and host.
- The MCP plugin id is defined in `app.plugins.register` to avoid a circular dependency with workspace MCP code.
## Proxy behavior
- Public Plugin API objects are lightweight handles, not durable snapshots. Most getters locate fresh state from `app.main.store/state` using hidden `$id`, `$file`, `$page`, etc.
- `not-valid` logs by default but throws when the plugin flag `throwValidationErrors` is enabled. The MCP execute-code handler deliberately enables that flag while running code.
- `naturalChildOrdering` and `throwValidationErrors` are stored per plugin under `[:plugins :flags plugin-id ...]`; changing default behavior affects automation and MCP diagnostics.
- Plugin data is stored under keyword namespaces: private data uses `(keyword "plugin" plugin-id)`, shared data uses `(keyword "shared" namespace)`.
## Events and history
- Plugin listeners are watches on the global store and callbacks are debounced about 10ms. Callback exceptions are caught and logged so plugin code does not crash the app.
- `selectionchange` callbacks receive arrays of shape id strings, while `filechange`, `pagechange`, and `shapechange` return proxies.
- `contentsave` fires only when persistence status transitions to `:saved`; it calls the callback with no value.
- Plugin history `undoBlockBegin` creates a workspace undo transaction with a JS `Symbol`; `undoBlockFinish` commits that symbol. Missing finish eventually relies on the workspace transaction timeout.
@@ -0,0 +1,17 @@
# Frontend Routing, App Shell, Websocket, and Error Subtleties
## Router, app shell, and errors
- Routing uses browser-history hash tokens, but `on-navigate` rejects navigation if the current origin/path does not match `cf/public-uri`.
- Route params are split into `:path` and `:query`; duplicate query params can become vectors, so use `rt/get-query-param` when a scalar is required.
- Unknown/empty routes trigger an extra `get-profile`/`get-teams` check before redirecting. This avoids invitation and root-route race conditions.
- The root app renders an exception page from `:exception` state before the normal error boundary. `rt/navigated` clears `:exception`.
- Frontend error handling treats stale cross-build JS chunk failures specially: messages containing `$cljs$cst$` or `$cljs$core$I` plus undefined/null/not-a-function signatures trigger throttled reload.
- Plugin-originated uncaught errors are identified through the plugin runtime hook and logged rather than turning into the global exception page.
## Store and websocket
For general store mechanics such as `emit!`, `last-events`, persistence, and undo, read `mem:frontend/workspace-state-persistence-subtleties`.
- Websocket initialization uses `cf/public-uri` joined with `ws/notifications`, converting `http/https` to `ws/wss`, and includes the current `session-id` as query param.
- Reinitializing or finalizing websocket stops the previous receive stream. Incoming websocket payloads become Potok data events under `app.main.data.websocket/message`.
+35
View File
@@ -0,0 +1,35 @@
# Frontend Testing and Live Verification
Frontend validation: CLJS + React/Rumext + RxJS/Potok; SCSS modules; shared CLJC from `common/`.
## Unit tests
Frontend unit tests live under `frontend/test/frontend_tests/` and use `cljs.test`. They should be deterministic, avoid DOM/UI integration where possible, and mock side effects such as RPC, storage, timers, or network access.
From `frontend/`:
- Full unit test run: `pnpm run test:quiet`.
- Focus a frontend CLJS test namespace: `pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens`.
- Focus one frontend CLJS test var: `pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens/change-spacing-token-in-main-updates-copy-layout`.
- Quiet `app.*` logging during a run: append `--log-level warn` (or `trace|debug|info|warn|error`).
- Build test target only: `pnpm run build:test`.
- After `pnpm run build:test`, direct compiled runner focus is faster: `node target/tests/test.js --focus frontend-tests.logic.components-and-tokens/change-spacing-token-in-main-updates-copy-layout`.
- Watch tests: `pnpm run watch:test`.
New frontend test namespaces must be required/listed in `frontend_tests/runner.cljs`; new vars in existing namespaces need no runner change.
## Playwright integration tests
Do not add, modify, or run Playwright integration tests under `frontend/playwright` unless explicitly asked. When explicitly asked, use `pnpm run test:e2e` or `pnpm run test:e2e --grep "pattern"` from `frontend/`; ensure dependencies are installed through `./scripts/setup` if the environment is not prepared.
Integration tests fake backend behavior by intercepting network/websocket traffic, so every RPC or websocket the page needs must be mocked. Use existing Page Object Models:
- `BasePage.mockRPC` intercepts RPC calls and already prefixes `/api/rpc/command/`; pass command names such as `get-profile`, not full URLs.
- Workspace or other websocket-using pages should extend/use `BaseWebSocketPage`, initialize websocket mocks before each test, and mock `/ws/notifications` with the provided helpers.
- Prefer common locators/actions in POMs; ad-hoc locators can stay in a single test.
Locator priority should follow user-facing semantics: `getByRole`, `getByLabel`, `getByPlaceholder`, `getByText`, then semantic alternatives such as alt/title, with `getByTestId` as the last resort. Name tests from the user's perspective and prefer positive, single-purpose assertions.
## Live browser verification
Because CLJC compiles to both JVM and CLJS, JVM/common tests can miss frontend-only state caused by browser runtime, WASM modifier math, or real pointer events. Use `mem:frontend/cljs-repl` to inspect live app state and `mem:frontend/playwright-gestures` when real input is needed.
For stale hot reload or failed CLJ/CLJC/CLJS source builds, read `mem:frontend/compile-diagnostics`. For Internal Error pages or delayed runtime crashes after automation/API actions, read `mem:frontend/handling-crashes`. Translation `.po` changes are bundled into `index.html` and require a browser refresh.
@@ -0,0 +1,56 @@
# Frontend UI Conventions and Style System
## CLJS app UI
- Main app components live under `frontend/src/app/main/ui*` and normally use Rumext `mf/defc` with a `*` suffix for component vars and `[:> component* props]` call sites.
- Components should have clear ownership. Use `children` for normal composition; use slotted props only when separate owned regions are needed. Do not style or structurally manipulate child DOM that the component did not instantiate.
- Accept and merge a `class` prop when callers reasonably need layout/positioning customization. Use `mf/spread-props` so Rumext prop transformations such as `:class` -> `className` still apply.
- Avoid boolean prop names ending in `?`; they do not translate cleanly to JavaScript props. Use type hints such as `^boolean` where JS truthiness/semantics matter.
- Split large components into smaller private components when useful; `::mf/private true` is the local convention for private Rumext components.
## Styling
- Co-located SCSS modules are preferred. Use `app.main.style/stl` helpers from CLJS and design-system SCSS tokens/mixins instead of legacy global selectors or high-specificity nesting.
- Keep CSS specificity low. Avoid nested selectors unless they target elements the component owns; CSS Modules already prevent class-name collisions.
- Prefer CSS logical properties for directional spacing/layout (`padding-inline-start`, etc.). Physical `width`/`height` are still acceptable where they are clearer.
- Use named design-system variables/tokens for spacing, borders, fixed dimensions, colors, and typography. Avoid hardcoded px/rem values and deprecated `resources/styles/common/refactor/spacing.scss` variables.
- Use component-local CSS custom properties for variants and theming instead of one-off Sass variables when a component has multiple visual states.
- Prefer DS typography components (`heading*`, `text*`) and typography mixins instead of plain text wrappers or deprecated typography mixins.
## Accessibility
- Prefer semantic HTML first: anchors for navigation/download/email links, buttons for actions, correct heading levels, and keyboard-focusable controls.
- If native elements cannot be used, apply appropriate ARIA roles/patterns. Follow WAI-ARIA APG patterns for standard widgets.
- Icon-only controls need an accessible name via surrounding text, `aria-label`, `alt`, or equivalent. Decorative images/icons should be hidden from assistive tech.
## I18n
- Translations must be resolved during render or render-time memoization, not at namespace load time. For static option lists, memoize inside render so locale changes still update labels.
- Translation files live in `frontend/translations/*.po`. Translation changes are bundled into `index.html`; refresh the browser after changing translations because there is no hot reload for translation strings.
- Run `pnpm run translations` from `frontend/` after adding/updating translation text.
- Adding a new supported locale requires updates in both `frontend/src/app/util/i18n.cljs` (`supported-locales`) and `frontend/scripts/_helpers.js` (`langs`).
## Performance
- Keep expensive derived data in refs, memoized selectors, or pure helpers. In hot render paths, prefer existing `app.common.data.macros` helpers where local code already uses them.
- Avoid creating new callback functions/objects inside hot renders when a named function, memoized callback, data attribute, or precomputed JS props object works.
- Destructure props/state values used repeatedly. Avoid repeated deref/property access in render loops.
## Shared React UI package
- `frontend/packages/ui` is the shared React/Vite package. It should remain framework-neutral relative to the CLJS app store; reusable primitives belong here only when they do not depend on Potok/Rumext app state.
- Package styles are emitted through the package build and copied into `frontend/resources/public/css/ui.css`; stale shared styles are often a build artifact issue.
- Storybook is the primary visual harness for shared UI/package behavior. Use `mem:frontend/ui-packages-text-editor-workflow` for package build/test commands.
## Choosing a location
- Put editor/dashboard/viewer workflow logic in CLJS app namespaces close to the owning feature.
- Put reusable presentational React primitives in `frontend/packages/ui` when they can be consumed without Penpot app state.
- Put CLJS design-system components under `frontend/src/app/main/ui/ds`; new DS components need implementation, CSS module, Storybook story, optional MDX docs, and export from `frontend/src/app/main/ui/ds.cljs` with a JavaScript-friendly name.
- Put text editing internals in `frontend/text-editor` when the behavior belongs to the JS editor package; use `mem:common/text-subtleties` for shared text data-model behavior.
## Validation
- For CLJS app UI, use `mem:frontend/testing`, `mem:frontend/compile-diagnostics`, and live browser/REPL checks when behavior depends on store or canvas state.
- For shared UI package changes, run the package build plus Storybook/component tests when relevant.
- For text editor changes, run `frontend/text-editor` tests and refresh/copy WASM artifacts if render-wasm output is involved.
@@ -0,0 +1,34 @@
# Frontend UI Packages and Text Editor Workflow
`frontend/packages/`, `frontend/text-editor/`, Storybook/component tests. Separate from CLJS app UI under `frontend/src/app/main/ui`.
## Package boundaries
- `frontend/packages/ui` builds `@penpot/ui`, a React/Vite library package. It exports ESM and type declarations from `dist/`; React and ReactDOM are peer dependencies and must stay external in the Vite library build.
- The UI package build copies generated `dist/index.css` into `frontend/resources/public/css/ui.css`. If shared UI styles look stale in the app, rebuild the package or check this copy step before debugging CLJS style code.
- `frontend/text-editor` builds `@penpot/text-editor` from `src/editor/TextEditor.js`. It is a Vite JS package, not CLJS, and has its own Vitest/browser-test setup.
- The text editor consumes render-wasm artifacts copied from `frontend/resources/public/js` into `frontend/text-editor/src/wasm`. Use `pnpm run wasm:update` after rebuilding `render-wasm` if tests or local dev use stale WASM files.
- Other packages under `frontend/packages/` such as `tokenscript`, `draft-js`, and `mousetrap` are workspace dependencies used by the frontend app; do not assume their runtime behavior lives in CLJS namespaces.
## Commands
From `frontend/`:
- Build app-side JS package assets: `pnpm run build:app:libs`.
- Watch app-side JS package assets: `pnpm run watch:app:libs`.
- Storybook build: `pnpm run build:storybook`; local Storybook: `pnpm run watch:storybook`.
- Storybook/component tests: `pnpm run test:storybook`.
From `frontend/packages/ui`:
- Build library and CSS artifact: `pnpm run build`.
- Watch library build: `pnpm run watch`.
From `frontend/text-editor`:
- Local Vite dev: `pnpm run dev`.
- Tests: `pnpm run test`; coverage: `pnpm run coverage`; browser watch: `pnpm run test:watch:e2e`.
- Format check: `pnpm run fmt:js`.
## Validation notes
- Frontend root `check-fmt:js` covers stories, Playwright scripts, frontend scripts, and `text-editor/**/*.js`; it does not replace package-specific builds/tests.
- Changes to shared UI package exports should be validated both in the package build and in the consuming app/Storybook path.
- Changes that alter text rendering/editing can involve `frontend/text-editor`, `render-wasm`, CLJS text integration, and `mem:common/text-subtleties`; verify the runtime that actually owns the changed behavior.
@@ -0,0 +1,33 @@
# Frontend Workspace State and Persistence Subtleties
## Store and interaction streams
- `app.main.store/state` is the Potok store; `emit!` always returns nil. Store errors flow through the mutable `on-error` atom.
- `last-events` keeps a filtered rolling buffer of about 50 event type strings and commit hint origins. It intentionally omits noisy websocket/persistence/pointer events.
- `ongoing-tasks` controls `window.onbeforeunload`: any non-empty set blocks tab unload.
- `app.main.streams/wasm-modifiers` and `workspace-selrect` are behavior subjects used for high-frequency interactive preview state that bypasses normal store updates and lenses.
- Keyboard modifier streams merge a window blur signal so stuck modifier-key state is cleared after focus loss.
## Repo calls
- `app.main.repo/send!` uses GET only when the RPC name starts with `get-`, when all params are query params, or for configured special cases. Only GET requests are retried.
- GET retry is limited to transient `:network`, `:bad-gateway`, `:service-unavailable`, and `:offline` errors with exponential backoff. Mutations are not retried.
- A server SSE response is only accepted when the command is configured `:stream?`; otherwise it raises an unexpected-response assertion.
## Commits, undo, persistence
- `commit-changes` refuses to create commits unless `:permissions :can-edit` is true. It captures file revn/vern, selected-before, features, tags, undo group, and translation flag into a `::commit` event.
- Applying a remote commit first rolls back pending local commits, applies the remote changes, then replays pending local redo changes. Index updates are emitted for undo, remote redo, and replayed redo paths.
- Local commits are independently consumed by undo, persistence, WASM model updates, thumbnail/library watchers, and text position-data recalculation.
- Persistence buffers local commits: status becomes pending after about 200ms, commits are flushed after about 3s or `::force-persist`, and buffered commits are merged per file before `:update-file`.
- Persistence sends revn as the max of the commit revn and locally tracked latest revn; remote commits update that revn tracker.
- Persistence is skipped in version preview/read-only mode or without edit permission.
- Undo transactions can stay open only temporarily; timed-out pending transactions are force-committed after about 20s. Undo entries are capped at 50.
- Undo/redo are ignored while a normal editor/drawing interaction is active, except grid-layout edition handles undo through this path.
- After local commits and when render-wasm is active, text shapes get derived `:position-data` recomputed in a separate commit tagged `#{:position-data}`; that tag is excluded from the position-data watcher to avoid loops.
## Refs
- `refs/libraries` is explicitly deprecated for performance; prefer derefing `refs/files` and memoizing `select-libraries` in components.
- `refs/workspace-page-objects` uses `identical?` equality, so preserving object map identity matters for avoiding derived-ref churn.
- Selected-shapes refs use a small `{objects selected}` wrapper with custom equality before running `process-selected`; avoid bypassing that pattern in hot UI paths.
@@ -0,0 +1,17 @@
# Frontend Workspace Token Subtleties
## Token refs and visibility
- Workspace token refs intentionally hide the internal hidden theme from theme trees/lists and expose active tokens through `get-tokens-in-active-sets`.
- Token values stored on shapes are token names under `:applied-tokens`, not token ids. Renames/group renames must update those paths in common token logic.
## Token application
- Token application refuses to run while a text shape is in text-editing mode and shows a warning instead.
- Applying a token writes token names into shape `:applied-tokens`, resolves active tokens through Style Dictionary or `tokenscript` depending on feature flags, updates concrete shape attrs, and wraps the operation in an undo transaction.
- Applying composite typography removes atomic typography token attrs; applying atomic typography removes the composite typography token attr.
- Spacing tokens have a special split path: layout containers receive gap/padding updates, while immediate children of layouts receive margin updates.
## Propagation
- Token propagation resolves active tokens, buffers many `update-shapes` commits, walks the current page first then the remaining pages, clears affected frame/component thumbnails, and drops `:position-data` for text shapes on non-current pages so it can be regenerated.
@@ -0,0 +1,20 @@
# Frontend Workspace Transform Subtleties
## Preview vs committed transforms
- High-frequency previews use `app.main.streams/wasm-modifiers` and `workspace-selrect` behavior subjects instead of normal store commits; components consume them through refs that wrap plain atoms.
- `apply-modifiers*` is the lower-level commit path once object/text modifiers are ready. It updates frame guides, frame comment threads, and then emits `update-shapes` with `:reg-objects? true`.
- Transform commits restrict diff attrs to `transform-attrs` to avoid scanning unrelated shape attrs.
- Text transforms may carry derived `:position-data`; `assoc-position-data` attaches it while preserving the original text shape context.
## Component-copy touched suppression
- `calculate-ignore-tree` walks modified shapes and descendants to decide per copy-shape `ignore-geometry?`.
- `check-delta` compares a copy's relative position/rotation to its component root before and after transform. If relative movement is under about 1px and size/rotation are effectively unchanged, geometry touching is suppressed.
- This logic is why pure translations of component copies can avoid marking every descendant as geometry-touched, while resizes/rotations still propagate touched state.
## WASM bridge details
- WASM modifier updates set plugin/local props with parsed geometry/structure modifiers rather than directly mutating file data.
- The position-data recomputation watcher ignores commits tagged `:position-data`; keep that tag when adding derived position-data commits.
- Rotation has separate WASM and non-WASM event paths. Check both when changing rotation modifier semantics.
+30
View File
@@ -0,0 +1,30 @@
# Library Architecture and Workflow
`library/`: builds `@penpot/library`; JS-facing in-memory Penpot file builder and `.penpot` ZIP exporter. Separate from main app runtime.
## Layout and commands
- Source: `library/src/`; tests: `library/test/`; experimentation/docs: `playground/`, `docs/`; config: `shadow-cljs.edn`, `deps.edn`, `package.json`.
- From `library/`: build `pnpm run build`; bundle helper `pnpm run build:bundle` or `./scripts/build`; tests `pnpm run test`; watch `pnpm run watch` / `pnpm run watch:test`; lint `pnpm run lint`; format check/fix `pnpm run check-fmt` / `pnpm run fmt`.
- When changing file-format construction or export behavior in `common/`, consider whether `@penpot/library` should be tested because it constructs Penpot files outside the app UI.
## JS API and builder state
- The JS build context wraps an atom and implements `IDeref`; `getInternalState` exposes the CLJ state converted to JS.
- Public methods decode JS objects through the JSON transformer before calling common builder functions. Exceptions become JS `BuilderError` objects with enumerable `cause` and an `explain` getter for Malli explain data.
- `create-build-context` can store an optional `referer`, later written into the export manifest.
- The builder is stateful: call `addFile` before `addPage`. `addPage` resets the frame/group stack to the root and clears page-local naming state when the page closes.
- `addBoard` and `addGroup` push onto the parent stack; matching close calls pop it. `closeGroup` requires at least one child and recalculates group geometry. Masked groups use the first child as mask and copy its geometry.
- `commit-shape` emits `:add-obj` with `:ignore-touched true`, using the current parent, frame, and page from the stack.
- Layer names are uniqued per current page; duplicate names get generated suffixes.
- `addBool` converts an existing group into a bool shape and updates style/content/geometry via `:mod-obj` operations rather than adding a new object.
- Media blobs are stored separately from file-media metadata; `add-file-media` requires a `BlobWrapper`.
## Export package
- `.penpot` ZIPs include `manifest.json`, file/page/shape JSON, components/colors/typographies/tokens, media metadata, and media object blobs.
- Path/bool shape `:content` is converted to vectors before JSON encoding.
- File export intentionally includes only selected top-level attrs plus data options; color export removes `:file-id` and drops empty paths.
- Manifest type is `penpot/export-files`, version 1, generated by `penpot-library/%version%`, with optional referer and file relations.
- Export generation is sequential and lazy: delayed JSON/blob work is computed only as each zip entry is written, and the progress callback receives `{total,item,path}` after each entry.
- The library has compatibility defaults for features/migrations in the common builder; do not assume it always exports with the newest app-default migrations/features.
+95
View File
@@ -0,0 +1,95 @@
# Penpot MCP
This subproject provides an MCP server for Penpot integration.
The MCP server communicates with a Penpot plugin via WebSockets, allowing
the MCP server to send tasks to the plugin and receive results,
enabling advanced AI-driven features in Penpot.
## Tech Stack
- Language: TypeScript
- Runtime: Node.js
- Framework: MCP SDK (@modelcontextprotocol/sdk)
- Build Tool: TypeScript Compiler (tsc) + esbuild
- Package Manager: pnpm
## General Principles
IMPORTANT: Use an idiomatic, object-oriented style.
In particular, this implies that, for any non-trivial interfaces, you use interfaces that expect explicitly typed abstractions
rather than mere functions (i.e. use the strategy pattern, for example).
Comments:
When describing parameters, methods/functions and classes, you use a precise style, where the initial (elliptical) phrase
clearly defines *what* it is. Any details then follow in subsequent sentences.
When describing what blocks of code do, you also use an elliptical style and start with a lower-case letter unless
the comment is a lengthy explanation with at least two sentences (in which case you start with a capital letter, as is
required for sentences).
## Project Structure (Excerpt)
```
mcp/
├── packages/common/ # Shared type definitions
│ ├── src/
│ │ ├── index.ts # exports for shared types
│ │ └── types.ts # PluginTaskResult, request/response interfaces
│ └── package.json # @penpot-mcp/common package
├── packages/server/ # MCP server subproject
│ ├── src/
│ │ ├── index.ts # entry point
│ │ ├── PenpotMcpServer.ts # MCP server implementation (connection handling, tool registration, etc.)
│ │ ├── Tool.ts # base class for tools
│ │ ├── PluginTask.ts # base class for plugin tasks
│ │ ├── tasks/ # PluginTask implementations
│ │ └── tools/ # Tool implementations
| ├── data/ # contains resources, such as API info and prompts
│ └── package.json
├── packages/plugin/ # Penpot plugin subproject
│ ├── src/
│ │ ├── main.ts # handles communication
│ │ └── plugin.ts # plugin implementation
│ └── package.json # Includes @penpot-mcp/common dependency
└── prepare-api-docs # Python project for the generation of API docs
```
## Key Development Tasks
### Adjusting the Prompts
The system prompt file (aka Penpot High-Level Overview) is located in
`packages/server/data/initial_instructions.md`.
### Adding a new Tool
1. Implement the tool class in `packages/server/src/tools/` following the `Tool` interface.
IMPORTANT: Do not catch any exceptions in the `executeCore` method. Let them propagate to be handled centrally.
2. Register the tool in `PenpotMcpServer`.
Tools can be associated with a `PluginTask` that is executed in the plugin.
Many tools build on `ExecuteCodePluginTask`, as many operations can be reduced to code execution.
### Adding a new PluginTask
1. Implement the input data interface for the task in `packages/common/src/types.ts`.
2. Implement the `PluginTask` class in `packages/server/src/tasks/`.
3. Implement the corresponding task handler class in the plugin (`packages/plugin/src/task-handlers/`).
* In the success case, call `task.sendSuccess`.
* In the failure case, just throw an exception, which will be handled centrally!
4. Register the task handler in `packages/plugin/src/plugin.ts` in the `taskHandlers` list.
## Dev Tooling
From the `mcp/` directory, run
* `pnpm run build` to test the build of all packages
* `pnpm run fmt` to apply the auto-formatter
## Devenv plugin/server wiring
In the normal Penpot devenv MCP path, the browser plugin does not discover or route through Postgres. The frontend provides the plugin extension API with `mcp.getServerUrl()`, currently derived from `frontend/src/app/config.cljs` as `penpotMcpServerURI` if set, otherwise `<public-uri>/mcp/ws`. The MCP plugin opens a direct WebSocket to that URL and appends the current MCP access token as a query parameter.
The live plugin connection registry is in-memory inside each MCP server process (`PluginBridge.connectedClients` / `clientsByToken`). The database only stores MCP access tokens and profile props such as `mcp-enabled`; it does not manage which plugin is connected to which MCP server.
For parallel devenvs, prefer same-origin MCP routing: each Penpot instance should expose `/mcp/ws` through its own nginx/Caddy path to the MCP server running inside the same main container. Keep container-internal ports fixed (MCP defaults `4401/4402/4403`, backend/exporter/frontend defaults, etc.) and only offset host-side published ports per instance. If internal ports are offset, hardcoded local proxy config such as `docker/devenv/files/nginx.conf` will misroute unless templated too.
+33
View File
@@ -0,0 +1,33 @@
# Memory Maintenance
## Discovery Model
- Core principle: progressive discovery through references, building a graph of memories.
- Initially, agents are provided with the list of all memories (names only).
- Agents should read `mem:critical-info` as the top-level entry point (graph root).
This memory should contain references to other memories covering major project domains.
The referenced memories shall, in turn, shall contain references to even more specific memories, and so on.
The depth of the graph shall depend on the project complexity.
- Use topics/folders to group related memories in order to make the content structure explicit.
Folders can mirror project structure (e.g. modules like frontend/backend) or topics like debugging, architecture, etc.
- Memory references must use a mem: prefix inside backticks, e.g. `mem:frontend/core`.
The surrounding text should clearly indicate when to read the memory/which content to expect.
The text should provide more precise guidance than the memory name alone,
i.e. avoid a reference like "frontend debugging and error handling: `mem:frontend/handling-errors-and-debugging` and instead make clear which concrete aspects are covered in the memory.
- Memories themselves should not contain information about when to read them; this is the responsibility of the referring memory.
## Style
Dense agent notes, not prose docs. Prefer invariants, terse bullets.
Avoid obvious context, rationale, and examples unless they prevent likely mistakes.
Keep guidance durable and generalizable, not task-local.
## Add/update threshold
Add or update memories only with stable, non-obvious project conventions that avoid complex rediscovery in the future.
Do not add: quick-read facts; generic language/framework knowledge; one-off task notes; volatile line-level details; behavior likely to change soon.
## Maintenance Actions
- Renaming memories: References are updated automatically if handled via Serena's memory rename tool.
- Checking for stale memories (e.g. after deletion): Call `serena memories check` for a report.
+37
View File
@@ -0,0 +1,37 @@
# Plugins Architecture and Workflow
`plugins/`: standalone TypeScript/pnpm workspace for Plugin API packages and sample plugins. Related to, distinct from, frontend CLJS Plugin API runtime.
## Layout
- `libs/plugin-types`: TypeScript declarations for the public Penpot Plugin API. Type-only package; runtime behavior is implemented elsewhere.
- `libs/plugins-runtime`: runtime that loads plugins and exposes/generated API behavior to plugin code.
- `libs/plugins-styles`: reusable styling package for plugins.
- `apps/*-plugin`: sample/development plugins. `apps/e2e`: plugin e2e tests.
## Dev Workflow
- From `plugins/`: install `pnpm -r install`; runtime dev server `pnpm run start` or `pnpm run start:app:runtime`; sample plugin `pnpm run start:plugin:<name>`; build runtime `pnpm run build:runtime`; build plugins `pnpm run build:plugins`; lint `pnpm run lint`; format `pnpm run format:check` / `pnpm run format`; tests `pnpm run test`; e2e `pnpm run test:e2e`.
- If a change affects public Plugin API types or runtime, update `plugins/CHANGELOG.md`. Prefix type/signature entries with `**plugin-types:**`; runtime behavior entries with `**plugin-runtime:**`.
- JS Plugin API behavior inside Penpot app: `mem:frontend/plugin-api-to-cljs-binding`; TS declarations are not runtime code; many API objects are CLJS proxies in `frontend/src/app/plugins/*.cljs`.
## Sandbox and global cleanup
- The runtime uses SES compartments. Public API return values are passed through `ses.safeReturn` before crossing back to plugin code.
- Plugin `fetch` is sanitized: credentials are omitted and Authorization is blanked. The exposed response only includes ok/status/statusText/url/text/json.
- Timer callbacks are wrapped to mark plugin-originated errors, and timeout/interval IDs are tracked so plugin close can clear them.
- Plugin-originated errors are tracked in a WeakMap instead of mutating error objects, because SES can freeze errors.
- Closing a plugin removes public API keys from the compartment globalThis.
## Lifecycle
- Loading a plugin closes existing non-background plugins and resets the runtime registry. Be careful around `allowBackground` semantics when changing load/close behavior.
- If sandbox evaluation fails, the runtime marks the error as plugin-originated, closes the plugin, and rethrows.
- `plugin-manager` removes event listeners, timers, intervals, and modal state on close, and marks the plugin destroyed. Listener callbacks check that flag because Penpot events can fire after close.
## Modal/UI behavior
- Modal URL preparation differs by manifest version: v1 uses query string parameters, v2 puts parameters in the URL hash.
- `openModal` is idempotent for the same iframe source and avoids reopening when the target URL is already displayed.
- Modal permissions are derived from manifest permissions (`allow:downloads`, `clipboard:read`, `clipboard:write`).
- `resizeModal` clamps to at least 200x200 and at most the window minus margins, adjusting transform so the modal remains in the viewport.
+33
View File
@@ -0,0 +1,33 @@
# Production infrastructure (services Penpot depends on)
Backend (`app.config`, `PENPOT_*` env vars) is parameterized; deployments choose providers.
## Services
- **PostgreSQL**: durable store. Profiles, teams, files, sessions, audit, `storage_object` metadata, the `task` queue, `scheduled_task` cron registry, migrations. File-data also lives here when the file-data backend is `legacy-db`/`db`. One shared DB across all backends.
- **Redis (Valkey-compatible)**: per-backend message bus and cache. Concrete uses: msgbus Pub/Sub for collaborative-editing broadcasts and team/profile-org notifications fired by RPC handlers (`app.rpc.notifications`, `files_update`, `teams`, `websocket`); file-summary cache gated by `enable-redis-cache`; rate-limit counters; and the dispatcher→runner work hand-off list `penpot.worker.queue:<tenant>:<queue>`. `PENPOT_REDIS_URI`.
- **Object storage**: backends `:s3` and `:fs`. S3 in prod; devenv uses MinIO. Holds uploaded media, file-data when the file-data backend is `storage`, exports. Backend-side details (resolve, dedup, bucket set, file-data backends): `mem:backend/http-storage-filedata-subtleties`.
- **SMTP mailer**: invitations, password resets, email verification (sent via the `:sendmail` worker task).
- **LDAP** (optional auth provider): helpers in `app.auth.*`, gated by `enable-login-with-ldap`.
## Task queue and worker model
Async tasks are enqueued via `wrk/submit!` (`app.worker`), which inserts a row into the shared Postgres `task` table tagged with `queue = "<tenant>:<queue-name>"`. Submission is **fire-and-forget** — RPC handlers never poll, never wait, and workers never publish to msgbus. The only completion signal is the `task` row's `status` / `completed_at` columns, which nothing in `rpc/` reads. Soft-delete RPCs return immediately after marking the top-level row, leaving the cascade and reaping to workers.
Workers run on backends with `enable-backend-worker` in `PENPOT_FLAGS`. Each worker-enabled backend has a `dispatcher` (polls `task` with `FOR UPDATE SKIP LOCKED`, marks status='scheduled', RPUSHes claimed task IDs into **its own** Redis list) and one or more `runner`s per queue (BLPOP from that same local list, execute, update the Postgres row). The Redis hand-off list is purely intra-backend — cross-backend coordination happens at the Postgres row level.
## Cross-backend safety
Postgres row locking is the only correctness primitive: `task` claims via `FOR UPDATE SKIP LOCKED`, cron firing via `FOR UPDATE SKIP LOCKED` on the `scheduled_task` row, plus task-handler-internal locks (e.g. `file_gc_scheduler` locks candidate file rows). This makes the work-claim path safe across any number of worker-enabled backends.
Two known race patterns survive multi-backend operation:
- **Cron dedup is best-effort.** The lock on `scheduled_task` is released when the task body finishes. If two backends' cron timers fire for the same scheduled instant with a gap larger than the task body's runtime, both execute it. Penpot's cron entries are idempotent (`session-gc`, `objects-gc`, `storage-gc-*`, `tasks-gc`, `upload-session-gc`, `file-gc-scheduler`); the exceptions are `:telemetry` (would double-report) and `:audit-log-archive` (depends on archive target idempotency).
- **`wrk/submit! ::dedupe true`** does a non-atomic `DELETE` then `INSERT`. Concurrent cross-backend submits can both bypass the `DELETE` (each sees the other's uncommitted insert as absent) and end up with duplicate `'new'` rows. Each row claims and runs once independently, so the underlying work is fine; the "at most one pending" guarantee weakens.
Penpot in production lives with both: horizontal-scale deployments accept "exactly-once" as "essentially-once for idempotent operations." Devenv parallel instances handle it by running workers only on ws0 (see `mem:devenv/core`).
## See also
- Devenv composition and the ws0-only worker placement: `mem:devenv/core`.
- Storage backend resolution, dedup, file-data lifecycle: `mem:backend/http-storage-filedata-subtleties`.
+32
View File
@@ -0,0 +1,32 @@
# render-wasm Architecture and Workflow
`render-wasm/`: Rust crate compiled to WebAssembly via Emscripten/Skia; frontend loads generated JS/WASM renderer. FFI/memory/tile behavior: `mem:render-wasm/ffi-rendering-subtleties`.
## Stable Architecture
- Exported functions live around `src/main.rs` / `src/wapi.rs` and are called from ClojureScript bridge namespaces under `frontend/src/app/render_wasm*`.
- Updates are two-phase: ClojureScript calls exported setters to push shape data, then `render_frame()` performs Skia drawing.
- Rendering is tile-based and shape data is stored separately from hierarchy.
## Source Areas
- `src/state*`: renderer state structures.
- `src/render/` and `src/render.rs`: tile/surface render pipeline.
- `src/shapes/` and `src/shapes.rs`: shape data and Skia drawing.
- `src/wasm/`, `src/wasm.rs`, `src/mem.rs`: JS/WASM memory and interop helpers.
- `src/math/` and `src/view.rs`: geometry and viewport helpers.
## Build Environment
`./build` sources `_build_env`, which sets the Emscripten paths and `EMCC_CFLAGS`. The WASM heap starts at 256 MB and uses geometric growth.
## Commands
From `render-wasm/`:
- Build/copy frontend artifacts: `./build`.
- Watch rebuild: `./watch`.
- Rust tests: `./test` or `cargo test <name>`.
- Lint: `./lint`.
- Format check: `cargo fmt --check`.
Do not change exported WASM function signatures without updating the corresponding frontend bridge and verifying the frontend renderer path.
@@ -0,0 +1,25 @@
# render-wasm FFI and Rendering Subtleties
## FFI state and errors
- The renderer uses one unsafe global `STATE`; the `with_state*` macros currently panic on invalid state pointer. Treat state pointer validity as critical, not recoverable.
- `#[wasm_error]` clears the error code on entry. Recoverable errors set code `0x01`, critical errors/panics set `0x02`, free the byte buffer, then panic so the CLJS bridge can catch and inspect `_read_error_code`.
- The frontend bridge maps `0x01` to `:non-blocking` and `0x02` to `:panic` in ex-data (`:type :wasm-error`). Check actual bridge code if changing names; older comments/docs may use different labels.
- WASM byte transfer is a single global slot. A caller that receives a pointer result must read and free it before another byte payload is written; errors free the slot via `#[wasm_error]`.
## Shape pool and loading
- Shapes are UUID-indexed, and hierarchy/structure is tracked separately. `ShapesPool::get` may return a cached modified clone when modifiers, structure, scale-content, or bool handling apply; `get_raw` bypasses those derived values.
- Bulk loading uses a `loading` flag. `touch_current` / `touch_shape` avoid tile invalidation while loading; text layouts and final view setup must happen after loading ends.
- Many setters mutate only the current shape selected by `use_shape` / current-shape APIs. If no current shape is selected, some mutation blocks are skipped silently.
- `set_parent_for_current_shape` only sets parent metadata and invalidates parent geometry; children must be updated separately to avoid duplicate children.
- Child deletion marks descendants deleted and removes them from all indexed tiles, preserving undo/redo while avoiding stale pixels after panning.
## Tile/render behavior
- Interactive transforms are distinct from viewport fast mode. `set_modifiers_start` enables fast mode and interactive transform; interactive transform still flushes each animation frame.
- During interactive transform, modifier tile invalidation is deferred to `render()` once per rAF. Outside interactive transform, `set_modifiers` rebuilds modifier tiles immediately.
- `set_modifiers_end` disables fast/interactive state and cancels pending async render; the caller must request the final full-quality render.
- Plain viewport fast mode (`options.is_viewport_interaction()`) renders from cache and does not flush target output inside `process_animation_frame`; interactive transforms do flush.
- Zoom changes rebuild the tile index while preserving cached tile textures. Avoid replacing that path with shallow rebuilds if blur/shadow cache preservation matters.
- Pending tile priority is intentionally reversed by pop order; check the queue construction before changing tile scheduling.
@@ -0,0 +1,23 @@
# Creating Commits
Commit only on explicit request. Before commit: `git status`; exclude unrelated user changes.
Do not guess or hallucinate git author information (Name or Email). Never include the
`--author` flag in git commands unless specifically instructed by the user for a unique
case; assume the local environment is already configured. Allow git commit to
automatically pull the identity from the local git config `user.name` and `user.email`.
## Message Format
```
:emoji: Subject line (imperative, capitalized, no period, <=70 chars)
Body explaining what changed and why.
Co-authored-by: model-name <model-name@penpot.app>
```
## Commit Type Emojis
`:bug:` bug fix · `:sparkles:` enhancement · `:tada:` new feature · `:recycle:` refactor · `:lipstick:` cosmetic · `:ambulance:` critical fix · `:books:` docs · `:construction:` WIP · `:boom:` breaking · `:wrench:` config · `:zap:` perf · `:whale:` docker · `:paperclip:` other · `:arrow_up:` dep upgrade · `:arrow_down:` dep downgrade · `:fire:` removal · `:globe_with_meridians:` translations · `:rocket:` epic/highlight
@@ -0,0 +1,160 @@
# Creating Issues
Create GitHub issues only on explicit request. Use `gh` CLI authenticated to `penpot/penpot`.
## Title Derivation
Derive the title from the source material (bug report, user feedback, feature request, etc.) — not from any pre-existing title which may be auto-generated or stale.
### Bug titles (descriptive present tense)
Describe the symptom as it appears to the user. Format: `[Where] [present-tense verb] when [condition]`.
- *"Plugin API crashes when setting text fills"*
- *"Canvas renders glitches when zooming quickly"*
- *"French Canada locale falls back to French (fr) translations"*
Do **not** start bug titles with "Fix" or any imperative verb — state what's broken, not command a fix.
### Feature / Enhancement titles (imperative mood)
Command what should be built. Format: `[Imperative verb] [what] in/on [where]`.
- *"Add customizable dash and gap length controls to dashed strokes in the sidebar"*
- *"Show user, timestamp, and hash in the workspace history panel like git commits"*
### Universal rules
- **Include the "where"** — specify the UI location or module (e.g. "in the sidebar", "on the stroke options")
- **No prefixes** — strip `bug:`, `feature:`, `feat:`, `:bug:`, `:sparkles:`, `[PENPOT FEEDBACK]`, etc.
- **No emoji** — plain text only
- **Be specific** — prefer concrete detail over generality
- **Two problems → cover both** — if the description has two distinct but related issues, capture both joined by "and"
## Metadata
| Field | Rule |
|-------|------|
| **Labels** | `bug` (crashes/regressions) · `enhancement` (new features) · `community contribution` (PRs from non-core) · skip workflow labels (`backport candidate`, `team-qa`) |
| **Milestone** | Use the current or next planned milestone. Fetch available milestones: `gh api repos/penpot/penpot/milestones --jq '.[].title'`. If unsure, omit. |
| **Project** | Always `Main` (project number 8). Use `--project "Main"` flag. |
| **Issue Type** | See Issue Type section below. Cannot be set via `gh issue create` — use GraphQL after creation. |
## Issue Body Template
Write the body to a temp file to avoid shell quoting issues:
**Bug template:**
```markdown
### Description
<what breaks, what the user experiences>
### Steps to reproduce
1. <step 1>
2. <step 2>
### Expected behavior
<what should happen instead>
### Affected versions
<version>
```
**Enhancement template:**
```markdown
### Description
<what the user can now do that they couldn't before>
### Use case
<why this is useful, who benefits>
### Affected versions
<version>
```
## Creating the Issue
```bash
cat > /tmp/issue-body.md << 'ISSUE_BODY'
<body content here>
ISSUE_BODY
gh issue create \
--repo penpot/penpot \
--title "<Derived title>" \
--label "<label>" \
--project "Main" \
--body-file /tmp/issue-body.md
```
Output: `https://github.com/penpot/penpot/issues/<NUMBER>`
## Setting the Issue Type
`gh issue create` can't set Issue Type directly. Use GraphQL after creation.
**Issue Type IDs for penpot/penpot:**
| Type | ID |
|------|----|
| Bug | `IT_kwDOAcyBPM4AX5Nb` |
| Enhancement | `IT_kwDOAcyBPM4B_IQN` |
| Feature | `IT_kwDOAcyBPM4AX5Nf` |
| Task | `IT_kwDOAcyBPM4AX5NY` |
| Question | `IT_kwDOAcyBPM4B_IQj` |
| Docs | `IT_kwDOAcyBPM4B_IQz` |
**Map:**
- `bug` label → Bug
- `enhancement` label → Enhancement
- Feature/epic → Feature
- Docs → Docs
- None of the above → Task
**Set it:**
```bash
ISSUE_ID=$(gh api graphql -f query='
query { repository(owner: "penpot", name: "penpot") {
issue(number: <NUMBER>) { id }
}}' --jq '.data.repository.issue.id')
gh api graphql -f query='
mutation {
updateIssue(input: {
id: "'"$ISSUE_ID"'"
issueTypeId: "<TYPE_ID>"
}) {
issue { number issueType { name } }
}
}'
```
## Verification
```bash
gh issue view <NUMBER> --repo penpot/penpot \
--json title,labels,milestone,projectItems \
--jq '{title, milestone: .milestone.title, labels: [.labels[].name], projects: [.projectItems[].title]}'
gh api graphql -f query='
query { repository(owner: "penpot", name: "penpot") {
issue(number: <NUMBER>) { issueType { name } }
}}' --jq '.data.repository.issue.issueType.name'
```
## Cleanup
```bash
rm -f /tmp/issue-body.md
```
## See Also
- Creating issues **from PRs** (separating WHAT from HOW): `mem:workflow/creating-prs`
+62
View File
@@ -0,0 +1,62 @@
# Creating Pull Requests
PR only on explicit request. Branch: issue/feature-specific; fallback `<type>/<short-description>` (`fix/...`, `feat/...`, `refactor/...`, `docs/...`, `chore/...`, `perf/...`).
## Title Format
PR titles follow commit title conventions:
```
:emoji: Subject line (imperative, capitalized, no period, <=70 chars)
```
See `mem:workflow/creating-commits` for emoji codes. Squash merge uses the PR title as the final commit subject, so title format matters.
## Description Body
Include concise sections covering:
- what changed and why;
- related GitHub issues or Taiga stories (`Fixes #NNNN`, `Relates to #NNNN`, `Taiga #NNNN`);
- screenshots or recordings for UI-visible changes;
- testing performed and residual risk;
- breaking changes or migration notes, if any.
PR descriptions follow this structure:
```markdown
**Note:** This PR was created with AI assistance as part of the Penpot MCP self-improvement initiative.
## What
<one paragraph: the problem or feature, user-facing impact>
## Why
<root cause or motivation, why this change was necessary>
## How
<high-level approach, key technical decisions>
```
The "Note:" line is required at the top. Adjust if this is a manual (non-AI) PR.
## Writing Principles
- **Write for humans.** The diff shows what changed. The description explains why.
- **Be concise.** Focus on reasoning: What was the problem? Why did it happen? How did you solve it?
- **Skip the obvious.** Don't explain what `git diff` already shows.
### What NOT to Include
- ❌ List of files changed (visible in diff)
- ❌ Testing steps (CI handles this)
- ❌ Screenshots unless UI-visible
- ❌ Migration notes unless breaking changes
- ❌ Regression fixes introduced during the PR (they're part of the development process, not the feature)
## Before Opening
- Follow `mem:workflow/creating-commits` for commits
- Run the focused tests/lints appropriate to touched modules.
- Do not force-push during review unless the maintainer workflow explicitly asks for it.
+19
View File
@@ -0,0 +1,19 @@
# Docs Workflow
`docs/`: Penpot documentation site; Eleventy.
## Layout and Tooling
- `docs/package.json`: Eleventy commands and documentation-site dependencies.
- `docs/README.md`: local setup and tooling notes.
- Diagrams may use PlantUML, svgbob, mermaid, and C4/arc42 conventions depending on the existing page.
## Commands
From `docs/`:
- Install deps if needed: `pnpm install`.
- Local server: `pnpm start` or `pnpm run serve` (serves on `http://localhost:8080`).
- Build: `pnpm run build`.
- Watch: `pnpm run watch`.
Documentation changes should follow the existing page structure and rendered Help Center conventions rather than inventing a new style locally.
+142
View File
@@ -0,0 +1,142 @@
# the name by which the project can be referenced within Serena
project_name: "penpot"
# list of languages for which language servers are started; choose from:
# al ansible bash clojure cpp
# cpp_ccls crystal csharp csharp_omnisharp dart
# elixir elm erlang fortran fsharp
# go groovy haskell haxe hlsl
# java json julia kotlin lean4
# lua luau markdown matlab msl
# nix ocaml pascal perl php
# php_phpactor powershell python python_jedi python_ty
# r rego ruby ruby_solargraph rust
# scala solidity swift systemverilog terraform
# toml typescript typescript_vts vue yaml
# zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- clojure
- typescript
- rust
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:
# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}
# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:
# list of mode names that are to be activated by default, overriding the setting in the global configuration.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
# for this project.
# This setting can, in turn, be overridden by CLI parameters (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: |
CRITICAL: Read the memory `critical-info` IMMEDIATELY. It is absolutely critical for your work on this project - as is
the Serena Instructions Manual. Do NOT wait for further instructions, read these critical items NOW!
# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:
# list of regex patterns which, when matched, mark a memory entry as readonly.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []
# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_modes:
# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos).
# Paths can be absolute or relative to the project root.
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
# symbols and references across package boundaries.
# Currently supported for: TypeScript.
# Example:
# additional_workspace_folders:
# - ../sibling-package
# - ../shared-lib
additional_workspace_folders: []
-40
View File
@@ -1,40 +0,0 @@
dist: xenial
language: generic
sudo: required
cache:
directories:
- $HOME/.m2
services:
- docker
branches:
only:
- master
- develop
install:
- curl -O https://download.clojure.org/install/linux-install-1.10.1.447.sh
- chmod +x linux-install-1.10.1.447.sh
- sudo ./linux-install-1.10.1.447.sh
before_script:
- env | sort
script:
- ./manage.sh build-devenv
- ./manage.sh run-frontend-tests
- ./manage.sh run-backend-tests
- ./manage.sh build-images
- ./manage.sh run
after_script:
- docker images
notifications:
email: false
env:
- NODE_VERSION=10.16.0
-9
View File
@@ -1,9 +0,0 @@
{
"files.exclude": {
"**/.clj-kondo": true,
"**/.cpcache": true,
"**/.lsp": true,
"**/.shadow-cljs": true,
"**/node_modules": true
}
}
-11
View File
@@ -1,11 +0,0 @@
enableGlobalCache: true
enableImmutableCache: false
enableImmutableInstalls: false
enableTelemetry: false
httpTimeout: 600000
nodeLinker: node-modules
+79
View File
@@ -0,0 +1,79 @@
# AI AGENT GUIDE
## CRITICAL: Read module memories BEFORE writing any code
Do this **before planning, before coding, before touching any file**:
1. Read `critical-info` (use `serena_read_memory critical-info` or read `.serena/memories/critical-info.md`).
It describes the project structure and tells you which modules exist.
2. From `critical-info`, identify which modules your task affects.
3. Read each affected module's **core memory** — the name is `<module>/core`
(e.g. `frontend/core`, `backend/core`, `common/core`).
4. If the core memory references deeper `mem:` memories relevant to your task, read those too.
**STOP: Do not proceed until you have read the core memory of every affected module.**
Skipping this step is the #1 cause of incorrect or incomplete work.
---
# Memory system
Memories are the **primary project guidance** — not docs or readme files.
They are dense, agent-oriented notes: terse bullets, invariants, no prose.
## Entry point
Start at `critical-info` (the graph root). It describes the project structure,
module dependency graph, and references section-level core memories.
## Progressive discovery model
Memories form a **reference graph**, not a flat list:
```
critical-info ← read first (graph root)
└─ <section>/core ← top-level memory per section (e.g. frontend/core, backend/core)
└─ <topic> ← focused memories (e.g. frontend/handling-errors-and-debugging)
└─ ... ← deeper memories as needed
```
When working on a task:
1. Read `critical-info` to identify which sections are affected.
2. Read the affected section's `core` memory for an overview.
3. Follow `mem:` references in the core memory to focused memories relevant to your task.
4. Continue following references deeper as needed.
## Accessing memories
- **If `serena_read_memory` / `serena_list_memories` tools are available**: use them.
`serena_read_memory` takes a memory name (e.g. `critical-info`, `frontend/core`).
- **If tools are NOT available**: read the filesystem directly.
Memory name `mem:foo/bar` maps to file `.serena/memories/foo/bar.md`.
## Cross-reference convention
Memories reference other memories with `mem:<section>/<name>` inside backticks.
Example: `mem:common/changes-architecture`.
When you encounter a `mem:` reference relevant to your task, read that memory next.
## Topic/folder organization
Memories are grouped into folders that mirror project modules or topics:
`backend/`, `common/`, `frontend/`, `render-wasm/`, `exporter/`, `workflow/`, etc.
Each folder's top-level memory is `<folder>/core`.
---
# Role: Senior Software Engineer
You are a high-autonomy Senior Full-Stack Software Engineer. You have full
permission to navigate the codebase, modify files, and execute commands to
fulfill your tasks. Your goal is to solve complex technical tasks with high
precision while maintaining a strong focus on maintainability and performance.
## Operational Guidelines
1. Before writing code, describe your plan. If the task is complex, break it
down into atomic steps.
2. Be concise and autonomous.
3. Do **not** touch unrelated modules unless the task explicitly requires it.
+712 -87
View File
File diff suppressed because it is too large. Load diff
Loaded 100 of 2886 files, more files were not shown because too many files have changed in this diff. Show more