24544 Commits

Author SHA1 Message Date
Miguel de Benito Delgado
8c67ac7fb6 🚧 WIP Step 16.1 Relax scene macro constraints 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
e5958db3aa 🚧 WIP Step 16 Introduce hooks for the browser and load-scene 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
cf6af0cf62 🚧 WIP Step 15.1, cosmetic 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
dbd37c73a2 🚧 WIP Step 15, Use transit to simplify validation 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
eb566b0c47 🚧 WIP Step 14, Grab correct w/h attrs from context 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
d12df5058b 🚧 WIP Step 13, Track failure causes
AI-assisted-by: muse-spark, GLM-5.3
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
09ac810229 🚧 WIP Step 12.4, Comments
AI-assisted-by: gpt6-astra
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
5b2e4679f3 🚧 WIP Step 12.3, Test cleanup + missing coverage
AI-assisted-by: muse-spark, deepseek-flash
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
16966cf190 🚧 WIP Step 12.2, Fix browser bridge, lifecycle
AI-assisted-by: muse-spark, glm-5.3
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
a8f10b6058 🚧 WIP Step 12.1, Improve case validation
AI-assisted-by: muse-spark, glm-5.3
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
ccd7648901 🚧 WIP Step 12, temporary pilot
- pilot runner for testing
- will be entirely removed later

