Commit Graph
23725 Commits
Author SHA1 Message Date
Valentina Chapellu 8d9894653c 🌐 Add translations for: Italian
Currently translated at 83.8% (2092 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2026-09-15 17:18:23 +00:00
Danial Shirali b5dafa3d49 🌐 Add translations for: Persian
Currently translated at 96.3% (2404 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2026-09-15 17:18:20 +00:00
Ahmad HosseinBor c4318d738e 🌐 Add translations for: Persian
Currently translated at 96.3% (2404 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2026-09-15 17:18:20 +00:00
Joseph V M 04994af434 🌐 Add translations for: Malayalam
Currently translated at 2.7% (68 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ml/
2026-09-15 17:18:17 +00:00
william chen 088bc2f214 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 62.4% (1558 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-09-15 17:18:15 +00:00
Andy Li 3ec46a231a 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 62.4% (1558 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2026-09-15 17:18:15 +00:00
Yaron Shahrabani 6928474fdb 🌐 Add translations for: Hebrew
Currently translated at 85.8% (2141 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-09-15 17:18:12 +00:00
Linerly 2974a83f0d 🌐 Add translations for: Indonesian
Currently translated at 66.4% (1657 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2026-09-15 17:18:10 +00:00
Amine Gdoura 9300287762 🌐 Add translations for: Arabic
Currently translated at 44.6% (1113 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2026-09-15 17:18:08 +00:00
AlexTECPlayz fd6e681699 🌐 Add translations for: Romanian
Currently translated at 76.0% (1897 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2026-09-15 17:18:06 +00:00
George Lemon 052097ccf4 🌐 Add translations for: Romanian
Currently translated at 76.0% (1897 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2026-09-15 17:18:05 +00:00
Simon Bechmann 4c4a179bda 🌐 Add translations for: Danish
Currently translated at 4.1% (104 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/da/
2026-09-15 17:18:02 +00:00
Allan Nordhøy 26879a0423 🌐 Add translations for: Norwegian Bokmål
Currently translated at 6.5% (163 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nb_NO/
2026-09-15 17:18:00 +00:00
Stas Haas d3a04c55dc 🌐 Add translations for: German
Currently translated at 77.5% (1933 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-09-15 17:17:58 +00:00
Pablo Alba ab826031c6 🌐 Add translations for: German
Currently translated at 77.5% (1933 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2026-09-15 17:17:58 +00:00
Renan Mayrinck 2783b34991 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 54.6% (1364 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2026-09-15 17:17:55 +00:00
DoubleCat f3d86b10c2 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 91.6% (2285 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2026-09-15 17:17:53 +00:00
Oğuz Ersen cfe1246f99 🌐 Add translations for: Turkish
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-09-15 17:17:51 +00:00
Anonymous 11ed5cc24a 🌐 Add translations for: Turkish
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2026-09-15 17:17:50 +00:00
Vint Prox d2c68a2ade 🌐 Add translations for: Russian
Currently translated at 66.3% (1656 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2026-09-15 17:17:48 +00:00
Anonymous f48a3b4204 🌐 Add translations for: Greek
Currently translated at 20.3% (508 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/el/
2026-09-15 17:17:46 +00:00
Corentin Noël 0f3a32e12c 🌐 Add translations for: French
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-09-15 17:17:43 +00:00
Surfoo f2f336b99a 🌐 Add translations for: French
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-09-15 17:17:43 +00:00
Pablo Alba 4a62e9063e 🌐 Add translations for: French
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2026-09-15 17:17:42 +00:00
Aryiu 4b04c05a53 🌐 Add translations for: Catalan
Currently translated at 42.3% (1057 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ca/
2026-09-15 17:17:39 +00:00
Hosted Weblate dd4c92a4e0 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2026-09-15 19:12:42 +02:00
Hosted Weblate b419efea54 🌐 Merge branch 'origin/develop' into Weblate. 2026-09-15 19:11:04 +02:00
Danial Shirali 8177022ffc 🌐 Add translations for: Persian
Currently translated at 99.8% (2429 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2026-09-15 17:09:26 +00:00
8a1bb76ef6 🐛 Fix form select label resetting to its default option (#11664)
select* resets its label to `default-selected` whenever its options
change identity. The access token dialog builds its options vector
inline, so picking an expiration re-rendered the dialog and the label
snapped back to "Never", although the form kept the picked value and
the token was created with it.

form-select* now passes the form value as `default-selected` when it
holds a non-blank string, so the reset lands on the picked option. The
caller's default still applies while the form value is blank.

Closes #11663

AI-assisted-by: claude-opus-5

Signed-off-by: chulgil <2044587+chulgil@users.noreply.github.com>
Co-authored-by: chulgil <2044587+chulgil@users.noreply.github.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-15 18:54:58 +02:00
David Barragán Merino 38712aa29f 🐛 Make integration test summary non-fatal
The summary step reads a filter from .github/scripts, which is absent
on branches predating it, so a missing file turned otherwise green
runs red. Tolerate both a missing filter and a jq error: a cosmetic
summary should never fail a run.

Use `!cancelled()` instead of `always()` on the merge job. With
`always()` it also ran when the run itself was cancelled by
cancel-in-progress, after the shards had been killed without uploading
their blobs, leaving the download step with nothing to match.

Lower the JSON report retention to 7 days, the repository maximum.
Requesting 30 was silently clamped and logged a warning on every run.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-15 18:49:38 +02:00
b9e233f497 Add 3x export scale option (#11598)
Adds a 3x entry to the export scale selector in the workspace design
sidebar and in the viewer inspect panel, so @3x assets can be exported
natively without resizing them in external tools.

Signed-off-by: Shreyash Agare <agareshreyash26@gmail.com>
Co-authored-by: Shreyash Agare <agareshreyash26@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-15 18:27:39 +02:00
Andrey Antukh 91ecfefa31 📚 Add EPIC issue type to creating-issues memory
The repository defines an EPIC issue type, but the memory only
listed Bug, Enhancement, Feature, Task, Question and Docs. Add
the EPIC row with its type id and its mapping entry so umbrella
tracking issues are typed consistently.

AI-assisted-by: deepseek-v4.1-flash
2026-09-15 16:12:02 +00:00
Teja Poosa f635aef4eb 📚 Fix typo in Docker Compose comment (#11660) 2026-09-15 17:56:11 +02:00
Álvaro Tejero-Cantero a56c3cb7ce 🐛 Pass the base branch and the project when opening a PR (#11720)
* 🐛 Pass the base branch and the project when opening a PR

The create-pr skill resolves the target branch with
scripts/detect-target-branch in step 1 and then never passes it, so
gh opens the PR against the repository default. A branch cut from
staging therefore targets develop. mem:workflow/creating-prs also
requires --project "Main", which the skill's command omitted.

Two checks were missing as well. A branch behind its base produces a
verdict about a tree nobody will ship, because checks run against head
merged into base. A draft PR runs no checks at all, since every test
workflow is gated on the pull_request.draft flag, so the skill now
asks before opening one and states that cost.

Finally, the AI-assisted-by trailer belongs in the PR body: Penpot
squash-merges, so the landed commit message comes from the PR title
and body, and a trailer that lives only on branch commits disappears
at merge.

AI-assisted-by: claude-opus-5
Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>

* ♻️ Drop the behind-the-base check and fix the trailer note

Review found both claims wrong. The behind-the-base check said the
checks would report on a tree nobody ships, but every test workflow
runs actions/checkout with no ref, so a pull_request event builds
refs/pull/N/merge, which is head already merged into base. Being
behind the base therefore changes nothing about the verdict, and the
check only forced a rebase for no gain.

The trailer note claimed a trailer on branch commits disappears at
merge. It does not: GitHub's squash body lists the commit messages,
and 91b433985a carries three AI-assisted-by lines from the two
commits of #11677. The paragraph now asks for the trailer in the body
as a courtesy to the reviewer, once per model, and says the commits
keep theirs.

AI-assisted-by: claude-opus-5
Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>

---------

Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
2026-09-15 17:43:35 +02:00
makesomethingshitandAndrey Antukh 4a5c6fce7f Fetch only caller share-link in view-only bundle (#11657)
*  Fetch only caller share-link in view-only bundle

Share-link callers now resolve a single row with a composite
(id, file-id) predicate instead of loading all sibling rows
and filtering in memory. Membership path keeps full query.

Related #11633

AI-assisted-by: muse-spark-1.3-contributor

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

*  Add DB-access regression test for share-link bundle

The share-link path must resolve the caller row with a composite
(id, file-id) single-row lookup and never run the full
file-id query. Keep cross-file replay coverage.

Related #11633

AI-assisted-by: muse-spark-1.3-contributor
Signed-off-by: makesomethingshit <junsoo1172@gmail.com>

---------

Signed-off-by: makesomethingshit <junsoo1172@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-15 17:40:52 +02:00
Andrey Antukh ad7e035b63 Add indirection for upload-chunks storage via new table (#11651)
*  Add upload_session_chunk indirection for chunked uploads

Chunks now live in the upload_session_chunk table with non-deleting
foreign keys to storage_object and upload_session, instead of tempfile
objects with session metadata. Reads go through a JOIN, so chunk state
never scans storage_object.

Uploads validate the live session, reject duplicate indexes, and store
objects in the new upload-session bucket without extra metadata.
Assemble removes mappings and marks the session consumed; objects-gc
procedurally purges consumed and stalled sessions, touching referenced
objects first. Touched-gc and deleted-gc handle the new bucket, and
upload-session-gc is removed.

Closes #11644

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix quota, give-up and coverage for session chunks

Exclude consumed sessions from the sessions-per-profile quota so
finished uploads free their slot at once. Remove chunk mappings before
the gc-deleted give-up delete to respect the NO ACTION keys. Catch
java.sql.SQLException for duplicate chunks. Cover the profile-owned
session purge and the UNIQUE race backstop with tests.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Align chunked upload tests with upload_session_chunk

Drop the duplicate-index tests written against metadata-backed
chunks; the UNIQUE mapping makes those cases unrepresentable and
the new tests cover them. Rewrite the rejected-duplicate tests to
expect :validation/:chunk-already-exists and assert against the
upload_session_chunk table, and scope the chunk-too-large "nothing
stored" check to the mapping table.

AI-assisted-by: muse-spark-1.3-contributor

* ♻️ Use NO ACTION DEFERRABLE session FKs in single migration

Fold the profile FK change into 0154 so the feature ships one
migration. All three upload session FKs use ON DELETE NO ACTION
DEFERRABLE: identical to RESTRICT in normal operation, but deferrable
for tooling that relies on SET CONSTRAINTS ALL DEFERRED. Extend the
RESTRICT test to the direct profile delete.

AI-assisted-by: muse-spark-1.3-contributor

* ♻️ Reserve chunk slot before writing blob in upload-chunk

Make object_id nullable and insert the mapping with NULL inside the
session-locking transaction, then write the blob outside it and link
it with a conditional update. A failed write removes the mapping and
reraises; a mid-flight death leaves a NULL row and the client starts
a new session.

AI-assisted-by: muse-spark-1.3-contributor

* 🔥 Remove redundant session_id index on upload_session_chunk

The UNIQUE(session_id, chunk_index) btree already serves
session_id-only lookups and the session FK check through its
leftmost column, so the standalone index only taxed the
per-chunk INSERT path. Verified with EXPLAIN on an equivalent
table shape.

AI-assisted-by: muse-spark-1.3-contributor

*  Merge chunk touch and delete into single RETURNING query

Replace the SELECT-then-DELETE round-trip in
delete-upload-sessions! with DELETE ... RETURNING object_id,
touching each returned object. Same semantics, one less query
per purged session. Follows the RETURNING pattern already used
in file-gc.

AI-assisted-by: muse-spark-1.3-contributor

* ♻️ Let objects-gc own chunk mapping deletion

Assemble-chunks now only marks the session as consumed; the
chunk mappings stay until objects-gc purges them (touching the
chunk objects first), leaving a single procedural deletion
path for consumed, stalled and profile-purge sessions.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix font-deletion GC expectations for chunk objects

Update final storage-gc-touched counts to include the two
chunk objects touched by objects-gc when purging consumed
upload sessions (8/5/5 instead of 6/3/3).

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Release chunk reservation when the link UPDATE fails

Review feedback on #11651: the link UPDATE in upload-chunk could
leave a NULL reservation behind, blocking retries of the same
index with :chunk-already-exists. Remove the reservation when
the link fails so the client can retry in the same session;
the orphaned blob stays touched for touched-gc. Also realign
the process-bucket! cond branches in gc-touched.

Tests: chunked-upload-link-failure-releases-slot and
chunked-upload-null-reservation-blocks-retry.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-15 17:26:43 +02:00
Andrey Antukh b660ea9d53 Route the app by query string with screen key
Move SPA routing out of the URL fragment into the normal query
string. The screen travels in a reserved `screen` key holding
the route name (`?screen=workspace&team-id=…`); every other
param keeps its name. `rt/nav` and `rt/resolve` keep their
signatures.

This deletes the fragment-mirroring URL surgery, simplifies
link-preview (the server sees everything) and nginx (single
path, no SPA fallback rules needed), and migrates OIDC
redirects, email links, e2e helpers and plugin test utils to
the new format.

Legacy `#/…` URLs translate client-side for one Penpot version
(`legacy-routes`, marked TODO(next-version)); non-SPA paths
are untouched.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-15 17:10:54 +02:00
elhombretecla a91d81c695 🎉 Add link preview metadata for shared links 2026-09-15 17:10:54 +02:00
Alonso Torres ff88a055fd 🐛 Fix stalled saving states (#11699) 2026-09-15 16:04:00 +02:00
Eva Marco 1c8b0c1844 🐛 Fix show more files button overlap (#11675) 2026-09-15 15:41:15 +02:00
Eva Marco a91aa0e37d 🐛 Fix permissions for search and replace (#11713) 2026-09-15 15:40:15 +02:00
Andrey Antukh 0487abb2d5 🐛 Fix unresolved make-dummy-request in middleware test
The session-renewal test called bare make-dummy-request, which
no longer resolves. Qualify it with the existing th alias for
backend-tests.helpers, matching every other call site.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-15 14:01:28 +02:00
David Barragán Merino 3cdf86a253 🐳 Apply the common security headers to the external locations
The three locations of nginx-external-locations.conf define their own add_header directives, which under nginx's inheritance rules discards every add_header from the enclosing server block. They were therefore served without any of the four security headers already shipped, and would equally have been served without the new CSP and HSTS ones.

Include the common file in all three, which is the pattern the development environment already follows for the equivalent locations.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-09-15 13:41:51 +02:00
David Barragán Merino 52723b3851 🐳 Add configurable CSP and HSTS headers to the frontend image
Ship both headers from the image so that every deployment starts from a sensible default instead of each installation deriving its own policy. Report-only mode never blocks a request, so this changes no behaviour for existing deployments, and HSTS stays absent unless PENPOT_PUBLIC_URI declares an https scheme.

The policy can be narrow because the frontend already reverse proxies its own external dependencies, so 'self' covers them. What it must permit beyond that comes from the code: 'wasm-unsafe-eval' for the render engine, 'unsafe-inline' styles for the inline style attributes of the UI, and blob:/data: for thumbnails, exports and fonts.

Closes #11374
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
AI-assisted-by: Claude
2026-09-15 13:41:51 +02:00
Belén AlbezaandClaude Opus 5 91b433985a 🎉 Add context menu for v3 editor (#11677)
* 🎉 Add a context menu to the v3 text editor

Right-clicking while editing a text shape now opens a menu with Cut,
Copy, Paste and Select all, so those actions stop being reachable only
from the keyboard. Cut and Copy are disabled when nothing is selected,
and with no selection the caret first moves to the click position.

The menu is a new `:text` kind of the workspace context menu, so it
inherits the existing positioning, styling and shortcut hints. Its
entries emit events that drive the WASM editor through the same calls
the keyboard path uses, so a menu edit commits exactly like a
keystroke: same undo step, same layer rename.

A secondary click has to leave the text alone until the menu acts on
it. The pointer handlers now ignore it, both the right button and the
macOS Ctrl+Click that reports button 0, so it can no longer start a
drag-selection over what was selected. Pressing the mouse on the menu
would blur the capture surface and end the session, so the menu
cancels the default action of `mousedown` while a text is being
edited.

Paste reads the system clipboard through `navigator.clipboard`, since
a menu click carries no clipboard event.

Closes #10914

AI-assisted-by: claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

*  Add Playwright tests for the text editor context menu

Eight tests under "Text Editor context menu" in the v3 editor spec.
They load the existing lorem ipsum fixture rather than drawing a shape,
enter edit mode, and drive the menu with a real right click.

They cover the four entries, that the right click keeps the selection
(copy returns the whole text), that a right button drag no longer
replaces it, that moving the caret abandons a staged typography, and
that cut and copy are disabled at a collapsed caret. Typing right after
a menu action also shows the editor kept the focus while the menu was
open.

AI-assisted-by: claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ♻️ Use a test id instead of a DOM id in the v3 editor spec

AI-assisted-by: claude-opus-5
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 12:53:07 +02:00
Alonso Torres 8d69374386 🐛 Makes the CI more stable (#11698) 2026-09-15 10:43:41 +02:00
María Valderrama 66876f16b2 Error page for instances without admin-console configuration (#11649)
*  Error page for instances without admin-console configuration

* 📎 Code review
2026-09-15 07:24:55 +02:00
Alexis Morin c45bb130bf 🌐 Add translations for: French (Canada)
Currently translated at 100.0% (2432 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-09-14 17:51:41 +00:00
Yaron Shahrabani ec996f08ac 🌐 Add translations for: Hebrew
Currently translated at 88.6% (2155 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2026-09-14 17:51:30 +00:00
Andrey Antukh 0caabfdd5b Merge remote-tracking branch 'origin/staging' into develop 2026-09-14 16:36:07 +02:00