* ⬆️ Update devenv dependencies
Update Node.js, OpenCode, clj-kondo, Babashka, Pixi, GitHub CLI, uv,
and Serena to their current stable releases.
AI-assisted-by: gpt-5.6-sol
* ⬆️ Update devenv to Java 27
Use Zulu JDK 27 in the development image for compatibility testing.
Update the official checksums for both supported architectures.
AI-assisted-by: gpt-5.6-sol
* 🐳 Replace MinIO with RustFS in devenv
Run RustFS as the development S3 service and wait for its health check.
Install a pinned AWS CLI with checksums and use it to create the bucket
idempotently from each backend entry point.
Keep the old MinIO volume untouched and use a new RustFS volume.
AI-assisted-by: gpt-5.6-sol
* 🐳 Replace MailCatcher with persistent Mailpit
Run Mailpit as the devenv SMTP sink while preserving mailer:1025 and the
localhost:1080 UI.
Store its SQLite inbox in a named volume and wait for the readiness
endpoint before starting runtime containers. Bind the web UI to loopback so
development emails stay local.
AI-assisted-by: gpt-5.6-sol
* ⬆️ Update Node.js to 24.21.0
Align the host NVM version with the Node.js version used by devenv.
AI-assisted-by: gpt-5.6-sol
* ⬆️ Update devenv to PostgreSQL 18.6
Run PostgreSQL 18 with its versioned volume layout and a TCP readiness
check that ignores the temporary initialization server.
Install the matching client, create penpot_nexus, and preserve the old
PostgreSQL 16 volume for rollback or logical migration.
AI-assisted-by: gpt-5.6-sol
* 🐳 Expose RustFS ports in devenv
Publish the RustFS S3 API and management console on localhost port 9000
and 9001.
Keep both bindings on loopback so object storage is not exposed to the local
network.
AI-assisted-by: gpt-5.6-sol
* 🐳 Install standalone pnpm in devenv
Install pnpm 12.5.0 from architecture-specific release archives and
verify their published checksums.
Remove the Corepack setup while allowing pnpm to honor the project
packageManager pins.
AI-assisted-by: gpt-5.6-sol
* 🔥 Remove corepack, use system pnpm everywhere
Corepack is gone from Node 25+, so every `corepack enable` call
fails. pnpm now ships as a system binary (devenv, CI runners and
Docker images install it directly) and auto-downloads the version
pinned in `packageManager` on mismatch.
Scripts, workflows and Dockerfiles call `pnpm` straight away; the
three deploy workflows use a single `pnpm/setup@v2` step; and the
new `scripts/sync-pnpm-version` stamps all 35 `packageManager`
fields from the system pnpm, replacing the `corepack use` sweep.
AI-assisted-by: muse-spark-1.3-contributor
* 🐛 Fix exporter watch missing render-wasm build step
The exporter watch compiled CLJS requiring the generated
src/app/wasm/shared.js, which only render-wasm/build export
produces. Without it shadow-cljs failed with a cryptic missing
./shared.js dependency. Run build:wasm before watching, as
the frontend watch:app and exporter scripts/build already do.
AI-assisted-by: muse-spark-1.3-contributor
* 🔧 Add opencode V2 support and adapt plugins
Register the penpot tools for both opencode V1 (server())
and V2 (setup() with JSON Schema inputs) from a single
dependency-free plugin file, sharing the psql and
paren-repair runners between both paths.
Install the opencode2 binary side-by-side with V1 in the
devenv image and document the dual registration in the
paren-repair and psql memories.
AI-assisted-by: muse-spark-1.3-contributor
* ⬆️ Update pnpm and opencode
* ✨ Auto link tokens when adding external libraries (provisional)
* 🔧 Refactor tokens-lib initialization
* 🔧 Add separated TokenStatus to store status apart of TokensLib
* 🔧 Make all status operations use the new data structure
* 🔧 Normalize status helper functions and access token sets by id
* 🔧 Rename :tokens-file to :tokens-source
* 🎉 Allow the user to choose the tokens-source of a file
* 🎉 Make tokens library readonly when it's in an external file
* 🎉 Show tokens in library summaries
* 🎉 Show source info in sidebar
* 🔧 Fix integration tests
* 🐛 Propagate changes of token values in external library
* 🎉 Layout updates
* 🔧 Refactor tokens source calculations
* 🔧 Add harder checks for nil or empty values in everything
* 🐛 Fix some integration tests
* 🔧 Add integration tests for tokens in external libs
* 🔧 Validate and repair missing tokens status
* 🎉 Make ui changes optional with config flag
* 🐛 Propagate tokens after synchronizing components in ext library
* 🐛 Propagate tokens after creating new instances
* 🐛 Propagate tokens after synchronizing tokens in ext library
* 🐛 Add a tokens source icon to libraries section (#11439)
* 🐛 Add a tokens source icon to libraries section
* 🐛 Fix ellipsis on library names
* ♻️ Remove code under flag on legacy component
* 🐛 Fix token theme name on inspect tab
* 🎉 Add changes notification (#11476)
* 🎉 Add changes notification
* ♻️ Change fn names
* 🐛 Fix tokens source label truncation and missing translations (#11533)
* 🐛 Fix tokens source label truncation and missing translations
The tokens source file name always showed, even for the current file,
and long names wrapped onto a second line instead of truncating
because the header used flex-wrap and overflow-wrap: break-word
instead of single-line ellipsis.
Show the source row unconditionally (it now displays "This file" when
the source is the current file, matching the connected-library case),
truncate the file name to one line with an ellipsis, and only attach a
tooltip with the full name when the text is actually truncated.
Replace the hardcoded UI strings with translated ones and add their
English and Spanish entries.
AI-assisted-by: claude-sonnet-5
* 🐛 Remove redundant effect dependency in tokens source
file-name-truncated? was listed as a dependency of the with-effect
that checks and observes label truncation, even though it isn't
read inside the effect body. Since the effect itself flips that
state via check-file-name-truncated, including it as a dependency
caused the ResizeObserver to be needlessly disconnected and
reconnected on every truncation change.
AI-assisted-by: claude-sonnet-5
* 🐛 Fix small visual error
* 🐛 Fix problem with plugins
* 🐛 Fix playwright tests
---------
Co-authored-by: Eva Marco <evamarcod@gmail.com>
Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Run `corepack use pnpm@next-12` (resolved to 12.3.4) on every directory
with a package.json: the repo root, the 11 module workspaces, and all
submodules. Every packageManager field now carries the same pinned
version and hash; the root and backend move off 11.20.0.
Fix the composable-test-suite workspace config (esbuild allowBuilds
placeholder left by pnpm 12) so its install passes, and add the missing
packageManager fields to frontend/packages/ui and mcp/packages/plugin,
since corepack only updates existing fields.
Document the canonical update procedure in
.serena/memories/workflow/updating-pnpm.md.
AI-assisted-by: omen-alpha
* ⬆️ Update pnpm and its deps
* ⬆️ Update JVM dependencies in backend and common
Update several JVM dependencies across backend and common:
- passay 1.6.6 -> 2.0.0 (package reorg, ctor-based rules)
- siphash 2.0.0 -> 3.0.0 (SipHasher* renamed to SipHash*)
- lettuce-core, guava, sqlite-jdbc, jsoup, lz4-java, markdown-clj,
awssdk s3/sts, selmer, jackson-core/databind, shadow-cljs
Adapt passay validation to the new API (moved packages, constructor
configuration) and siphash to the renamed classes. Add tests for
password validation and UUID advisory-lock hashing.
AI-assisted-by: deepseek-v4-flash
* ⬆️ Update node on docker images
* 📎 Minor fixes related to pnpm12 compatibility
* ✨ Auto-link libraries during import based on slugified name
When a Penpot file is exported without bundled libraries and then
imported into a different environment, external library links are
broken because library UUIDs differ across environments.
This feature adds a heuristic to auto-relink libraries by matching
slugified library names against shared files in the target team:
- Export: embed external library metadata (id, name, slug, used-by)
in the manifest when libraries are not included in the export.
- Import: resolve external libraries by slugifying shared file names
in the destination team and matching against manifest slugs.
- Single match: auto-link silently (creates file-library-rel row).
- Multiple matches: emit SSE event so the frontend shows a selection
dialog for the user to pick the correct library.
- No match: import continues without linking (current behavior).
Backend changes:
- Extended manifest schema with optional :external-libraries field
- Added slugify-name, get-files-names, get-shared-files-for-team,
find-shared-files-by-slug helpers in app.binfile.common
- Threaded team-id into import cfg from RPC layer
- Added resolve-external-libraries and auto-link-libraries in v3
- Emit :library-candidates SSE event for multi-match cases
Frontend changes:
- Worker captures library-candidates SSE events and forwards them
- Import dialog shows auto-link notification and multi-match
selection UI with select dropdowns
- Added link-files-to-library! RPC helper for user selections
- Added en/es translations for new UI strings
Closes#9263
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ✨ Add UI for the auto-link plumbing
* ♻️ Consolidate auto-link libraries with unified export-type and fix ref integrity
Consolidates the auto-link libraries feature into a single coherent implementation:
- Unify boolean flags (embed-assets, include-libraries, link-later)
into single ::bfc/export-type parameter
- Fix critical reference-integrity bug: pre-resolution no longer remaps
:component-file refs when no link is created (multi-match / no-permission)
- compute-link-decisions as single source of truth for auto-link logic
- 80+ backend tests covering round-trip, cross-team, permissions,
edge cases, and reference integrity
AI-assisted-by: longcat-2.0
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Eva Marco <evamarcod@gmail.com>
* ✨ Adds static dispatch safe stubs in tests
* 🐛 Fix shapesColors metadata key to match ColorShapeInfo
* 🐛 Fix CommentThread.remove rejecting the owner's own threads
* 🐛 Fix page.removeCommentThread throwing on a spurious Promise
* ✨ Implement ShapeBase.swapComponent in the plugin API
* ✨ Expose File.revn in the plugin API
* 🐛 Fix FileVersion.createdAt calling Luxon method on a js/Date
* 🐛 Fix plugin font/typography application to text and ranges
* 🐛 Default plugin overlay interaction position for non-manual types
* 🐛 Fix plugin interaction setters passing an id-only shape
* 🐛 Fix grid addColumnAtIndex rejecting valid track types
* 🐛 Expose libraryId on library color/typography/component proxies
* ✨ Implement LibraryTypography.setFont in the plugin API
* 🐛 Fix typography.applyToTextRange reading unexposed range bounds
* 🐛 Fix utils.geometry.center argument mismatch
* 🐛 Fix localStorage.removeItem calling getItem
* 🐛 Fix shape backgroundBlur proxy key casing
* 🐛 Report boolean shape type as 'boolean' in the plugin API
* 🐛 Return the resulting paths from plugin flatten
* 🐛 Make plugin z-order methods act on the target shape
* 🐛 Make is-variant-container? return a boolean
* ✨ Implement Group.isMask in the plugin API
* 🐛 Return a shape proxy from TextRange.shape
* 🐛 Return the duplicated set from TokenSet.duplicate
* 🐛 Fix theme addSet/removeSet reading set name with a keyword
* 🐛 Accept string fontFamilies token value in the plugin API
* 🐛 Fix combineAsVariants ignoring the passed component ids
* 🐛 Fix board removeRulerGuide ignoring its argument
* 🐛 Fix board guides setter schema and parser
* 🐛 Avoid 0-byte allocation when syncing empty grid tracks
* 🐛 Validate grid track indices in the plugin API
* 🐛 Return null for empty input in group() and centerShapes()
* 🐛 Return TokenTypographyValue[] from a typography token's resolvedValue
* 🐛 Return TokenShadowValue[] from a shadow token's resolvedValue
* 🐛 Return string[] from a fontFamilies token's resolvedValue
* 🐛 Clear mutually-exclusive reps when setting LibraryColor gradient/image
* 🐛 Add readonly tags to types, deprecate Image type
* 📚 Update plugins changelog
* 🎉 Add background blur
* 🎉 Add test
* 🎉 Add background blur info to plugins API
* 🎉 Suport in wasm for both layer and background blur
* 🐛 Fix failing test
* ♻️ Fix comments
---------
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Change isVariant() return type from boolean to 'this is LibraryVariantComponent',
enabling TypeScript users to directly access variants, variantProps, and
variantError after a type-narrowing check. Update MCP instructions with
improved variant navigation guidance.
Closes#9185
Co-authored-by: Claude (Anthropic) <noreply@anthropic.com>
* 🐛 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>
- 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>
- 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>
* ✨ Add core changes for mcp server
* ✨ Changes to plugins-runtime to add mcp extensions
* ✨ Changes to MCP plugin
* ✨ Changes post-review and ci fixes
* 🎉 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>