AI-assisted-by: muse-spark-1.3, deepseek-flash, glm-5.3
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
b43b2fbdf1 🚧 WIP Step 11, browser bridge
- New schema:viewport
- Browser bridge (with some temp code)
- Move prepare-scene out measured regions
-
AI-assisted-by: muse-spark-1.3, deepseek-flash, glm-5.3
2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
f9cd9843ac 🚧 WIP Step 10, scene upload 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
5691183c04 🚧 WIP Step 10, defcase body validation, docs 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
c89a58e182 🚧 WIP Step 9, fix naming conventions 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
796c76559e 🚧 WIP Step 8, simplify and clean up tests 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
c2b43cb92b 🚧 WIP Step 7, scene and case declarations 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
242a621e5e 🚧 WIP Step 6, ellipses 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
76cc5f02a9 🚧 WIP Step 5, bools 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
882e585b13 🚧 WIP Step 4, frames and groups 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
9e0600df0b 🚧 WIP Step 3, fixture macros, prng, defaults, placeholder scene 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
32c3dba17c 🚧 WIP Step 2, common scene code, validation 2026-11-05 11:18:13 +01:00
Miguel de Benito Delgado
9caddb7596 🚧 WIP Step 1, scaffold cljs benchmark runner 2026-11-05 11:18:12 +01:00
Andrey Antukh
9d898b8b31
🔧 Normalize the tenant to lowercase (#12843)
An operator writing `PENPOT_TENANT=ACME` and one writing `acme` name the
same tenant: the value is compared as an exact string in the keys and
rows it lands in, and one uppercase letter would otherwise split one
tenant into two.

The value is normalized when the configuration is read, so nothing
downstream has to care about case, and the validated form accepts only
lowercase labels from then on.

AI-assisted-by: deepseek-v4.1-flash
2026-11-05 08:45:01 +01:00
Alonso Torres
eb9594a614
🐛 Fix ChatGPT tool discovery with the submit_feedback tool (#12845)
* 🐛 Fix ChatGPT tool discovery with the submit_feedback tool

ChatGPT compiles the regex patterns advertised in tool input schemas
with Python, which rejects Unicode property escapes such as \P{Cc}. The
submit_feedback subject pattern used one, so ChatGPT dropped the whole
tool list ("action discovery failed") on deployments that register the
tool.

The subject pattern now spells out the Cc range as a character class,
which accepts the same input. A test asserts that no advertised pattern
uses \p{...} or \P{...}.

AI-assisted-by: claude-opus-5-5

* 🐛 Fix MCP plugin test stub for the version check

plugin.ts checks version compatibility with extractMinorVersion, but the
tests loaded it with a require stub that returned the same dummy object
for every import, so the check threw once the UI initialized.

The plugin.ts tests now share a stub that supplies the real
VersionCompatibility, ErrorUtils and TaskHandler modules, stubs only the
code execution handler and rejects unexpected imports. TaskHandler is
transpiled for the sandbox because Node's type stripping cannot load its
parameter properties.

AI-assisted-by: claude-opus-5-5

* 🐛 Fix Management tests for backend-confirmed setters

Management setters save to the backend first and update the object only
when the save succeeds, so seven live tests failed:

- setter tests now wait for the new value before reading it
- createProject checks fileCount by listing the project again, as
  fileCount is the count from when the project was listed
- project moveTo no longer calls remove on the target team, which teams
  do not have; the live CI driver deletes temporary teams
- renaming the open file waits for its restore, which made the next
  navigation test fail

The README notes on setters, fileCount and team cleanup match this.

AI-assisted-by: claude-opus-5-5
2026-11-04 21:47:55 +01:00
makesomethingshit
d42217a32b
🐛 Create Plugin API shapes at the page root (#12499)
* 🐛 Create Plugin API shapes at the page root

Create component instances and SVG shapes at the page root so a board at
the page origin cannot capture them. Continue using the existing forced
frame option for component instances and an explicit parent frame for
SVG imports. Interactive placement retains its current behavior.

Cover component instances, SVG imports and SVG imports with images on
ordinary, flex and grid origin boards, including undo, redo, position
changes and explicit reparenting.

Closes #12086

AI-assisted-by: deepseek-v4.1-flash

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

* 🐛 Preserve interactive creation placement

Cover component and SVG placement inside and outside an origin board.
Exercise the sidebar move lifecycle and verify parent preservation.
Correct the Plugin API changelog category.

AI-assisted-by: gpt-6
Signed-off-by: choi junsoo <junsoo1172@gmail.com>

* 🔧 Cover page-root creation in the Plugin API test suite

Check that LibraryComponent.instance(), createShapeFromSvg() and
createShapeFromSvgWithImages() create shapes at the page root when a
board covers the page origin. Each test removes the shape it creates,
since it lives outside the scratch board.

AI-assisted-by: claude-opus-5-5

---------

Signed-off-by: choi junsoo <junsoo1172@gmail.com>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-11-04 17:40:14 +01:00
Elena Torró
4be8df5877
⚡ Load the rasterizer iframe only when the SVG renderer needs it (#12835)
Create it once features say SVG, never in wasm mode. Also skip SVG frame
thumbnails in wasm mode.
2026-11-04 15:50:13 +01:00
Eva Marco
2f060e9baf
✨ Add skills for the DS migration epic (#12739)
* ✨ Add skills for the DS migration epic

Two skills that drive the DS migration (#12375) one area at a time, so
anyone working with an agent follows the same flow.

refactor-scss moves one .scss off frontend/resources/styles/common/
refactor/ to the DS tokens, mixins and px2rem as a pure refactor, with
a self-review for the issues reviewers flag and a zero-tolerance visual
check.

migrate-ds-area takes one area of a group issue (buttons and icons,
selects, ...): claim it in the group's PRs table, add a visual test,
change the controls, run refactor-scss, compare before and after with
the bundled diff scripts, then open the draft PR and post the QA
comment. It also lists devenv traps seen while migrating the first
areas.

AI-assisted-by: claude-opus-5-5

* ✨ Add the claim rules to the migrate-ds-area skill

The group issue body is saved whole, so two people claiming at the same
time can lose a row. The skill now rereads the body after saving and
puts the row back if it is gone. A claim with no PR for 7 days is free
once its owner has been asked in a comment and did not answer.

AI-assisted-by: claude-opus-5-5
2026-11-04 15:29:07 +01:00
María Valderrama
f532b8c12e
✨ Add get-profiles-by-ids management method (#12832) 2026-11-04 14:01:27 +01:00
Juanfran
757ec974df
🐛 Build plugin scripts before Angular copies assets (#12815)
Generate plugin.js before Angular builds each of the eight Angular plugins.
The previous order wrote the script to src/assets after Angular had copied
assets to dist, so clean deploys published manifests without their code.

Verified clean builds include all declared manifest assets for all eight
plugins. Plugin lint, format checks and all 101 tests pass.

AI-assisted-by: gpt-6.1-sol
2026-11-04 13:03:10 +01:00
Elena Torró
8d7e275b6b
🐛 Count id and type in the wasm shape proxy (#12793)
The proxy counted one entry fewer than it yields, so it was never equal to a
plain map with the same entries. Count both fields and test it.
2026-11-04 13:01:05 +01:00
Luke Lau
2573908315
⚡ Drop unused used-names sets from component duplication (#12809)
unames or update-unames! are no longer used after a1a3d09998, and each call to
cfh/get-used-names is a scan across all objects
2026-11-04 13:00:24 +01:00
Andrés Moya
177c3c6551
🔧 Add one more test case (#12827) 2026-11-04 12:05:38 +01:00
Florian Schrödl
4b2061f399
✨ Add unit tests for the token forms and token tree (#12781)
* ✨ Add unit tests for the token form and combobox helpers

Several helpers of the token forms and the token value combobox were
already pure but had no tests. Cover the reference under the cursor,
the order of the token tree, moving through the combobox options, the
shadow and typography form values and their validation, which value a
form tab validates, and that a token import needs a json file.

Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>

* ✨ Test the shadow and typography form rules

Add tests for the shadow and typography form schemas: a negative blur
is rejected, a negative spread and a blank blur or spread are accepted,
every shadow needs its offsets and color, a line height needs a font
size, and a token can't reference itself. A typography token submitted
without any field is rejected.

They replace the tests of the validator wrappers, the combobox focus
order and the import file check.

Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>

---------

Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>
2026-11-04 12:04:52 +01:00
andrés gonzález
4ee2a812be
🔥 Remove Beta badge from MCP server in Integrations (#12813)
The Integrations settings page labelled the MCP server as Beta, while
the MCP docs give no maturity status. Drop the badge, its style and the
unused translation key so the UI no longer presents it as beta.

Closes #12800

AI-assisted-by: claude-opus-5-5
2026-11-04 11:30:53 +01:00
Luke Lau
9b672785af
🐛 Fix gap handle drag leaving a stuck preview in wasm (#12782)
AI-assisted-by: claude-sonnet-5-5
2026-11-04 11:29:39 +01:00
Florian Schrödl
e6153b61a5
♻️ Extract the token pill state and tooltip from its component (#12691)
The token pill worked out inside the component whether its token is
applied to the selection, whether it can apply to it at all and why
the token is invalid, so no test could reach that logic. Move it into
`pill-state`, and split the tooltip into `tooltip-info`, which decides
what it says, and `render-tooltip`, which renders it. Both are pure
and get unit tests for the applied, partial, disabled and error states
and each tooltip case.

The two chained `on-click` handlers become one. Nothing else changes
in behavior.

Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>
2026-11-04 10:56:50 +01:00
Luke Lau
a089f7a5a1
🐛 Fix library telemetry counters (#12787)
At some point the code around this changed, I believe this used to reduce over
file ids. However today it reduces over entire library maps, so it passes a map
instead of an id to `dm/get-in files`. This then hashes the library which
forces it to decode all of its shapes, and then it fails to lookup the data. So
the component/colors/typography counts were always returning 0.

Using reduce-kv fixes this, and we can just get the library data directly with
`(:data library)`.

On the backstage file in penpot-files this brings the time to the telemetry
snapshot down from 3.8s to 1.5s

AI-assisted-by: claude-5.5-sonnet

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-11-04 10:44:30 +01:00
Andrey Antukh
4173cc34ab Merge remote-tracking branch 'origin/staging' into develop 2026-11-03 20:21:46 +01:00
Andrey Antukh
d5e7fe59c9 Merge remote-tracking branch 'origin/main-2.18.x' into staging 2026-11-03 20:17:01 +01:00
Andrey Antukh
59c61dab59 📎 Update changelog 2026-11-03 18:58:56 +01:00
Elena Torró
51cbae6ea5
⚡ Improve memory consumption on shape decoding (#12790)
* ⚡ Avoid decoding the opened page twice for its frame index

The frame index was built over a with-meta copy, so pages got decoded again.
Read it from the page objects themselves and fix the with-meta identity check.

* ⚡ Intern keywords when decoding transit on the frontend

Each shape is decoded on its own, so every shape had its own key keywords.
Cache them in the read handler so all decoded data shares one instance.

* ⚡ Use an array map as the wasm shape proxy delegate

A hash map with ~35 keys costs ~3 KB of nodes per shape on large pages.
An array map holds the same entries in one array, at a small lookup cost.
2026-11-03 17:41:36 +01:00
Nimesh
88cd0933bd
🐛 Raise and make configurable the MCP request body size limit (#12450)
The MCP endpoint parsed request bodies with express.json() and its
default 100kb limit, so large tool calls such as execute_code failed
with HTTP 413 (PayloadTooLargeError). Read the limit from the new
PENPOT_MCP_MAX_BODY_SIZE env var, defaulting to 10mb, and document it.

Closes #12080

Signed-off-by: Nimesh Kulkarni <167794819+GitNimay@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-11-03 16:21:28 +01:00
GennoBou
029d9c0af7
🐛 Fix Japanese autodetection fallback on "ja" locale (#12283)
Most browsers return "ja" rather than "ja-JP" in navigator.language.
Because parse-locale expands "ja-JP" to ["ja_jp" "ja"] but keeps "ja"
as ["ja"], using "ja_jp" causes autodetection to fall back to English.

Normalize the Japanese locale identifier to "ja" and map jpn_JP.po to
translation.ja.js, matching other single-region locales like Ukrainian.

AI-assisted-by: gemini-3.8-flash
2026-11-03 16:11:22 +01:00
Robert
2d649bd53c
🐛 Ignore patch versions in the MCP version compatibility check (#12009)
The plugin compared the full major.minor.patch of the running Penpot
version with the MCP package version. The MCP package version is only
set per minor release (2.15.0, 2.16.0, 2.17.0), so every Penpot patch
release (for example 2.16.2 or 2.17.2) showed the "Version mismatch
detected" warning although nothing changed for the MCP.

Compare only major.minor, move the check into a small module with
unit tests, and update the README versioning note to match.

AI-assisted-by: claude-opus-5-5

Signed-off-by: robertkodra <36516516+robertkodra@users.noreply.github.com>
2026-11-03 16:08:54 +01:00
Álvaro Tejero-Cantero
b19ad65581
🔧 Store devenv workspace clones in the XDG data directory (#11986)
manage.sh has kept the ws1+ workspace clones in
~/.penpot/penpot_workspaces since parallel instances landed (#9906).
That directory sits outside the XDG base directory layout. The default
is now $XDG_DATA_HOME/penpot-devenv/workspaces, with ~/.local/share
standing in when the variable is unset or empty. It is the data
directory and not the cache, because a clone can hold commits that
exist nowhere else.

A directory that already exists at the former default keeps being
used, so existing clones are neither orphaned nor seeded again. An
exported PENPOT_WORKSPACES_DIR still overrides both.

AI-assisted-by: claude-opus-5-5

Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
2026-11-03 16:06:44 +01:00
Florian Schrödl
d1c8b0f959
🐛 Report tokens named below another token with tokenscript (#12529)
A token cannot contain tokens: next to a token `a`, the tokens `a.b`
and `a.b.c` are now invalid with tokenscript, whatever the order of
their sets. As with StyleDictionary, their pills and the token form
show "Group name of a.b conflicts with a token of the same name in
another active set." and `{a.b}` is a missing reference.

The interpreter reports them as :processor/token-name-collision, which
the adapter maps to :error.token/name-collision. frontend/deps.edn
updates penpot/tokenscript-interpreter to v0.2.0, which adds the check.
Since v0.1.0 its errors carry codes and data only, which the adapter
already relies on.

Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>
2026-11-03 16:06:16 +01:00
penpot-renovate[bot]
2bf9f4c78b
⬆️ Update DHI base images (#12767)
- dhi.io/debian-base trixie-debian13-dev@af61a32 (docker/images/Dockerfile.backend)
- dhi.io/debian-base trixie-debian13-dev@af61a32 (docker/images/Dockerfile.backend)
- dhi.io/debian-base trixie-debian13@834d20c (docker/imagemagick/Dockerfile)
- dhi.io/debian-base trixie-debian13-dev@af61a32 (docker/imagemagick/Dockerfile)
- dhi.io/debian-base trixie-debian13-dev@af61a32 (docker/devenv/Dockerfile)
- dhi.io/nginx 1.31.8-debian13-dev@f69a0ea (docker/images/Dockerfile.frontend)
- dhi.io/node 24.22.2-debian13-dev@df819cf (docker/images/Dockerfile.media-processor)
- dhi.io/node 24.22.2-debian13-dev@df819cf (docker/images/Dockerfile.mcp)
- dhi.io/node 24.22.2-debian13-dev@df819cf (docker/images/Dockerfile.exporter)

Signed-off-by: penpot-renovate[bot] <338182062+penpot-renovate[bot]@users.noreply.github.com>
Co-authored-by: penpot-renovate[bot] <338182062+penpot-renovate[bot]@users.noreply.github.com>
2026-11-03 16:03:28 +01:00
penpot-renovate[bot]
b2be633b91
⬆️ Update pnpm to v12.16.1 (#12778)
- pnpm 12.16.1 (mcp/packages/browser-extension/package.json)

Signed-off-by: penpot-renovate[bot] <338182062+penpot-renovate[bot]@users.noreply.github.com>
Co-authored-by: penpot-renovate[bot] <338182062+penpot-renovate[bot]@users.noreply.github.com>
2026-11-03 16:02:56 +01:00
Andrey Antukh
be37341f36 Merge remote-tracking branch 'origin/staging' into develop 2026-11-03 15:07:18 +01:00