14169 Commits

Author SHA1 Message Date
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
Chulgil Lee
cdda6ae33c 🐛 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:56:12 +02:00
Chulgil Lee
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
Shreyash
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
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
39ca4c264c
🔥 Remove onboarding A/B test and welcome file creation (#11707)
Drop the onboarding-03 experiment consulted through
external-feature-flag, keeping the false-branch behavior: registration
never requests a welcome file and the workspace never shows the
onboarding modals. Remove the now-unused welcome-file machinery on the
backend (RPC wiring, welcome_file namespace, welcome-file-id prop and
the post-login redirect). Keep the external-feature-flag helper as the
seam for future experiments and note it in mem:frontend/core.

Closes #11705

AI-assisted-by: muse-spark-1.3-contributor
2026-09-15 14:14:32 +02:00
Belén Albeza
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
Andrey Antukh
5d5f7fcc14 🐛 Fix workspace crash on non-string thumbnail URIs
Thumbnail :uri values arrive from the server over transit, so
media-ids decode to UUID objects instead of strings. The
resolved-uri? helper added in #11563 called
clojure.string/starts-with? on them unconditionally, raising
TypeError: str.lastIndexOf is not a function and crashing the
workspace on frame render. Guard with string? so non-string
URIs fall through to resolve-media, which stringifies them.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-14 16:33:54 +02:00
Andrey Antukh
ba608f8c77 Merge remote-tracking branch 'origin/staging' into develop 2026-09-14 14:50:32 +02:00
Andrey Antukh
5931f60d53
🐛 Fix workspace crash when resolving thumbnail data URIs (#11563)
The workspace-thumbnail-by-id ref unconditionally called resolve-media
on thumbnail URIs, which caused a stack overflow when the URI was a
data URI (which can be megabytes long for large images).

Data URIs contain thousands of '/' characters (base64 uses '/' as one
of its 64 characters), causing lambdaisland.uri/join to iterate
thousands of times in remove-dot-segments and overflow the JavaScript
call stack.

Add a resolved-uri? helper that checks if the URI already starts with
'blob:' or 'data:', and skip resolve-media for those cases. Only call
resolve-media when the URI is a plain UUID (media-id from the server).

Closes #11562

AI-assisted-by: qwen3.7-plus
2026-09-14 13:25:31 +02:00
Andrey Antukh
8629dc6b2c
🔥 Remove unused comment profiles code in the frontend (#11624)
* 🔥 Remove unused comment profiles fan-out in dashboard

The dashboard event retrieve-unread-comment-threads issued one
get-profiles-for-file-comments call per distinct file id and stored
the result in :current-team-comments-users, a state key no one
reads. The dashboard renders each thread author from the thread
payload via get-owner, so the fan-out was N+1 dead work.

Drop the per-file branch and the state write; the event now issues
a single get-unread-comment-threads call.

AI-assisted-by: deepseek-flash

* 🔥 Remove unused file comments users event and state

fetch-file-comments-users had no callers and passed a :team-id
where get-profiles-for-file-comments requires :file-id. Its only
effect was writing :file-comments-users, a viewer state key no
one reads.

Remove the event and the unused state key. The viewer still
loads comment profiles through fetch-profiles.

AI-assisted-by: deepseek-flash
2026-09-14 13:23:44 +02:00
Shreyash
6a46f8beb5
🐛 Fix email already used showing a toast instead of an input error (#11647)
* 🐛 Fix email already used showing a toast instead of an input error

Registering with an email that already exists reported the problem with a
toast while the email input stayed in its valid state, which reads as if the
form was accepted. The message now lands on the email input itself, the same
way the recovery and change password forms report server side errors.

The error is written to :extra-errors because the form mutator recomputes
:errors from the schema on every change, so a value written there is dropped
on the next render.

Fixes #10890

* 📎 Update CHANGES.md to remove email registration fix

Removed a note about a fix for email registration error display.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

---------
Co-authored-by: Shreyash Agare <agareshreyash26@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-14 10:13:54 +02:00
Alexis Morin
0dbc8e1b55
🌐 Add translations for: French (Canada)
Currently translated at 97.3% (2368 of 2432 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-09-11 14:51:30 +02:00
Alonso Torres
947954933c
🐛 Fix problem with node splitting in paths (#11553)
* 🐛 Fix problem with node splitting in paths

* 🐛 Fix merge splitting node in paths
2026-09-11 14:20:09 +02:00
Alejandro Alonso
87e5ffdf94
🐛 Restart progressive render on canvas background change (#11643)
Cancel Partial/ViewportReady before invalidating tiles so a mid-flight
pass does not leave background-only holes. Closes #11642.
2026-09-11 14:12:57 +02:00
Eva Marco
f3da8af7b6
🎉 Avoid interacting with clipped content (#11613)
* 🐛 Fix nested board drop target ignoring ancestor clip bounds

Frame hit-testing (get-frame-by-position, get-frames-by-position and
top-nested-frame) only checked a candidate board's own rectangle,
without accounting for an ancestor board with clip content enabled.
A nested board wider/taller than its clipping ancestor could still be
picked as the drop target in its invisible, clipped-away area, so a
dragged shape would get reparented there and disappear from view.

Add clipped-by-ancestor? to reject a point when it falls outside the
bounds of any ancestor board that has clip content enabled, so the
lookup now stops at the correct visible ancestor instead of
descending into the hidden region.

* 🐛 Fix Ctrl+click deep-select reaching into clipped board area

The clip-aware quadtree query (query-index) filters candidate shapes
by whether they overlap every clip-parent ancestor, but the whole
filter was skipped whenever clip-children? was false. That flag is
turned off while a modifier key (Ctrl/Cmd) is held for deep/penetrate
selection, which was meant to let it reach past boolean/mask clip
boundaries, but it also disabled enforcement for board "Clip content"
ancestors, letting a modifier-held click select a shape sitting in a
board's invisible, clipped-away region.

overlaps-parent? now only relaxes the check for non-frame clip-parents
(bool shapes / mask children) when clip-children? is false; board clip
ancestors are always enforced regardless of the modifier key.
2026-09-11 13:42:13 +02:00
Eva Marco
c0221a9bf8
🐛 Hide "Create typography style" button for shapes with missing fonts (#11527)
The button let users convert a text shape's inline styles into a
typography asset even when the shape's font-id couldn't be resolved
(e.g. a custom/team font that was removed or isn't loaded), silently
baking a missing font into the new typography asset.

Guard the button on the font actually resolving via
app.main.fonts/fontsdb, in addition to the existing checks (no
typography or token already applied, single selection).

Added e2e coverage for all four conditions that must independently
hide the button: missing font, applied typography asset, multiple
selection with differing values, and applied typography token.

AI-assisted-by: claude-sonnet-5
2026-09-11 13:41:11 +02:00