23987 Commits

Author SHA1 Message Date
Alejandro Alonso
dbe5941a23
✨ Export layer blur to WASM SVG (#11580)
SkSVGDevice drops paint image-filters, so re-emit visible layer blur as a
native feGaussianBlur filter on the composite <g>. Match canvas sigma via
radius_to_sigma(value * scale), and skip Skia blur filters on the SVG
VectorRenderer path so shapes do not vanish.

Closes #11380
2026-09-10 14:25:47 +02:00
makesomethingshit
30849babcc
🐛 Fix fontFamilies token property mapping in Plugin API (#11566)
* 🐛 Fix fontFamilies token property mapping in Plugin API

The Plugin API exposes the font-family token property as `fontFamilies`,
while Penpot stores the canonical applied-token attribute as
`:font-family`. The bidirectional plugin/internal attribute map did not
contain that alias, so explicit `applyToken(..., ["fontFamilies"])`
validation rejected the property and applied-token readback exposed the
undocumented singular `fontFamily`.

Add `:font-family -> :font-families` to the existing canonical alias
map. The reverse mapping is derived automatically, keeping application
and readback symmetric without introducing a font-specific code path.

Closes #11405

AI-assisted-by: Omen Alpha
Signed-off-by: 최준수 <junsoo1172@gmail.com>

* 🐛 Fix fontFamilies e2e test to target a text shape

The fontFamilies end-to-end regression created a flex layout frame,
whose attribute set (frame-with-layout-attributes) excludes
:font-family. The workspace token application filters such shapes,
so the internal binding and readback assertions would pass vacuously
without exercising the alias.

Target an actual `:text` shape (ctho/add-text) instead, so the test
verifies the full JS "fontFamilies" -> schema -> alias -> canonical
:font-family -> camelCase readback path.

AI-assisted-by: Omen Alpha
Signed-off-by: 최준수 <junsoo1172@gmail.com>

* 🐛 Fix fontFamilies test WASM error and add changelog entry

The text-shape fontFamilies e2e applies a layout-affecting token via
wasm renderer path, hitting missing WASM exports under Node. Merge
thw/setup-wasm-mocks! into the :each fixture and add plugins
CHANGELOG entry for the fontFamilies alias fix.

AI-assisted-by: muse-spark-1.3-contributor
Related to #11566

Signed-off-by: makesomethingshit <junsoo1172@gmail.com>

---------

Signed-off-by: 최준수 <junsoo1172@gmail.com>
Signed-off-by: makesomethingshit <junsoo1172@gmail.com>
2026-09-10 13:43:41 +02:00
Alejandro Alonso
dc12f1db91
✨ Export image-filled strokes to WASM SVG as linked images (#11559)
Closes #11384

Skia's SVG backend drops save_layer+SrcIn, so image strokes are re-emitted
as a linked <image> clipped to an opaque stroke silhouette (filled outline,
clip-rule evenodd). Open-path caps join the silhouette and grow the image
dest by cap_bounds_margin so markers stay textured.
2026-09-10 13:29:14 +02:00
Luis de Dios
c589563912
♻️ Replace digit with number in password validations (#11609) 2026-09-10 12:15:58 +02:00
andrés gonzález
0eb3179016
💄 Adjust release notes 2.18 titles (#11608) 2.18.0-RC4 2026-09-10 11:50:57 +02:00
Andrey Antukh
d1ebf4cda2 📎 Update changelog 2026-09-10 10:41:10 +02:00
Andrey Antukh
fdb9e97572 📎 Update planner skill and AGENTS.md 2026-09-10 10:12:09 +02:00
Elenzakaleidos
9cd3b63eea
📚 Update README.md (#11602)
Added a new section for Penpot Enterprise detailing its features and benefits for organizations.

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2.18.0-RC3
2026-09-10 09:52:06 +02:00
David Barragán Merino
94555c027e 🔧 Sync .github/workflows with develop
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-10 09:47:10 +02:00
Elena Torró
9b24907992
🐛 Fix render text-decoration on spans and update regression tests (#11583) 2026-09-10 09:42:09 +02:00
Juan de la Cruz
b283d952a8
✨ Add new slides content for 2.18 release (#11222)
* ✨ Add new slides content for 2.18 release

* ♻️ Use buttons from DS

* ♻️ Use new SCSS guidelines

* ♻️ Use a base stylesheet for all version files

* ♻️ Use new SCSS guidelines

* ✨ Add new images and wording

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-09-10 09:35:34 +02:00
andrés gonzález
6f63a5fcbf
✨ Persist hide resolved comments preference (#10694)
Store the hide-resolved filter in user storage and restore it when
entering the workspace or viewer, consistent with canvas comment
visibility from #10239. Match the comments filter separator styling to
the main menu and add the missing mentions option in the viewer
dropdown.

Closes #10686

Signed-off-by: Andres Gonzalez <andres.gonzalez79@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:14:37 +02:00
Elena Torró
61caefbcf3
🔧 Support independent image bounds on wasm export (#11590) 2026-09-09 18:27:22 +02:00
Elena Torró
6586631293
🐛 Fix wasm boolean paths with opposite winding operand (#11551) 2026-09-09 18:19:29 +02:00
Andrey Antukh
66fb4a69ba 📎 Update copyright headers 2026-09-09 17:58:09 +02:00
Alejandro Alonso
ac84557740
🐛 Re-upload WASM text after WebGL context restore (#11589)
During reload-renderer!, reloading? keeps initialized?/ready? false
while set-objects runs (especially the sync path for small files).
Text content used that guard and was skipped; geometry already used
live?. Gate use-shape, has-shape, and set-shape-text-content on
wasm/live? so text is restored with the rest of the shapes.
2.18.0-RC2
2026-09-09 17:25:18 +02:00
Elena Torró
3d4a5ca2aa
🔧 Use mutex so only one test owns global at a time (#11585) 2026-09-09 17:10:04 +02:00
Danny Shirely
d45c6710b7
🎉 Implement independent image bounds resizing (#11430)
* 🎉 Implement independent image bounds resizing

Add canvas resize interaction mode that allows users to resize an
image object's bounding box independently from the underlying bitmap
content without scaling or distortion while holding the Mod key.

AI-assisted-by: gemini-2.5-pro

* ♻️ Address reviewer feedback from elenatorro

- Remove legacy cfh/image-shape? check in shape-has-image-fill?
- Guard bounds-resize with positive dimensions instead of clamping scalev to preserve flipping
- Remove :metadata from transform-attrs in modifiers.cljs
- Restore preserveAspectRatio logic based on keep-ar? in fills.cljs
- Compute source rect against destination rect for raster and SVG fills in WASM renderer

* 🔧 Fix clippy needless borrow warnings in wasm image fills

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-09 15:16:56 +02:00
Alonso Torres
bf2fcba3be
🐛 Fix problems with pixel precission in WASM renderer (#11560) 2026-09-09 15:11:58 +02:00
Alexis Morin
bb6472a300
🌐 Add translations for: French (Canada)
Currently translated at 96.5% (2349 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-09-09 14:51:33 +02:00
Andrey Antukh
43f7e49aa0 📎 Update changelog 2026-09-09 11:48:37 +02:00
Andrey Antukh
d6e82f44a6
✨ Enforce absolute maximum session duration (#11447)
Session tokens now carry an :exp claim anchored to created-at (not
modified-at), so activity cannot extend the session beyond the
absolute maximum (default 30 days, configurable via
PENPOT_AUTH_TOKEN_COOKIE_MAX_AGE_ABSOLUTE). The existing token
verification already rejects expired tokens, so enforcement is
automatic. Also extends the GC task to purge expired
http_session_v2 rows, which were previously never cleaned up.

Closes #11444

AI-assisted-by: longcat-2.0
2026-09-09 11:45:28 +02:00
Andrey Antukh
eca1d81692 🔧 Remove legacy pnpm build key and clarify updating doc
Drop the ignored-since-pnpm-11 onlyBuiltDependencies entry from
render-wasm/pnpm-workspace.yaml, keeping allowBuilds as the single
source of build approvals. Clarify the updating-pnpm gotcha so it no
longer claims pnpm writes ignoredBuiltDependencies.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-09 11:45:00 +02:00
Pablo Alba
d263c23a58
🐛 Add ssrf check for nitrate sso and add timeouts to http client (#11576) 2026-09-09 11:23:53 +02:00
Alejandro Alonso
e67ebacef2
🐛 Export svg-raw shapes through WASM SVG draw_svg (#11567)
SVG export treated SVGRaw like a fill/stroke leaf and never called
draw_svg, so imported SVG content exported as an empty document.

Relates to #10546
2026-09-09 11:16:01 +02:00
Andrey Antukh
91860f6916 📎 Update changelog 2026-09-09 11:12:57 +02:00
Andrey Antukh
831953c41e Merge remote-tracking branch 'origin/staging' into develop 2026-09-09 11:02:06 +02:00
Andrey Antukh
8a3540336b 📚 Add local-ci skill and scripts/ci memory
Teach agents to verify their changes with ./scripts/ci: module list,
task selection flags, log locations under .ci-logs/, and per-module
workflows (lint-only pass, --fix, --paren-repair, common/ consumer
checks). Register the skill in the skills README, point AGENTS.md at
the new memory, and add the script to the critical-info dev scripts.

AI-assisted-by: omen-alpha
2026-09-09 11:01:49 +02:00
Andrey Antukh
6f4a68100d ✨ Add direct mode to implement-plan flow
The flow only supported continue mode (existing branch) or standalone
mode (issue + branch). Add a "direct" argument (aliases: "no branch",
"direct commit") that skips both and commits on the current branch as
it is, even when it is a base branch — for small tooling changes the
user wants committed in place.

AI-assisted-by: omen-alpha
2026-09-09 11:01:49 +02:00
esther-moreno
62fdc02c0f
📚 Adding enterprise section in the user guide (#10821)
* 📚 Add enterprise section

* 📚 Update images

* 📚 Add SSO module section to Enterprise plan user guide

* 📚 Add more FAQs to Enterprise plan user guide

* 📚 Add SSO configuration screenshot to Enterprise plan user guide

* 📚 Replace membership screenshot with webp to fix broken reference

The membership image reference already pointed to .webp while the tracked
file was .png, leaving a broken image. Swap the asset to match.

* 📚 Document how to access the Admin Console

Add a paragraph explaining how to reach the Admin Console and an
accompanying screenshot to the Enterprise plan user guide.

* 📚 Update Admin Console access screenshot

* 📚 Open Enterprise plan images in a new tab on click

Wrap the enterprise screenshots in a target="_blank" link so they open
full size, matching the behavior on other user-guide pages. The
organization hierarchy diagram is left inline.

* 📚 Document identity provider configuration for SSO

* 📚 Refine SSO session, Admin Console docs, and screenshots

* 📚 Split Organizations content out of the Enterprise plan page

* 📚 Fix wording in Enterprise plan FAQ answers

---------

Co-authored-by: miryamgduque <miryam.gonzalez@kaleidos.net>
2026-09-09 10:59:17 +02:00
Andrey Antukh
c1bd3cb9f0 Merge remote-tracking branch 'origin/staging' into develop 2026-09-09 10:31:41 +02:00
Eva Marco
011feeaf71
🐛 Fix missing text in legacy SVG board thumbnails (#11552)
* 🐛 Fix missing text in legacy SVG board thumbnails

Board thumbnails rendered by frame-imposter used React's
renderToStaticMarkup, a synchronous pass with no live DOM to measure
text against. Text shapes without a persisted position-data value
therefore rendered as nothing, so a cached board thumbnail silently
lost its text until the board was hovered, selected, or the canvas
was zoomed past 130%, all of which bypass the cached thumbnail in
favor of live content.

frame-imposter now provides the same is-render? context the
standalone exporter already sets, so text without position-data
falls back to the synchronous foreignObject renderer instead of
rendering nothing.

Thumbnails cached before this fix stay broken until something
regenerates them, so on each page load, board thumbnails containing
text are opportunistically regenerated once per browser (tracked via
local-storage) so existing files self-heal without requiring an
edit.

AI-assisted-by: claude-sonnet-5

* ♻️ Use a transducer in heal-stale-text-thumbnails

Fixes a PR review comment: the frame filtering/mapping was spread
across four separate rx operators. Collapse it into a single
transducer pass over frame-ids, leaving only one rx/map to perform
the mark-healed side effect and build the update-thumbnail action.
2026-09-09 09:56:10 +02:00
Andrey Antukh
e96a75d366 ✨ Send telemetry event batch as plain JSON vector
The event batch sent to the telemetry server was encoded as a
fressian+zstd base64 blob. Send it as a plain vector of event maps
instead: the JSON encoder handles UUID and temporal types natively,
the payload becomes inspectable, and the receiver schema coerces
values back to proper types.

The receiver (penpot-telemetry) now accepts both the blob and the
plain vector, so it must be deployed before this backend change.

AI-assisted-by: omen-alpha
2026-09-09 09:39:30 +02:00
andrés gonzález
3b1290c6a2
📚 Update user guide for upcoming release features (#11458)
Document stroke to path, toolbar flyouts, line and arrow tools,
font previews, page multi-select, and dashboard grid/list view.

Relates to #9145, #10403, #10580, #10691
2026-09-09 09:36:48 +02:00
Shreyash
3f1c88fca2
📚 Document MCP and internal resolver environment variables (#11572)
Add the frontend MCP variables PENPOT_MCP_URI and PENPOT_MCP_URI_WS,
the PENPOT_INTERNAL_RESOLVER variable used by nginx to resolve the
internal service names, and the enable-mcp flag to the configuration
guide. All three were used by the frontend docker image but were
missing from the docs.

Closes #11318

AI-assisted-by: claude-opus-5

Signed-off-by: Shreyash Agare <envisiontechdevelopers@gmail.com>
2026-09-09 08:16:04 +02:00
Andrey Antukh
5c474939ac 🔧 Pin all pnpm workspaces to one shared pnpm store
Set storeDir in every pnpm-workspace.yaml: `.pnpm-store` at the repo
root and `../.pnpm-store` in the ten module workspaces, so all of them
resolve to <repo>/.pnpm-store. pnpm resolves the value against the
workspace root, and nested workspaces do not inherit settings, which
had left the root workspace and the modules on two different stores.

Add scripts/clean-node-modules: removes every workspace node_modules
in one pass (ignores external/ and .opencode/), keeps the shared store
unless --store removes it too.

Verified: every workspace resolves the same store path; reinstalls
after a full clean reuse the cache with zero downloads;
frozen-lockfile installs pass in all 11 workspaces with no lockfile
changes; the frontend storybook suite stays green.

AI-assisted-by: omen-alpha
2026-09-09 08:04:11 +02:00
Andrey Antukh
f68c266380 ♻️ Fold composable-test-suite into the plugins workspace
Drop the nested pnpm-workspace.yaml and pnpm-lock.yaml from
plugins/apps/composable-test-suite. It stays a plain member of the
plugins workspace (apps/** glob); its dependencies already resolve
through plugins/pnpm-lock.yaml, so no lockfile change is needed.

Update the pnpm update procedure memory to the single-workspace-per-
module layout, keep the invariant that members carry no nested
pnpm-workspace.yaml, and drop the now-stale 12-workspace count from
critical-info.

AI-assisted-by: omen-alpha
2026-09-09 08:04:11 +02:00
Andrey Antukh
05ea09f2ba ♻️ Make vite configs compatible with the native config loader
Replace __dirname with import.meta.dirname in the plugins workspace
vite configs (root paths and the plugins-runtime tsconfigPath) and in
frontend/vite.config.js, dropping the fileURLToPath fallback.

Import vite.config.iife with the explicit .ts extension in the
plugin-api-test-suite headless/tests configs and allow it in
tsconfig.node.json (moduleResolution Bundler + noEmit), matching the
mcp/packages/plugin precedent.

Remove .storybook/vitest.setup.ts and its setupFiles entry;
@storybook/addon-vitest 10.3+ provisions preview annotations
automatically.

These clear the Vite `configLoader: 'native'` warnings ahead of that
loader becoming the default.

AI-assisted-by: omen-alpha
2026-09-09 08:04:11 +02:00
Andrey Antukh
e4d1816117 ⬆️ Update pnpm to 12.3.4 across all workspaces
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
2026-09-09 08:04:11 +02:00
andrés gonzález
93b18fb1c1
✨ Add Penpot-specific board size presets (#11565)
Add a "PENPOT" section to the board size presets list with sizes for
File thumbnail, Template cover, Plugin icon and Plugin cover, so
designers no longer need to look up these dimensions manually when
creating thumbnails for files, template submissions or plugin
listings.

Closes #11561

AI-assisted-by: claude-sonnet-5

Signed-off-by: Andres Gonzalez <andres.gonzalez79@gmail.com>
2026-09-09 08:01:04 +02:00
Andrey Antukh
8fac9cf178 Merge remote-tracking branch 'origin/staging' into develop 2026-09-08 23:01:03 +02:00
Andrey Antukh
61ed2a203f ✨ Present full flow and step-by-step mode in implement-plan
Two changes to the implement-plan flow:

- It now shows the whole picture before touching the repo: which
  issue and branch will be created (or the current branch it
  continues on), the execution style, and the task checklist — then
  waits for the user's confirmation. Flow detection is read-only;
  issue and branch creation moved to the execution step.

- New opt-in execution mode: on request ("step by step"), it runs
  one task at a time, commits each one with the create-commit skill,
  and waits for the user's review before the next task. The default
  stays as it was: every task, one commit.

README and the implement-plan command description updated to match.

AI-assisted-by: omen-alpha
2026-09-08 21:00:32 +00:00
Andrey Antukh
3b49ff532f Merge remote-tracking branch 'origin/staging' into develop 2026-09-08 21:41:43 +02:00
Andrey Antukh
fbfef42145 ♻️ Move skills and plans to .agents for cross-tool use
.agents is the shared home that opencode, Claude Code (through the
.claude/skills symlink) and Codex all read, so the skills and the
saved plans now live there instead of .opencode:

- .opencode/skills moved to .agents/skills (24 files, no content
  changes).
- .opencode/plans moved to .agents/plans; the .gitignore entry
  follows, so plans stay untracked.
- .claude/skills symlink retargeted to ../.agents/skills.
- planner, make-a-plan and review-plan updated to the new plans path;
  new .agents/README.md documents every skill with when-to-use
  examples and links to each SKILL.md.
- workflow/creating-issues memory: create-issue path updated.

opencode discovers .agents/skills natively, so .opencode needs no
reciprocal link.

AI-assisted-by: omen-alpha
2026-09-08 19:40:38 +00:00
Andrey Antukh
f695553469 Merge remote-tracking branch 'origin/staging' into develop 2026-09-08 21:15:58 +02:00
Andrey Antukh
59c8a690da ♻️ Add when to use sections to all skills
Every skill in .opencode/skills now carries a "When to use" section:
triggers in any phrasing plus the matching /command for the flow
skills, one-line triggers for the utility skills, an
explicit-invocation mirror for ste, and the proactive case for
resolve-git-conflicts.

code-review-criteria drops its old usage bullets ("before merging any
PR ...") for the same role pattern as plan-review-criteria: loaded by
the reviewer subagent of the review-code flow, redirect there to
review code. Flow and criteria skills no longer compete for the same
trigger.

AI-assisted-by: omen-alpha
2026-09-08 21:12:28 +02:00
Andrey Antukh
b115b75d83 📚 Add CLAUDE.md pointer and Claude Code skills symlink
CLAUDE.md points Claude Code to AGENTS.md as the canonical project
instruction file, and .claude/skills symlinks the opencode skills so
both tools discover the same single source of truth. The root
.gitignore keeps ignoring new files under those paths; the tracked
entries are unaffected from now on.

AI-assisted-by: omen-alpha
2026-09-08 18:04:15 +00:00
Andrey Antukh
3d1393e8fc ♻️ Make commands thin dispatchers over canonical skills
Every command in .opencode/commands now only switches to the build
agent, injects the user context, and delegates to a same-named skill
that holds the full flow logic — skills are the primary mechanism for
Claude Code and Codex compatibility.

- make-a-plan, implement-plan, resolve-git-conflicts: flow logic
  moved to same-named skills.
- review-plan / review-code: orchestration skills delegating to the
  renamed criteria skills plan-review-criteria and
  code-review-criteria, with redirecting descriptions so there is no
  trigger overlap between flow and criteria.
- create-pr: absorbed the open-pr flow as Open mode (validated
  branch-to-PR pipeline using scripts/detect-target-branch) plus a
  new Update mode (gh pr edit); /open-pr removed.

AI-assisted-by: omen-alpha
2026-09-08 18:00:05 +00:00
David Barragán Merino
afe62e6592 ✨ Add force rebuild flag and adhoc ref builds
Add a `force` input to Bundles Builder, Docker Images Builder and the
admin-console dispatcher to bypass the existing S3/registry skip
checks and overwrite artifacts unconditionally. Propagate it through
_DEVELOP, _STAGING and _TAG (defaulting to false on non-dispatch
triggers, since schedule/push events carry no inputs).

Add a new _ADHOC workflow to build the full image set (bundle + all
docker images + nitrate admin-console) from an arbitrary ref pair,
for one-off builds of feature branches like test-bameda.

Enrich the build/promote step summaries with the built version
(`git describe --tags --always`), a link to the commit and the build
timestamp. Add run-name to all `_`-prefixed workflows so the target
ref (and, where reliable, the commit sha) is visible directly in the
Actions run list.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-08 19:44:44 +02:00
Andrey Antukh
99e6d4f1ad 📚 Forbid hand-editing CHANGES.md in agent guides
Adds the hard rule to AGENTS.md and to mem:critical-info: CHANGES.md
is generated from GitHub milestones during the release process; it
must be updated only via the update-changelog skill flow or on
explicit user request.

AI-assisted-by: omen-alpha
2026-09-08 16:39:35 +00:00