2468 Commits

Author SHA1 Message Date
Andrey Antukh
4b978767ea
🌐 Clean up en translations (#11853)
Drop 277 keys nothing references from en.po (verified against
frontend/src and common/src) and let sync propagate the
deletions to every locale. Clear all 10 fuzzy entries: fill
the 5 empty translations, keep the 4 valid ones, and drop the
duplicated max-quote-reached in favor of max-quota-reached
(the backend code stays, the UI maps it to the quota text).

Recover 22 used-but-missing keys with translations: the 19
shortcuts section/subsection labels plus connected-to,
pixel-grid-color and tokens.add-set. Make the rest
statically visible to rehash instead: :label fns on shortcut
commands, sections and subsections (one debug-only and one
colorpicker-local id exempt); case branches in place of
dm/str-built keys (export modal, text decoration and
transform, undo history with raw-key fallback); hoist
conditionals out of tr calls; pre-translate modal props and
role labels; replace the lone (i18n/tr ...) site with tr.
Turn static :error/code data into eager :error/fn calls in
the common schemas and the auth/password forms. Rename the
two keys containing spaces and point team leave at
max-quota-reached. Backend-driven keys stay dynamic by
design, declared with (tr ...) comments: the five
weak-password details, team and organization notifications.

Tooling: rehash also scans common/src and no longer treats
a missing -l as no locale; new clj-kondo tr-dynamic warning
flags non-literal tr args (lint scripts use --fail-level
error so it never fails CI); tr docstring states the
literal-only rule. Tests cover the shortcut label wiring,
the undo-history fallback and the :error/fn schemas.
Translations memory rewritten to match; es check word list
gains three entries.

Rebased onto develop: adopt the register field-error UX
(the weak-password declarations move onto the :options
code), keep develop's newer keys (connection-error,
account-locked, save-retrying, tokens-source strings) with
fresh references, and reword the shortcuts.cljs prose
comment so rehash does not invent a "literal" key.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-25 11:11:44 +02:00
María Valderrama
f35e12f716
🐛 Fix organization/team switcher issues from UX review (#11899)
* 🐛 Fix organization/team switcher issues from UX review

* 🐛 Let members leave an organization without SSO credentials

* 🐛 Fix style from previewed organization in the team switcher

* 🐛 Fix leave-organization modal test stubs
2026-09-25 09:54:58 +02:00
Eva Marco
62153dcb0b
♻️ Fix small errors (#11897)
* ♻️ Remove blank space on token sets

* ♻️ Disabled button instead of hidding it

* ♻️ Update typography
2026-09-24 13:45:17 +02:00
Eva Marco
e140bd5393
🎉 Add an empty state message on libraries modal (#11818) 2026-09-23 17:49:57 +02:00
Andrey Antukh
1c7a73ec16
✨ Add account lockout after failed login attempts (#11402)
* ✨ Add account lockout after failed login attempts

Implement per-account brute-force protection using a Redis-backed
failed-login counter. After 5 failed attempts within 15 minutes, the
account is temporarily locked out and all login attempts (including
with the correct password) are rejected with a 429 response.

Closes #11397

AI-assisted-by: longcat-2.0

* 🐛 Bind LDAP session to directory-verified profile

The account-lockout change added a shortcut that preferred the
profile matching the typed email over the one returned by the LDAP
directory. These can differ with aliases, UPNs, or multi-valued mail
attributes, letting a user with valid LDAP credentials bind a session
to another Penpot account.

Keep the typed-email profile only for lockout checks. After LDAP
succeeds, resolve the session profile from the directory identity as
before and clear failed attempts on that profile.

AI-assisted-by: deepseek-v4.1-flash
2026-09-23 12:31:50 +02:00
Andrey Antukh
34b24a9d9d ✨ Retry transient saves with backoff and reconnect notice
Classify save failures as transient or terminal (`transient-error?`
over the repo retryable types plus `:invalid-save-response`).
Transient failures keep the head commit queued under a new `:retrying`
status and resend it with backoff (2s/8s/20s, then terminal):
stamp rotation reuses the same `:commit-id`, the in-flight guard
prevents double-sends, and episode tokens silence stale timers.
One tagged reconnect notice per episode (hidden on save and on
terminal failure, silent recovery) plus a `:retrying` save-indicator
state; the browser `online` event and new edits resume the episode.
Terminal failures keep the exact `:error` path. Covers tasks 4, 6
and 7 with 31 persistence tests; updates the persistence memory.

Relates to #11724

AI-assisted-by: muse-spark-1.3-contributor
2026-09-23 12:05:24 +02:00
Andrey Antukh
95e551697f 🐛 Report environment failures as compact audit events
Connectivity and gateway failures (network, offline, 502/503 and
nitrate configuration) are not application defects, but offline fell
through to :default and 502/503 rendered exception-page, so they
reached the internal error reports and alerts with the full payload
(stack plus the last events). They are now classified as environment
failures and reported as audit-only handled-exception events.

generate-report accepts an explicit :format, as keyword arguments or as
a trailing map. :compact keeps the context header plus type, code and
uri, and skips the stack, the ex-data dump (which may contain request
headers) and the last-events list. flash derives the payload format from
the cause, so environment failures get a compact report; the audit event
name stays the canonical one requested by the caller
(handled-exception/unhandled-exception) because external tooling filters
on those names. Environment fingerprints drop the stack frame, so
grouping does not depend on the internal call site.

submit-report now requires an exception cause: a report without one is
ignored instead of using a separate fallback fingerprint, so a single
fingerprint format governs every report.

:offline gets its own handler and both connectivity handlers show the
new errors.connection-error message instead of the generic toast.

Closes #11743

AI-assisted-by: deepseek-v4.1-flash
2026-09-23 12:05:24 +02:00
Andrey Antukh
bc3cb4bddf
🌐 Complete Catalan translations in frontend (#11741)
* 🌐 Complete Catalan translations in frontend

Complete the Catalan (ca.po) locale to 100% coverage against en.po,
using es.po as support reference. Adds the 1439 missing entries
across workspace, dashboard, labels, shortcuts, subscription,
errors, modals and onboarding, keeping vosaltres treatment and
IEC/Termcat terminology consistent with the existing strings.
Normalizes placeholders and plural forms, drops the 14 stale
obsolete entries and canonicalizes the file with the repo
translations script.

Closes #11739

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

* 📚 Add frontend translations memory with Catalan criteria

Record the PO workflow, the sync fuzzy-flag gotcha and the
Catalan glossary and tone agreed upon while completing ca.po,
and link the new memory from the frontend core routing.

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

* 🔧 Add gettext to devenv image

Provide msgfmt and msgattrib in the dev environment for
checking PO translation files.

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

* 🌐 Fix Catalan translations and add PO checker

Review of the missing-whitespace pattern found ~90 glued words
across 75 entries, plus 4 lost plural forms and 2 placeholder
mismatches verified against tr call sites. All fixed in ca.po.

Adds frontend/scripts/check-translations.js (vocabulary-free PO
QA: glued words, punctuation, placeholders, plurals) with
--self-test, wired as pnpm run check-translations and
documented in mem:frontend/translations.

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

* 🌐 Multi-locale PO checker with word catalogs

Split the checker engine from its word lists: ca/es catalogs now
live in scripts/check-translations/words.<locale>.txt and all
messages are in English. Adds an es seed (calibrated to zero
errors) and fixes 7 typos it found in es.po. Universal checks
(placeholders, plurals, punctuation) run without a catalog.

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

* 🌐 Merge PO checker into translations.js

Fold check-translations.js into translations.js as a check
subcommand reusing its locale helpers; word lists stay in
scripts/check-translations/words.<locale>.txt. Also fixes the
getopts stopEarly bug that made -l useless after the command
(sync -l ca synced every locale), drops dead lodash import
and code, unifies help and exit codes. Removes the
check-translations package alias; use translations.js
check -l <locale> with explicit -l.

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

* 🌐 Keep unused placeholders out of the gate

Reverts the %s-stripping on unused auth.terms-privacy-agreement:
the links mirror its markdown sibling and a reactivation may
need them. Placeholder mismatches on #, unused keys now warn
instead of failing, and the rule is recorded in
mem:frontend/translations.

AI-assisted-by: muse-spark-1.3-contributor
2026-09-22 14:10:01 +02:00
Eva Marco
683e0aed40
🐛 Fix ambiguation on import modal (#11747) 2026-09-22 10:23:38 +02:00
Andrés Moya
3cd9bfa9de
✨ Add sync with design tokens in external libraries (#10293)
* ✨ 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>
2026-09-21 15:37:45 +02:00
María Valderrama
b32a73519e
🎉 Add combined organization/team switcher to the dashboard (#11760)
* 🎉 Add combined organization/team switcher to the dashboard

* 📎 Code review
2026-09-21 12:50:32 +02:00
Eva Marco
52825f528c
♻️ Update import modal UI (#11704)
* 🌐 Translate import dialog strings

Several plain hardcoded strings in the import dialog (the file
rename aria-label, library-resolution messages, table headers, and
the manually-linked summary labels) were never wired to the i18n
system. Wire them up via tr and add the corresponding English and
Spanish entries to the translation catalogs.

* 🐛 Fix library-resolution summary layout in import dialog

The auto-linked/manually-linked summary list had several layout bugs:
name/selection columns stretched unevenly, the auto-linked badge sat
nested inside the name's ellipsis text (unreliable across browsers),
and a long unbreakable library name could grow the whole modal past
its fixed width because a bare 1fr grid track has no minimum-size
cap. Fix the column sizing, split the badge out as a sibling of the
name, and clamp the modal content column with minmax(0, 1fr).

* ✨ Add Skip button to library resolution wizard

Add a "Skip" action to the per-file library resolution step so users
can leave all of a file's pending libraries unconnected and move on
to the next one, instead of being forced to pick a candidate or go
back. Clears any pending selections for the current file before
advancing the wizard, so the summary correctly shows those libraries
as unlinked.

* 🐛 Fix stale translation key on library resolution button

The primary action button in the library resolution wizard referenced
"dashboard.import.review-links", a translation key that no longer
existed, leaving the button with no text. Rename it to
"dashboard.import.connect-selected-libraries" to match the actual
button label.

* 🐛 Align no-selection state with arrow and file name

.summary-no-selection was missing display:flex/align-items:center,
so its icon and text weren't vertically centered against each other
or against the file name column in the import library summary.

* 💄 Use select-accent color for auto-linked badge border

* 💄 Change import summary columns from 50/50 to 40/60 split

Give the "new library" column more room than the "original library"
column in the manually-linked libraries table (header and rows), since
it usually carries the longer "name (project)" text.

* 🐛 Divide back buttons from forward buttons

* 🐛 Fix dropdown typography
2026-09-21 12:29:14 +02:00
Alejandro Alonso
ecf8436eea Merge remote-tracking branch 'origin/staging' into develop 2026-09-17 17:12:39 +02:00
Alonso Torres
2fc2a9064a
🐛 Add download report to the error toast (#11762)
* 🐛 Add download report to the error toast

* 🐛 Restore old behavior for some cases
2026-09-17 17:05:06 +02:00
Alejandro Alonso
14c3135e21 Merge remote-tracking branch 'origin/main' into staging 2026-09-17 11:26:23 +02:00
Alonso Torres
420aa981b2 🐛 Fix stalled saving states (#11699) 2026-09-17 11:25:36 +02:00
Andrey Antukh
5c38dcab28 🌐 Validate and rehash translations 2026-09-15 19:21:59 +02:00
Anonymous
b8534245b9
🌐 Add translations for: French (Canada)
Currently translated at 96.6% (2410 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-09-15 17:19:22 +00:00
Alexis Morin
1b46b288f4
🌐 Add translations for: French (Canada)
Currently translated at 96.6% (2410 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr_CA/
2026-09-15 17:19:21 +00:00
VKing9
5e3ffd3345
🌐 Add translations for: Hindi
Currently translated at 78.7% (1965 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2026-09-15 17:19:19 +00:00
Late Night Defender
c035e4841f
🌐 Add translations for: Thai
Currently translated at 7.3% (183 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/th/
2026-09-15 17:19:17 +00:00
AntonPalmqvist
2ef3487518
🌐 Add translations for: Swedish
Currently translated at 93.3% (2329 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-09-15 17:19:14 +00:00
Anonymous
346884ef60
🌐 Add translations for: Swedish
Currently translated at 93.3% (2329 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-09-15 17:19:14 +00:00
Henrik Allberg
796b6c2f07
🌐 Add translations for: Swedish
Currently translated at 93.3% (2329 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2026-09-15 17:19:13 +00:00
Црнобог
f81ba88459
🌐 Add translations for: Serbian
Currently translated at 53.4% (1332 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2026-09-15 17:19:10 +00:00
Alejandro Alonso
1f54e4d284
🌐 Add translations for: Yoruba
Currently translated at 45.5% (1137 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2026-09-15 17:19:08 +00:00
Alejandro Alonso
d73135839b
🌐 Add translations for: Igbo
Currently translated at 19.4% (484 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2026-09-15 17:19:06 +00:00
Revenant
474bcf54a2
🌐 Add translations for: Malay
Currently translated at 25.5% (637 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ms/
2026-09-15 17:19:04 +00:00
Yessenia Villarte Vaca
b9899d2050
🌐 Add translations for: Spanish (Latin America)
Currently translated at 4.3% (108 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es_419/
2026-09-15 17:19:02 +00:00
Alejandro Alonso
0324a726ed
🌐 Add translations for: Hausa
Currently translated at 48.2% (1203 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2026-09-15 17:18:59 +00:00
Hugo Vermaak
ff3e54bdba
🌐 Add translations for: Afrikaans
Currently translated at 3.0% (75 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/af/
2026-09-15 17:18:57 +00:00
Stephan Paternotte
1e551a37fe
🌐 Add translations for: Dutch
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-09-15 17:18:55 +00:00
Sebastiaan Pasma
c48c121378
🌐 Add translations for: Dutch
Currently translated at 96.5% (2407 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2026-09-15 17:18:54 +00:00
Edgars Andersons
a0363ff083
🌐 Add translations for: Latvian
Currently translated at 73.5% (1835 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2026-09-15 17:18:52 +00:00
deveronica
4ab4090548
🌐 Add translations for: Korean
Currently translated at 79.1% (1975 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/
2026-09-15 17:18:50 +00:00
Bogi Napoleon Wennerstrøm
b69ec766b5
🌐 Add translations for: Faroese
Currently translated at 6.5% (163 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fo/
2026-09-15 17:18:48 +00:00
Denys Kisil
306223179f
🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 81.1% (2023 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2026-09-15 17:18:45 +00:00
al0cam
c8a27eb823
🌐 Add translations for: Croatian
Currently translated at 62.5% (1559 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2026-09-15 17:18:43 +00:00
Zvonimir Juranko
372f1c3873
🌐 Add translations for: Croatian
Currently translated at 62.5% (1559 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2026-09-15 17:18:43 +00:00
TheScientistPT
43d5ee6c2a
🌐 Add translations for: Portuguese (Portugal)
Currently translated at 61.4% (1532 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2026-09-15 17:18:41 +00:00
Dário
911568d42e
🌐 Add translations for: Portuguese (Portugal)
Currently translated at 61.4% (1532 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2026-09-15 17:18:40 +00:00
Amerey.eu
471a1be15f
🌐 Add translations for: Czech
Currently translated at 62.2% (1553 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2026-09-15 17:18:38 +00:00
matl-17
f71b7e9128
🌐 Add translations for: Czech
Currently translated at 62.2% (1553 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2026-09-15 17:18:37 +00:00
Mikel Larreategi
e3804a8120
🌐 Add translations for: Basque
Currently translated at 45.8% (1144 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/
2026-09-15 17:18:35 +00:00
Tatsuto Yamamoto
a1ae80e950
🌐 Add translations for: Japanese
Currently translated at 9.3% (234 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ja/
2026-09-15 17:18:33 +00:00
ascarida
262a218875
🌐 Add translations for: Galician
Currently translated at 14.5% (362 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/gl/
2026-09-15 17:18:31 +00:00
Vincas Dundzys
22dcd29601
🌐 Add translations for: Lithuanian
Currently translated at 4.4% (112 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lt/
2026-09-15 17:18:29 +00:00
Radek Sawicki
0aa49f0f3f
🌐 Add translations for: Polish
Currently translated at 44.8% (1119 of 2494 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2026-09-15 17:18:26 +00:00
Nicola Bortoletto
be67bb8485
🌐 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:24 +00:00
Anonymous
841288cbc3
🌐 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:24 +00:00