Penpot only accepts jpeg, png, webp, gif and svg images, so the
EXR coder was never used. It was enabled only because ImageMagick's
configure auto-detected libopenexr-dev at build time.
OpenEXR accounted for 25 CVEs (15 High) in both the exporter and
media-processor images, and was also bundled into the backend via
/opt/imagick/lib/deps, where dpkg-based scanners cannot see it.
- Build ImageMagick with --without-openexr and drop libopenexr-dev
- Drop libopenexr-3-1-30 from the imagemagick, backend, exporter,
media-processor and devenv images
- Remove `apt-get upgrade` from the ImageMagick build stage
- Bump penpotapp/imagemagick to 7.1.2-27-1
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
* 🐛 Migrate openUIApi schema to Zod v4 function syntax
Zod 4 removed z.function().args(), which broke the
plugins-runtime build with implicit-any errors on every
openUIApi parameter and knock-on possibly-null errors on
the modal in plugin-manager.
Declare the inputs with z.function({ input: [...] }) so the
parameter and return types infer again; behavior is unchanged.
Add a regression spec covering delegation, optional args and
rejection of invalid theme and title values.
AI-assisted-by: muse-spark-1.3-contributor
* 📚 Fix deprecated markdown-it-anchor permalink option in docs
Migrate docs Eleventy config to the markdown-it-anchor v10 API.
Replace the deprecated boolean permalink option with
linkInsideHeader, keeping the same symbol and class.
Bump markdown-it-anchor to v10 and related docs deps.
AI-assisted-by: muse-spark-1.3-contributor
* ⬆️ Update deps
* ⬆️ Update base docker images
* 📎 Fix mcp tests
Parallel deploy jobs on the shared self-hosted host raced on
/home/runner/setup-pnpm (ENOTEMPTY / missing tarball errors).
Use penpotapp/devenv, which already ships the Node and pnpm versions
pinned in develop, and mount a persistent pnpm store from the host
instead of pnpm/setup + actions/cache.
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
Bump the devenv and image Dockerfiles to pnpm 12.6.0
(latest stable; 12.8.0 is still on the next tag) with
fresh SHA256 pins, and opencode to 1.18.33 plus
opencode2 to 2.0.18 with fresh checksums.
Stamp all 35 package.json files via
scripts/sync-pnpm-version and refresh the 11 lockfiles;
diffs are metadata-only, with no dependency re-resolution.
Drop the last corepack calls from the media-processor
build script and its generated image setup: pnpm now
ships as a system binary and self-heals version drift.
AI-assisted-by: muse-spark-1.3-contributor
Creating a typography from a text whose font is no longer installed
baked the broken font-id into a new asset. With several texts selected
there is no single style to capture either. The add-typography event
now does nothing in both cases, and the "Add typography" button in the
local library is disabled with a label that explains why.
The button lives in its own component so selection, shape and editor
changes re-render only the button, not the typography list, and shared
libraries do not subscribe to that state at all. The event and the
button read the editor data through the new `editor-text-options`, so
both see the same font for the wasm, v2 and v1 text editors.
AI-assisted-by: claude-opus-5-5
* ✨ Add renderer option on create-demo-profile command
Allow create-demo-profile to take an optional renderer (svg or
wasm) and store it on profile props. When omitted, no renderer is
written so the Penpot default still applies.
Closes#11893
AI-assisted-by: Muse Spark 1.3 Free
* 📚 Fix renderer doc version on create-demo-profile
Correct the ::doc/changes version for the new renderer param from
2.20 to 2.18.1.
AI-assisted-by: Muse Spark 1.3 Free
* 🐛 Update canvas background when its color token changes
Token propagation only walked the shapes of each page, so a canvas
background linked to a color token kept its old value after switching
the active set or editing the token. Propagation now also updates the
background of every page whose `:background-token` resolves to a new
color, inside the same undo transaction.
AI-assisted-by: claude-opus-5-5
* 🐛 Select the dragged token set by id instead of by path
Starting a drag on an unselected token set stored its path as
`:selected-token-set-id`. The sidebar then crashed on the
`(uuid? force-set-id)` assert of `get-tokens-in-active-sets-force`.
This could happen when toggling a set checkbox with a slight mouse
move.
AI-assisted-by: claude-opus-5-5
* 🎉 Add playwright test
Values coming from `${{ }}` expressions were interpolated directly into
`run:` scripts, so GitHub substituted them into the shell source before
bash parsed it. A commit title containing a double quote broke the
"Write step summary" step of the bundle build with a syntax error, and
the same pattern allowed arbitrary command execution on the
self-hosted runners.
Pass every expression used inside `run:` through step/job `env:` and
reference it as a quoted shell variable instead. Use the runner's
default variables (GITHUB_RUN_ID, GITHUB_REPOSITORY, ...) where the
value comes from the `github` context.
Also validate `plugin_name` in plugins-deploy-package.yml against
`^[a-z0-9][a-z0-9-]*$`, since it is free-form and reaches paths,
worker names, GITHUB_ENV and action inputs.
Affected workflows: build-bundle, build-docker,
build-docker-admin-console, plugins-deploy-package,
plugins-deploy-api-doc, plugins-deploy-styles-doc, release, tests-e2e.
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
Individual stroke widths shipped with a `:stroke-per-side` boolean on every
stroke, telling the renderer and the CSS generator whether the four
per-side widths were meaningful. Comparing the sides answers that on its
own, so the attribute was later dropped from the closed stroke schema
and the toggle became ephemeral editor state.
Files written between those two changes still carry the attribute, and
the closed schema rejects the unknown key, so loading such a file fails
`check-file-data` with a `:malli.core/extra-key` error and surfaces as
an internal error in the editor.
Add migration `0030-remove-stroke-per-side-attr`, which drops the
attribute from every stroke of every page and component shape. The
per-side widths are the saved design data and are kept untouched, so a
file whose boolean was `true` renders exactly as before.
The migration is naturally idempotent and a no-op for files that never
carried the attribute, since `dissoc` on a map without the key returns
an equal map.
Cover it with tests for the schema rejection, the per-side and global
widths surviving, component shapes, idempotency, and the run through
`migrate-file`.
Closes#11943
AI-assisted-by: space-bunny-free
* ✨ Enable stroke-per-side flag
* 🐛 Fix stroke per side tests for the enabled flag
The default flags now include :enable-stroke-per-side, so
frontend tests and Playwright specs that assumed the flag
was off need to turn it off explicitly.
Update the token context menu test expectations: rects and
boards now expose the stroke-width submenu, and force the
flag off in the "per-side is disabled" cases.
Pass disable-stroke-per-side in the two Playwright specs
that check the flag-off behavior, since app.config always
merges the default flags.
AI-assisted-by: deepseek-v4.1-flash
When render-wasm/v1 is active, also enable text-editor-wasm/v1 so
the WASM text editor turns on with the renderer. Keep forcing
text-editor/v2 as before; the viewport still prefers the WASM
editor when both features are set. Classic unchanged.
Closes#11934
Relates to #11935
Firefox draws its own resize grip for a box with `resize: both` and,
unlike Chrome and Safari, gives no way to hide it: `::-webkit-resizer`
is ignored. The plugin modal therefore showed two grips in its
bottom-right corner, its own icon and the one from the browser.
Show the custom icon only where the browser grip can be hidden. In
Firefox the native grip is now the only one.
Closes#11795
* ✨ Show color tokens toggle on canvas background picker
* 🐛 Apply token click on canvas background with no shape selected
* ✨ Apply and persist color tokens on canvas background
* 🌐 Translate canvas background section label
* 🐛 Fix shadows derived from imported SVG filters
* 🐛 Fix inherited group fills in exports and group drop shadows
* 🔧 Run exporter tests on render-wasm changes
* 🐛 Allow invitation-based registration when disable-registration is set (#5178)
Per documentation, disable-registration 'disables registration
(still enabled for invitations only)'. Two bugs prevented this:
1. verify_token.clj: when processing an invitation token for a
non-logged-in user with no member-id, the redirect included
registration-disabled? in its condition, sending invited users
to the login page instead of the register page.
2. auth.clj validate-register-attempt!: the registration-disabled
check fired unconditionally before the invitation-token check,
rejecting the actual register RPC even with a valid invitation.
Fix: in verify_token.clj remove registration-disabled? from the
redirect condition for new-user invitations. In auth.clj restructure
the check as an if/else: with an invitation token, validate the token
and allow registration; without one, enforce the flag as before.
* 🐛 Allow registration with disabled public registration
Allow valid team invitations to create new profiles when public
registration is disabled, while keeping password login and invitation
validation required.
Add backend regression coverage for flag combinations and verify-token
redirects, frontend route coverage, and configuration documentation.
Closes#5178
AI-assisted-by: space-bunny-free
* 🐛 Revalidate active invitation during registration
Require a live, unexpired team invitation before using the
registration exception, and recheck it before creating a profile.
Reuse the same lookup in invitation token verification.
Add regression tests for canceled and expired invitations, the
registration race, and explicit redirect contracts. Update docs
and backend auth guidance.
AI-assisted-by: Space Bunny Free
* 🐛 Lock and normalize invitation registration checks
Lock active invitation rows during transactional registration and
acceptance so cancellations cannot race with profile or membership
creation.
Normalize invitation emails before comparisons and database lookups.
Add concurrency, email casing, and final flag regression tests.
AI-assisted-by: Space Bunny Free
---------
Co-authored-by: Sumit Ridhal <sridhal@redhat.com>