Compare commits

..
2285 Commits
Author SHA1 Message Date
Andrey Antukh 06e5825c8a 🐛 Add proper input checking to font related RCP method 2026-02-10 10:36:57 +01:00
Andrey Antukh d30387eb77 Backport docker images changes from develop 2026-02-09 19:21:30 +01:00
Andrey Antukh 33fd672c21 Backport MCP related changes from develop (#8306) 2026-02-09 18:00:43 +01:00
Andrey Antukh a7b2e98b8e ⬆️ Use latest imagemagick version on docker images 2026-02-09 13:19:26 +01:00
Andrey Antukh d979894872 Add libxml2 dep to imagemagick dockerfile 2026-02-09 12:27:44 +01:00
Alejandro Alonso 3d20fc508d 🐛 Fix image magick info call (#8300) 2026-02-09 12:26:42 +01:00
Alejandro Alonso ccfee34e76 Merge pull request #8289 from penpot/niwinz-staging-exporter-fix
🐛 Fix issue with pdf render on exporter
2026-02-06 11:40:18 +01:00
Andrey Antukh 6f3f2f9a71 🐛 Fix issue with pdf render on exporter
When paired with release build penpot app
2026-02-06 11:19:56 +01:00
Alejandro Alonso ad5e8ccdb3 🐛 Fix pdf sizing issue on export (#8274) 2026-02-05 09:23:14 +01:00
Andrey Antukh 44c7d3fbd6 Backport .github workflows from develop 2026-02-04 16:21:19 +01:00
Andrey Antukh 3d50aa6cb2 ⬆️ Update imagemagick version 2026-02-04 16:21:19 +01:00
Andrey Antukh 06afd94a74 ⬆️ Update backend dependencies (mainly bugfixes) 2026-02-04 16:21:19 +01:00
Andrey Antukh e7d9dca55e ⬆️ Update jdk and node on devenv and other images 2026-02-04 16:21:19 +01:00
Andrey Antukh c14ccc18b8 Import mcp from develop 2026-02-04 16:21:19 +01:00
Andrey Antukh 7d09d930fe 📚 Update changelog 2026-02-03 11:13:46 +01:00
Andrey Antukh 0d9b7ca696 Merge tag '2.13.0-RC11' 2026-02-03 08:23:27 +01:00
Andrey Antukh d215a5c402 Merge tag '2.13.0-RC10' 2026-02-03 08:22:50 +01:00
Andrey Antukh f65292a13c 📎 Mark as skip two text editor v2 tests (flaky) 2026-01-29 10:43:29 +01:00
Andrey Antukh 94722fdec2 Ensure .hidePopover fn exist before call 2026-01-29 10:43:29 +01:00
Andrey Antukh 28509e0418 Ensure .stopPropagation fn exists before calling
Also for .stopImmediatePropagation and .preventDefault on
the event instances.
2026-01-29 10:43:29 +01:00
Eva Marco 9569fa2bcb 🐛 Fix error when creating a token with an invalid name (#8216) 2026-01-29 10:41:52 +01:00
Eva Marco 852b31c3a0 🐛 Fix allow spaces on token description (#8234) 2026-01-29 10:40:32 +01:00
Andrés Moya 84b3f5d7c6 🐛 Fix import of shadow tokens 2026-01-29 10:25:22 +01:00
David Barragán Merino 76bd31fe7d 🔧 Fix CORS error 2026-01-28 13:40:45 +01:00
David Barragán Merino cc81e56d82 🔧 Fix CORS error 2026-01-28 13:40:26 +01:00
Andrey Antukh a9e2fc8d94 Backport linter fixes and config from develop 2026-01-28 12:58:54 +01:00
David Barragán Merino 77bbf30ae4 🔧 Fix file name 2026-01-27 21:16:44 +01:00
David Barragán Merino 693b52bf45 📚 Fix links related to penpot plugins 2026-01-27 21:16:44 +01:00
David Barragán Merino 0f51b23ce7 🔧 Deploy plugin styles documentation 2026-01-27 21:16:44 +01:00
David Barragán Merino ec61aa6b6d 🔧 Add custom domain 2026-01-27 21:16:41 +01:00
David Barragán Merino 18aca16f98 🔧 Fix file name 2026-01-27 21:12:32 +01:00
David Barragán Merino c6465e27e3 📚 Fix links related to penpot plugins 2026-01-27 21:12:32 +01:00
David Barragán Merino 1834a18263 🔧 Deploy plugin styles documentation 2026-01-27 21:12:32 +01:00
David Barragán Merino d220d07875 🔧 Add custom domain 2026-01-27 21:12:32 +01:00
Eva Marco 9ca76c745f 🐛 Fix app freeze on token name change (#8214) 2026-01-27 17:31:50 +01:00
Andrey Antukh 3112b240a0 📎 Add missing entry on changelog 2026-01-27 09:28:41 +01:00
Andrey Antukh 56fd66b91a 🐛 Fix several issues related to path edition (#8187)
*  Improve save-path-content event consistency

Mainly removing possible race conditions from the event
implementation.

*  Ensure path content snapshot on start-path-edit event

*  Reuse already available shape-id on split-segments
2026-01-27 09:27:42 +01:00
Andrey Antukh abc1773f65 Merge tag '2.13.0-RC9' 2026-01-26 18:12:16 +01:00
David Barragán Merino 93f5e74bb0 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:14:15 +01:00
David Barragán Merino 1ce0b60e3d 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:13:54 +01:00
David Barragán Merino 38179ba11e 🔧 Enable secret inheritance 2026-01-26 14:01:22 +01:00
David Barragán Merino ef80901400 🔧 Enable secret inheritance 2026-01-26 14:00:55 +01:00
David Barragán Merino 719a95246a 🔧 Define deploy plugin packages workflows 2026-01-26 13:48:33 +01:00
David Barragán Merino e590cd852d 🔧 Rename wrangle to wrangler 2026-01-26 13:48:33 +01:00
David Barragán Merino a9741073e5 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-26 13:48:33 +01:00
David Barragán Merino 5306bed548 🔧 Define deploy plugin packages workflows 2026-01-26 13:47:57 +01:00
David Barragán Merino 92a319ddd1 🔧 Rename wrangle to wrangler 2026-01-26 13:47:57 +01:00
David Barragán Merino 68a6d4c9a8 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-26 13:47:57 +01:00
Andrey Antukh f07495ae95 🐛 Fix incorrect handling of numeric values layout padding and gap
Fixes https://github.com/penpot/penpot/issues/8113
2026-01-26 11:01:25 +01:00
Andrey Antukh 23d5fc7408 🐛 Prevent exception on open-new-window when no window is returned
Fixes https://github.com/penpot/penpot/issues/7787
2026-01-26 11:01:25 +01:00
Andrey Antukh 8632b18eec 🐛 Avoid json decoder liner limit exception by chunking
Happens only when we send large binary data serialized with transit
(mainly used for upload fonts data).
2026-01-26 11:01:25 +01:00
Andrey Antukh 33e650242c Add slugify to the filename on assets exportation
Fixes https://github.com/penpot/penpot/issues/8017
2026-01-26 11:01:25 +01:00
Andrey Antukh e03ad25118 🔧 Backport CI workflow from develop 2026-01-26 10:18:24 +01:00
David Barragán Merino 599656c31e 🔧 Fix a typo in an interpolation 2026-01-23 19:52:47 +01:00
David Barragán Merino 5d7e6afd76 🔧 Fix a typo in an interpolation 2026-01-23 19:52:20 +01:00
Alonso Torres 15d369493b 🐛 Fix problem with z-index modal in dashboard (#8178) 2026-01-23 12:48:01 +01:00
Eva Marco 9c9b672e3e 🐛 Fix spanish translations on import export token modal (#8172) 2026-01-23 10:05:20 +01:00
Eva Marco 5146221513 🐛 Fix allow negative spread values on shadow token creation (#8167)
* 🐛 Fix allow negative spread values on shadow token creation

* 🎉 Add test
2026-01-23 09:50:36 +01:00
Eva Marco e53f335204 🐛 Fix unhandled error on tokens modal (#8165) 2026-01-23 09:35:53 +01:00
David Barragán Merino 16f22a7b5c 🔧 Fixes to the API documentation deployer 2026-01-22 12:10:27 +01:00
David Barragán Merino a1460115e8 🔧 Deploy penpot api documentation 2026-01-22 12:10:27 +01:00
David Barragán Merino 2574ad3315 🔧 Fixes to the API documentation deployer 2026-01-22 12:09:38 +01:00
David Barragán Merino e6b5364a84 🔧 Deploy penpot api documentation 2026-01-22 12:09:38 +01:00
Alonso Torres 656f81f89f ⬆️ Update plugins to 1.4.2 (#8157) 2026-01-21 17:36:58 +01:00
Alonso Torres 01a4ffeb8b ⬆️ Updated plugins release to 1.4.0 (#8148) 2026-01-21 15:41:00 +01:00
Andrey Antukh b8c70be9a2 Make frontend build and watch process more resilent to errors 2026-01-21 13:44:35 +01:00
Andrey Antukh 525adcfcbe Add wasm build on watch app script (devenv) 2026-01-21 13:44:35 +01:00
Eva Marco 7cce4c6532 🐛 Fix unhandled exception tokens creation dialog (#8136) 2026-01-21 13:09:22 +01:00
Alejandro Alonso a3fdd8b691 Merge pull request #8147 from penpot/niwinz-staging-file-menu-issue
 Use correct team-id on file-menu on dashboard
2026-01-21 12:54:28 +01:00
Andrey Antukh b6a9579c98 Use correct team-id on file-menu on dashboard
Before the changes on this commit, the team object is used for
retrieve the id, where we already have team-id. Additionally, the
team object resolution is async operation and is not available on
the first render which causes strange issues on automated flows
(playwright) where an option is clicked when the async flow is
still pending and we have no team object loaded.
2026-01-21 12:23:44 +01:00
Eva Marco 8c7fd0af4b 🐛 Fix shadow reference validation (#8132) 2026-01-21 09:17:03 +01:00
Andrey Antukh cf46051f56 🔥 Remove .traivis.yml file from the repository 2026-01-20 19:40:31 +01:00
Andrey Antukh 6393330ee1 Merge remote-tracking branch 'origin/staging' 2026-01-20 16:25:10 +01:00
Andrey Antukh 8252bc485e 📚 Fix oidc callback related documentation issue 2026-01-20 16:24:12 +01:00
Alonso Torres 47775a9e2c Merge pull request #8134 from penpot/alotor-fix-plugins-export
🐛 Fix problem with export in plugins
2026-01-20 15:03:04 +01:00
Andrey Antukh 8191d04114 Use non-legacy config example on docker compose file 2026-01-20 13:25:55 +01:00
Andrey Antukh 88f0f75174 Merge pull request #8129 from penpot/niwinz-staging-bugfix-1
 Several improvements for build process
2026-01-20 12:26:55 +01:00
Andrey Antukh 1ffa956251 Include timestamp on version tag 2026-01-20 12:26:39 +01:00
Andrey Antukh 31054099ff Use pseudo-names on release builds of frontend (#8105) 2026-01-20 12:26:39 +01:00
Eva Marco 983487d73c 🐛 Fix shadow token reference validation (#8128) 2026-01-20 10:56:27 +01:00
Andrey Antukh 3ecf509f3b 🔧 Add missing gif files handling on defaut nginx config 2026-01-19 13:32:01 +01:00
Dalai FelintoandDalai Felinto 7f395b2642 🐛 Fix import tokens dialog default option (#8051)
This was intended to be changed on 13fcf3a9bb. However only the menu
order changed, not the default option.

Signed-off-by: Dalai Felinto <dalai@blender.org>
Co-authored-by: Dalai Felinto <dalai@blender.org>
2026-01-19 11:14:34 +01:00
Juan de la Cruz 2a62bd2586 🎉 Add 2.13 release slides (#8116) 2026-01-19 11:09:58 +01:00
Andrey Antukh 1ae7515189 🐛 Set correct defaults for storage configuration on compose (#8060)
* 🔥 Remove mention to legacy and never released admin on compose

* 🐛 Set correct defaults for storage configuration on compose
2026-01-16 08:11:34 +01:00
andrés gonzález 8c401f5346 📚 Add Files and Projects section to user guide (#8095) 2026-01-15 17:01:05 +01:00
andrés gonzález b72de2dc8f 📚 Add shadow token documentation (#8082) 2026-01-15 09:09:08 +01:00
Alejandro Alonso 51635770ce Merge pull request #8065 from penpot/eva-fix-lost-translation
🐛 Fix translation on rename token
2026-01-14 09:44:41 +01:00
Eva Marco 18a4e63da0 🐛 Fix translation on rename token 2026-01-13 15:57:09 +01:00
Andrey Antukh d71f811dbe Update help center build script 2026-01-13 10:35:15 +01:00
Andrey Antukh 1beb3b86aa 🐛 Add missing msgid_plural on several translations 2026-01-12 11:52:07 +01:00
Andrey Antukh fe20bdd00e 🐛 Fix multiple selection options on dashboard deleted page (#8055)
* 🐛 Fix multiple selection options on dashboard deleted page

* 📎 Fix translations
2026-01-12 11:41:37 +01:00
María Valderrama 5420897b92 🐛 Fix empty state message in trash page (#8045) 2026-01-12 11:17:01 +01:00
Andrey Antukh e430a4c9f3 Revert " Backport translations from develop"
This reverts commit ec6d72bd91.
2026-01-12 10:40:17 +01:00
Andrey Antukh ec6d72bd91 Backport translations from develop 2026-01-12 09:39:33 +01:00
Eva Marco 6ec451b46d 🐛 Fix resolved value on line height (#8047) 2026-01-09 12:54:24 +01:00
Andrey Antukh 2b836f10cb 🐛 Do not show deleted files on search (#8036)
* 🐛 Do not show deleted files on search

* 💄 Add cosmetic changes to dashboard deleted files page
2026-01-09 11:11:29 +01:00
Andrey Antukh 1ae0f3fc87 Merge pull request #8037 from penpot/niwinz-staging-project-name-fix
🐛 Fix long project name visual problem on dashboard
2026-01-08 17:41:37 +01:00
Eva Marco e13c203b8d ♻️ Refactor scss file 2026-01-08 16:35:56 +01:00
Andrey Antukh 90efb665b5 Add several additional renames for make translation string consistent 2026-01-08 14:37:58 +01:00
Pablo Alba 47ee490158 🐛 Fix typos on download modal 2026-01-08 14:37:58 +01:00
Andrey Antukh f0f89599bc 🌐 Backport translations from develop 2026-01-08 14:08:02 +01:00
Andrey Antukh b0dc7d6ffb 🔧 Change default jmx port on deps.edn 2026-01-08 13:56:22 +01:00
Andrey Antukh b7cd315872 🐛 Fix wasm-playground on devenv 2026-01-08 13:48:09 +01:00
Eva Marco 743d4e5c8d 🐛 Fix error on shadow token creation (#8029) 2026-01-08 13:26:01 +01:00
Andrey Antukh 97e4f4c424 🐛 Fix long project name visual problem on dashboard 2026-01-08 11:40:55 +01:00
Belén Albeza fb9560c315 🐛 Fix guides dropdown width (#8031)
* 🐛 Fix width of guides column dropdown

* ♻️ Remove deprecated tokens in css

* 🔧 Update changelog
2026-01-08 10:47:11 +01:00
Alejandro Alonso d53c090900 Merge pull request #8028 from penpot/elenatorro-12956-fix-text-color-tokens
🐛 Fix missing text color token from selected shapes in selected colors list
2026-01-07 16:49:41 +01:00
Elena Torro 621e030095 🐛 Fix missing text color token from selected shapes in selected colors list 2026-01-07 16:41:25 +01:00
Alejandro Alonso 157e4aa2d0 Merge pull request #8025 from penpot/elenatorro-12951-fix-inner-text-shadow-token
🐛 Fix inner shadow selector on shadow token
2026-01-07 16:37:19 +01:00
Elena Torro 7cd2308f3b 🐛 Fix inner shadow selector on shadow token 2026-01-07 16:36:51 +01:00
Alejandro Alonso c315a15b48 Merge pull request #8026 from penpot/elenatorro-12997-fix-clojure-on-css-box-shadow
🐛 Fix CSS generated box-shadow property
2026-01-07 16:32:12 +01:00
Elena Torro 8a3e6d026e 🐛 Fix CSS generated box-shadow property 2026-01-07 16:28:05 +01:00
Florian Schrödl 0dd062d011 🐛 Fix line-height throwing for int (#7927) 2026-01-07 16:13:10 +01:00
Alejandro Alonso bfbb546699 Merge pull request #8027 from penpot/superalex-fix-colors-assets-from-shared-libraries
🐛 Fix color assets from shared libraries
2026-01-07 14:16:57 +01:00
Alejandro Alonso 083e77e9c5 🐛 Fix color assets from shared libraries 2026-01-07 14:02:28 +01:00
Alejandro Alonso 919f78daeb Merge pull request #7965 from penpot/eva-fix-styles-on-viewer
🐛 Fix inspect tab styles on viewer
2026-01-07 11:54:33 +01:00
Eva Marco b5c30f8c41 🐛 Fix inspect tab styles on viewer 2026-01-07 11:41:49 +01:00
Alejandro Alonso 60aa426753 Merge pull request #8022 from penpot/alotor-fix-drag-handlers
🐛 Fix problem with dragging handlers
2026-01-07 11:36:26 +01:00
Alejandro Alonso 86f7d6b26b Sanitizing error values (#8020) 2026-01-07 11:23:19 +01:00
Andrey Antukh 36732a4bd3 Make the devenv runtine initialization yarn independent (#8023) 2026-01-07 11:21:58 +01:00
Andrey Antukh eff572d3bb Revert "💄 Group tokens by name path (#7775)"
This reverts commit 0956b66281.
2026-01-07 11:20:44 +01:00
alonso.torres d470d96833 🐛 Fix problem with dragging handlers 2026-01-07 11:00:02 +01:00
Aitor Moreno cab70773d2 Merge pull request #7667 from penpot/azazeln28-doc-add-more-info-text-editor-v2-readme
📚 Add more info about text editor v2
2026-01-07 09:40:06 +01:00
Alejandro Alonso 32ca42a093 Merge remote-tracking branch 'origin/staging-render' into staging 2026-01-05 13:21:58 +01:00
Alejandro Alonso 523a97a4ec Merge pull request #8016 from penpot/alotor-fix-refresh-thumbnails
🐛 Fix problem with thumbnail regeneration
2026-01-05 13:21:34 +01:00
Alejandro Alonso 260f6861a3 Merge pull request #8015 from penpot/alotor-fix-grid-component-auto-sizing
🐛 Fix problem with grid layout components and auto sizing
2026-01-05 13:18:59 +01:00
alonso.torres edd53b419a 🐛 Fix problem with thumbnail regeneration 2026-01-05 13:09:40 +01:00
alonso.torres 078a3d5a5c 🐛 Fix problem with grid layout components and auto sizing 2026-01-05 10:54:36 +01:00
Alejandro Alonso c4e57427ac Merge branch 'staging-render' into staging 2026-01-05 10:30:06 +01:00
Alejandro Alonso 7a6405481c Merge remote-tracking branch 'origin/develop' into staging 2026-01-05 08:37:19 +01:00
Alejandro Alonso 218f34380a Merge pull request #8012 from penpot/azazeln28-refactor-minor-changes
♻️ Minor naming changes and event handling
2026-01-02 14:16:55 +01:00
Alejandro Alonso 47aaa2b5fa Merge pull request #8011 from penpot/alotor-fix-trash-bar
🐛 Fix problems with trash bar in dashboard
2026-01-02 13:46:53 +01:00
Aitor Moreno 6c6b3db87e ♻️ Minor naming changes and event handling 2026-01-02 13:41:48 +01:00
Alejandro Alonso 6eb32cfb79 Merge pull request #8008 from penpot/alotor-fix-path-editor
🐛 Fix problem with path editor and right click
2026-01-02 13:29:44 +01:00
alonso.torres dbba3496af 🐛 Fix problems with trash bar in dashboard 2026-01-02 12:00:16 +01:00
alonso.torres 55752d361f 🐛 Fix problem with path editor and right click 2026-01-02 10:37:52 +01:00
Alejandro Alonso fe94ee4526 Merge pull request #8009 from penpot/alotor-fix-style-font-input
🐛 Fix problem with style in fonts input
2026-01-02 10:23:02 +01:00
Aitor Moreno e39f292499 📚 Add more info about text editor v2 2026-01-02 10:13:34 +01:00
Andrey Antukh 5c71c57dd9 Merge tag '2.12.1' 2025-12-30 15:37:30 +01:00
Andrey Antukh 52b8560b70 Merge branch 'staging-render' into develop 2025-12-30 15:30:56 +01:00
Andrey Antukh 75860afe57 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-30 15:29:58 +01:00
Andrey Antukh 824ca1bbca 🔧 Make devenv init yarn indpendent 2025-12-30 15:28:19 +01:00
Andrey Antukh 5b6f9c1741 📎 Disable legacy cache on plugins nx config 2025-12-30 14:56:15 +01:00
Andrey Antukh 19853b832b 📚 Update documentation 2025-12-30 14:56:15 +01:00
Andrey Antukh d20c011db2 Migrate plugins to pnpm 2025-12-30 14:56:15 +01:00
Andrey Antukh 9431ae6858 📎 Update docs 2025-12-30 14:56:15 +01:00
Andrey Antukh 96356c1b89 ⬆️ Update storybook and fix compatibility issues 2025-12-30 14:56:15 +01:00
Andrey Antukh b7b68eeb47 🔥 Remove npx prefix on package.json scripts 2025-12-30 14:56:15 +01:00
Andrey Antukh 9bbeb657f8 🔧 Add plugins runtime ci job 2025-12-30 14:56:15 +01:00
Andrey Antukh ec1af4ad96 🎉 Import penpot-plugins repository
As commit 819a549e4928d2b1fa98e52bee82d59aec0f70d8
2025-12-30 14:56:15 +01:00
alonso.torres 23e7116b24 🐛 Fix problem with style in fonts input 2025-12-30 14:28:10 +01:00
Alejandro Alonso 48e3f35bb3 🐛 Fix setting a portion of text as bold or underline messes things up 2025-12-30 11:34:24 +01:00
Andrey Antukh 6b794c9d12 Merge branch 'staging' into staging-render 2025-12-30 11:13:15 +01:00
Yamila Moreno d3ee50daf5 🔧 Add ci for branch staging-render 2025-12-30 11:13:00 +01:00
Yamila Moreno 22a36d59d8 🔧 Add ci for branch staging-render 2025-12-30 10:57:51 +01:00
Alejandro Alonso a948e49e51 🐛 Fix using cache on first zoom after pan 2025-12-30 10:03:24 +01:00
Alejandro Alonso d635f5a8dc 🐛 Detecting situations where WebGL context is lost or no WebGL support 2025-12-30 10:03:24 +01:00
Alejandro Alonso ab3a3ef43b 🎉 Resize cache only when required 2025-12-30 10:03:24 +01:00
Alejandro Alonso 9c21fd3359 🐛 Fix resize cache memory leak 2025-12-30 10:03:24 +01:00
Andrey Antukh 7b5817f407 ♻️ Make several adjustments to the dashboard deleted page (#7999)
* ♻️ Make several sustantial adjustments to the dashboard deleted page

* 📎 Add PR feedback changes
2025-12-30 09:52:29 +01:00
Yamila Moreno e3405eacca 🔧 Improve mattermost notification 2025-12-29 19:06:26 +01:00
Alejandro Alonso 44b70cf1d4 Merge pull request #7998 from penpot/alotor-fix-problem-with-create-grid
🐛 Fix problem creating grid from elements
2025-12-29 14:31:15 +01:00
Alejandro Alonso a8bd74b392 Merge pull request #8001 from penpot/alotor-fix-gfonts-references
🐛 Fix problem with some fonts
2025-12-29 14:25:54 +01:00
alonso.torres 3d3e3582d6 🐛 Fix problem with some fonts 2025-12-29 12:35:19 +01:00
Andrey Antukh de052b5161 📎 Update changelog 2025-12-29 11:10:04 +01:00
Andrey Antukh e01654ba43 Merge branch 'staging-render' into develop 2025-12-29 10:43:00 +01:00
Andrey Antukh 6ebd48b94c Merge branch 'staging' into staging-render 2025-12-29 10:41:08 +01:00
Andrey Antukh 8a3b33797f 🐛 Fix error handling on password change form
Fixes https://github.com/penpot/penpot/issues/7978
2025-12-29 10:27:27 +01:00
Andrey Antukh 13fd20f76f Backport form error management improvements from develop 2025-12-29 10:27:27 +01:00
alonso.torres 417cd80564 🐛 Fix problem creating grid from elements 2025-12-23 14:49:21 +01:00
Alejandro Alonso a57011ec7b Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-23 13:35:27 +01:00
Andrey Antukh 69c880d00e 🐛 Fix importmap usage on firefox 2025-12-23 13:10:58 +01:00
Andrey Antukh 9eebc467ef Preload default translations 2025-12-23 13:10:58 +01:00
Andrey Antukh b77712ce73 Move frontend/vendor to frontend/packages 2025-12-23 13:10:58 +01:00
Andrey Antukh 3d3e81f314 Replace tubax with more modern tooling 2025-12-23 13:10:58 +01:00
Andrey Antukh fe6441bb24 Replace hightlight.js internal bundle with direct npm use 2025-12-23 13:10:58 +01:00
Andrey Antukh e15f0baf30 Replace direct draft-js usage with internal module 2025-12-23 13:10:58 +01:00
Andrey Antukh c040cbb784 🔥 Remove old gulp related dependencies 2025-12-23 13:10:58 +01:00
Andrey Antukh 7f674b78a9 📎 Move all deps to dev-dependencies on frontend package.json
All they only needed for build process.
2025-12-23 13:10:58 +01:00
Andrey Antukh 099b78affd 📎 Update frontend yarn.lock file 2025-12-23 13:10:58 +01:00
Andrey Antukh 78cc3f0aa4 📎 Add immutable dependency to vendor/draft-js 2025-12-23 13:10:58 +01:00
Andrey Antukh 76f5f12808 ⬆️ Update dependencies on exporter 2025-12-23 13:10:58 +01:00
Alejandro Alonso cb325282ec Merge pull request #7994 from penpot/alotor-fix-font-style
🐛 Fix problem when changing colors with multiple fonts
2025-12-23 07:34:41 +01:00
Andrey Antukh 01ecde3bfa Add the ability to add relations on penpot sdk (#7987)
*  Add the ability to add relations on penpot sdk

* 📎 Remove debug console log
2025-12-22 20:55:31 +01:00
Andrey Antukh 047483a70a 🐛 Fix deleted files thumbnails generation 2025-12-22 20:20:43 +01:00
Alonso Torres 4000ec8762 🐛 Fix problem resizing auto size layouts (#7995) 2025-12-22 20:17:11 +01:00
Andrey Antukh 8cb2f27de8 ♻️ Move file permissions to binfile common ns 2025-12-22 20:16:41 +01:00
Andrey Antukh 0433336fc9 📎 Use correct criterium version on frontend deps 2025-12-22 20:16:41 +01:00
Andrey Antukh ce234fbeda Allow get thumbnails for deleted files 2025-12-22 20:16:41 +01:00
Andrey Antukh fc4d31eed7 Add minor efficiency improvements to deleted dashboard page 2025-12-22 20:16:41 +01:00
María Valderrama c670aac339 🎉 Added deleted files to dashboard 2025-12-22 20:16:41 +01:00
Andrés Moya 1d3fb5434f Enable shadow tokens by default (#7996) 2025-12-22 18:17:29 +01:00
Andrey Antukh f478399ae0 Merge remote-tracking branch 'origin/staging-render' into develop 2025-12-22 17:28:18 +01:00
Andrey Antukh 6a1854f180 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 17:28:01 +01:00
Andrés Moya 0858e297e5 🎉 Add composite tokens to plugins API (#7992) 2025-12-22 17:14:54 +01:00
alonso.torres bd580ab159 🐛 Fix problem when changing colors with multiple fonts 2025-12-22 17:14:37 +01:00
Alejandro Alonso 5780a43fe0 🐛 Fix object added in different page (#7988) 2025-12-22 16:59:47 +01:00
Alejandro Alonso 737eceda3a 🐛 Fix unmasking shapes (#7989) 2025-12-22 16:59:04 +01:00
Alonso Torres 923c3c2dbd 🐛 Fix font weight token (#7991) 2025-12-22 16:58:26 +01:00
Alejandro Alonso a14b4561e7 🐛 Fix comment bubbles (#7990) 2025-12-22 16:57:45 +01:00
Andrey Antukh bb5568e15a 🎉 Enable hindi translations on the application 2025-12-22 16:57:00 +01:00
Pablo Alba 5cbcec3db6 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:57:00 +01:00
Anonymous 105e1fe86c 🌐 Add translations for: Spanish
Currently translated at 97.1% (1940 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-12-22 16:34:43 +01:00
Yaron Shahrabani 3e0a916883 🌐 Add translations for: Hebrew
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-22 16:34:42 +01:00
Ahmad HosseinBor 4f80238bc2 🌐 Add translations for: Persian
Currently translated at 39.2% (783 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2025-12-22 16:34:42 +01:00
Alejandro Alonso 5156cc5d9a 🌐 Add translations for: Yoruba
Currently translated at 58.8% (1176 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-12-22 16:34:42 +01:00
Yessenia Villarte Vaca 42c46b6cfc 🌐 Add translations for: Spanish (Latin America)
Currently translated at 6.5% (131 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es_419/
2025-12-22 16:34:41 +01:00
VKing9 8b3c40b35e 🌐 Add translations for: Hindi
Currently translated at 99.6% (1991 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-22 16:34:41 +01:00
Andy Li d3996e5fb1 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 80.1% (1601 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-12-22 16:34:40 +01:00
Anonymous 0c42bca866 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Marius e5685c1f1c 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Anonymous 2784209bde 🌐 Add translations for: Turkish
Currently translated at 99.5% (1989 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-12-22 16:34:40 +01:00
Alejandro Alonso 024f460e99 🌐 Add translations for: Igbo
Currently translated at 25.6% (512 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2025-12-22 16:34:39 +01:00
Anonymous 1d9b76b62a 🌐 Add translations for: Romanian
Currently translated at 97.2% (1942 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-12-22 16:34:39 +01:00
Shuaib Zahda 7e17a75b7d 🌐 Add translations for: Arabic
Currently translated at 56.4% (1127 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-12-22 16:34:39 +01:00
Anonymous ca093d6fae 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Alexandre Pawlak 0f0b7562b5 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Anonymous 9cdc694697 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Dário b972a4033b 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Anonymous cbe9f4da51 🌐 Add translations for: Swedish
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-12-22 16:34:37 +01:00
Anonymous c583bde9e3 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:37 +01:00
Vint Prox 3911ebdc4e 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:36 +01:00
Anonymous 3e3b18667b 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 69.9% (1396 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-12-22 16:34:36 +01:00
Anonymous ed81c9b8df 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Sebastiaan Pasma fbdf98d29c 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Anonymous e603825a55 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Valentina Chapellu 1d724783e6 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Hosted Weblate e0abe7dcb5 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-12-22 16:33:05 +01:00
Andrey Antukh 5c1bbf5be8 Merge remote-tracking branch 'weblate/develop' into develop 2025-12-22 16:32:30 +01:00
Pablo Alba bbb0d58190 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:27:10 +01:00
Andrey Antukh 88dcf9d1fe 🐛 Mark rpc calls as authenticated when shared key is used (#7901) 2025-12-22 12:18:36 +01:00
Alejandro Alonso fe44c14bac Merge pull request #7982 from penpot/niwinz-staging-import-bucket
🐛 Prefill storage object bucket if it comes nil on import binfile
2025-12-22 12:17:16 +01:00
Belén Albeza 20061067ad 🐛 Fix text editor not getting focus back after font variant change 2025-12-22 11:18:25 +01:00
Andrey Antukh 336173645e 🐛 Fix regression on export shape on plungins API 2025-12-22 10:41:42 +01:00
Andrey Antukh 2acf15958b Merge branch 'staging-render' into develop 2025-12-22 09:24:04 +01:00
Andrey Antukh 08267de242 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 09:23:48 +01:00
Pablo Alba 35fb376a78 Add proxypass to caddyfile on devenv (#7985) 2025-12-22 09:21:22 +01:00
Dalai FelintoandDalai Felinto 13fcf3a9bb 💄 Set import Tokens default option to be Single JSON value (#7918)
This patches makes the default Tokens importing option to match the
current default Tokens exporting option (single JSON value). This way it
is more obvious and quick to export the tokens from a file and import
in new one,

---

While testing our design system we are often re-exporting and
re-importing the Tokens to the files using the design system components.

I'm aware that this may be addressed in the future so the Tokens are
brought in together with the library. Meanwhile (and even in the future)
I think it is sensible to have a symmetry between the export and import
defeault options.

Co-authored-by: Dalai Felinto <dalai@blender.org>
2025-12-19 10:44:05 +01:00
Andrey Antukh 83bb4bf221 🐛 Prefill storage object bucket if it comes nil on import binfile 2025-12-19 09:32:51 +01:00
Henrik Steffens dba6ae2820 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:25 +01:00
Marius ada101c236 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:24 +01:00
Marius ea48fb5825 🌐 Add translations for: German
Currently translated at 90.4% (1804 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-16 11:00:25 +01:00
Alejandro Alonso 15ed25ca79 Merge pull request #7966 from penpot/niwinz-staging-abrreviate
🐛 Fix incorrect string truncation with abbreviate template filter
2025-12-12 13:53:33 +01:00
Andrey Antukh 9aa387a473 🐛 Fix incorrect string truncation with abbreviate template filter 2025-12-12 13:50:46 +01:00
Alejandro Alonso 67ba91b4b9 Merge pull request #7971 from penpot/niwinz-staging-bugfix-6
🐛 Fix tokens-lib encoding when value is nilable
2025-12-12 13:46:06 +01:00
Alejandro Alonso f67f1a6a0e Merge pull request #7972 from penpot/niwinz-staging-bugfix-7
🐛 Fix exception on assinging gradient to shadow on multiple selection
2025-12-12 13:42:39 +01:00
Alejandro Alonso 82d3e2024e Merge pull request #7973 from penpot/niwinz-staging-worker-scheduler
🐛 Fix incorrect redis connection error handling
2025-12-12 13:23:49 +01:00
Alejandro Alonso 4bd846c16d Merge pull request #7969 from penpot/niwinz-staging-fix-ratelimit
🐛 Fix issue on reading rlimit config
2025-12-12 13:22:53 +01:00
alonso.torres 8fde6b28ed 🐛 Fix problems with alignments and margins 2025-12-12 13:21:04 +01:00
alonso.torres 63325ec796 🐛 Fix problem with flex fill size distribution 2025-12-12 13:21:04 +01:00
alonso.torres 84415476d0 🐛 Fix problem with reflow layout 2025-12-12 13:21:04 +01:00
Andrey Antukh 94f95ca6b8 🐛 Fix incorrect redis connection error handling 2025-12-12 12:33:38 +01:00
Andrey Antukh 33c786498d Merge remote-tracking branch 'origin/staging-render' into develop 2025-12-12 12:19:49 +01:00
Andrey Antukh 5abc1aafb4 Merge tag '2.12.0-RC3' 2025-12-12 12:19:29 +01:00
Andrey Antukh 1f886b1f88 Merge remote-tracking branch 'origin/staging' into develop 2025-12-12 12:16:41 +01:00
Aitor Moreno 5a922c6bd6 Merge pull request #7960 from penpot/superalex-fix-too-many-active-webgl-contexts
🐛 Fix too many active WEBGL contexts
2025-12-12 12:03:46 +01:00
Andrey Antukh 507bf7445b 🐛 Fix tokens-lib encoding when value is nilable 2025-12-12 11:42:15 +01:00
Andrey Antukh 81b72c5acd 🐛 Fix exception on assinging gradient to shadow on multiple selection 2025-12-12 11:24:53 +01:00
Alejandro Alonso 1388865cfc 🐛 Fix too many active WEBGL contexts 2025-12-12 11:16:47 +01:00
Andrey Antukh 1738847694 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-12 10:20:21 +01:00
Aitor Moreno ca1c3c799d Merge pull request #7968 from penpot/alotor-fix-border-radius
🐛 Fix problem with border radius to path
2025-12-12 10:18:07 +01:00
Andrey Antukh 974495e08f Reduce log level for profile picture download error
Because it is not blocking operation and does not provents user
to proceed.
2025-12-12 08:17:13 +01:00
Andrey Antukh 2ed39e43c3 🐛 Fix issue on reading rlimit config 2025-12-11 23:50:01 +01:00
alonso.torres ce5006ae84 🐛 Fix problem with border radius to path 2025-12-11 22:40:44 +01:00
Eva Marco 50dbe6ab12 🐛 Fix horizontal scroll on layer panel (#7956) 2025-12-11 21:34:18 +01:00
Belén Albeza 0a7a65af5d ♻️ Make SerializableResult to depend on From traits 2025-12-11 16:00:03 +01:00
alonso.torres ea4d0e1238 Calculate position data in wasm 2025-12-11 16:00:03 +01:00
Elena Torro b705cf953a 🐛 Set layout data from set-object 2025-12-11 14:52:32 +01:00
Alejandro Alonso 90ce1f56e7 Merge pull request #7958 from penpot/superalex-fix-svg-extract-ids
🐛 Fix svg extract ids
2025-12-11 14:02:05 +01:00
Alejandro Alonso ab0438cc6f 🐛 Fix svg extract ids 2025-12-11 13:47:00 +01:00
Aitor Moreno c6aa9cc4b7 Merge pull request #7950 from penpot/ladybenko-12851-fix-text-selection
🐛 Fix text selection when editor regains focus
2025-12-11 13:45:29 +01:00
Andrey Antukh 5779adef33 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-11 13:30:59 +01:00
Andrey Antukh 2f46cbc0d4 Make render wasm import on worker http cache aware 2025-12-11 13:27:20 +01:00
Elena Torró ebf1758958 Merge pull request #7935 from penpot/superalex-improve-svg-import
🎉 Improve svg import
2025-12-11 13:21:29 +01:00
Elena Torró e94c56bfa7 Merge pull request #7954 from penpot/azazeln28-fix-font-weight-mixed-value
🐛 Fix font weight mixed value
2025-12-11 12:43:53 +01:00
Andrey Antukh 53be6f996b 🐛 Fix issues on build processs related to render-wasm 2025-12-11 12:41:19 +01:00
Alejandro Alonso 89d9591011 🎉 Improve svg import 2025-12-11 12:02:34 +01:00
Andrey Antukh 5a260294a1 🔧 Update build-tag.yml github workflow 2025-12-11 12:00:42 +01:00
Andrey Antukh 3becfcd723 🔧 Update build-tag.yml github workflow 2025-12-11 11:59:16 +01:00
Andrey Antukh 3f6e44316e 🐛 Add missing node depes install on render-wasm 2025-12-11 11:51:47 +01:00
Aitor Moreno 5501a2815f 🐛 Fix font-variant-id mixed value 2025-12-11 11:32:27 +01:00
Eva Marco 77ef8e6fe6 🐛 Fix scroll on move library modal (#7952) 2025-12-11 10:46:54 +01:00
Alejandro Alonso 1066438b02 Merge pull request #7922 from penpot/elenatorro-12855-improve-pan-rendering
🔧 Improve pan rendering
2025-12-10 15:58:59 +01:00
Alejandro Alonso 3b23a3ad19 Merge pull request #7947 from penpot/elenatorro-12880-fix-variant-ui
🔧 Support variants interactivity on the new render's UI
2025-12-10 15:27:48 +01:00
Andrey Antukh 7396f4bfb6 Merge remote-tracking branch 'origin/staging' into develop 2025-12-10 15:17:50 +01:00
Alejandro Alonso 916b7709dc Update Pencil Penpot Design System System template in carousel (#7948) 2025-12-10 15:09:28 +01:00
Belén Albeza 5cf51f3d26 🐛 Fix text selection not being restore if it was only 1 word 2025-12-10 15:05:13 +01:00
Belén Albeza 25acad5154 🔧 Add formatting rules to the TextEditor 2025-12-10 15:04:34 +01:00
Elena Torro 0a212b6291 🔧 Support variants interactivity on the new render's UI 2025-12-10 14:39:59 +01:00
Eva Marco 443e41fea4 🐛 Fix multiple selection with color tokens (#7941) 2025-12-10 14:36:08 +01:00
Alejandro Alonso c7c9b04095 Merge pull request #7944 from penpot/niwinz-staging-exporter-fix
🐛 Fix incorrect resource lifetime handling on exporter
2025-12-10 14:35:20 +01:00
Eva Marco c61a0c0332 📚 Add line to changelog (#7945) 2025-12-10 13:58:18 +01:00
Andrey Antukh eb1eeb4750 Merge remote-tracking branch 'origin/staging-render' into niwinz-develop-merge 2025-12-10 13:53:15 +01:00
Andrey Antukh a78477592b Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-10 13:36:23 +01:00
Eva Marco 8707ff6511 🎉 Add spanish translation 2025-12-10 13:12:30 +01:00
Florian Schroedl 3d8a251741 🐛 Disallow font-family referencing composite token 2025-12-10 13:12:30 +01:00
Andrey Antukh 34e84ee3c8 🐛 Fix incorrect resource lifetime handling on exporter 2025-12-10 13:02:31 +01:00
Xaviju 0956b66281 💄 Group tokens by name path (#7775)
* 💄 Group tokens by name path
2025-12-10 12:34:19 +01:00
Luis de Dios 007b3f11f9 🐛 Fix pass new icons to radio buttons (#7939) 2025-12-10 12:28:27 +01:00
Alejandro Alonso e8201402a7 Merge pull request #7938 from penpot/niwinz-staging-bugfix-5
🐛 Fix several issues
2025-12-10 12:05:42 +01:00
Aitor Moreno 8a22477b96 Merge pull request #7932 from penpot/niwinz-staging-worker-wasm-load
🐛 Fix WASM loading strategy on worker
2025-12-10 11:47:31 +01:00
Elena Torro a661b2564f 🐛 Fix default case on vertical align 2025-12-10 10:59:27 +01:00
Elena Torro 2c3732f3f4 🔧 Fix line height calculation 2025-12-10 10:59:27 +01:00
Andrey Antukh e16645227b Merge branch 'staging-render' into develop 2025-12-10 10:10:44 +01:00
Andrey Antukh 45665a3c21 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-10 10:09:58 +01:00
Alejandro Alonso 3e684ea54f ⬆️ Update svgo dependency on frontend (#7936) 2025-12-10 10:07:02 +01:00
Eva Marco 179e6a195d 🎉 Add test for token creation (#7915) 2025-12-10 09:56:21 +01:00
Andrey Antukh 98039f13d8 🐛 Fix main toolbar z-index 2025-12-10 09:47:40 +01:00
Alejandro Alonso 40c27591f6 🐛 Fix svg import (#7925) 2025-12-10 08:36:54 +01:00
Andrey Antukh 91d20a46d1 💄 Add cosmetic changes to exports assets progress component 2025-12-10 08:23:05 +01:00
Andrey Antukh 50bead7c56 🐛 Fix react warning on having p inside p on assets export progress 2025-12-10 08:22:41 +01:00
Andrey Antukh b75b999903 📎 Fix devenv jvm warning 2025-12-10 08:22:05 +01:00
Andrey Antukh 810f1721c8 🐛 Fix recursion render on subscription modal 2025-12-10 07:54:52 +01:00
Stephan Paternotte b45bdd723f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-09 22:00:21 +00:00
Ingrid Pigueron 8696044620 🌐 Add translations for: French
Currently translated at 97.1% (1937 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-09 22:00:19 +00:00
Andrey Antukh 8a8f360c7f Merge remote-tracking branch 'origin/staging' into develop 2025-12-09 19:53:38 +01:00
Andrey Antukh a4646373cf ♻️ Refactor wasm loading strategy on worker 2025-12-09 19:41:19 +01:00
Andrey Antukh f111cbb2a4 Add better cache config on devenv nginx 2025-12-09 19:38:30 +01:00
Luis de Dios e35fc85c3d 🎉 Create new empty-state component (#7903) 2025-12-09 16:48:12 +01:00
Aitor Moreno a614207f7e 🐛 Fix exporter failing with HTTPS 2025-12-09 16:08:20 +01:00
Elena Torro 81bc1bb0af 🔧 Log performance when building using profile-macros 2025-12-09 15:25:13 +01:00
Yamila Moreno 1798461d21 🐳 Add override for assets (#7926) 2025-12-09 14:55:21 +01:00
Luis de Dios 6ce3249c6d 🐛 Fix color format does not switch in the view mode (#7923)
* 🐛 Fix color format does not switch in the inspect mode of the view mode

* ♻️ Update components
2025-12-09 14:38:15 +01:00
Yamila Moreno dde0fddd6f 🐳 Add missing override to Dockerfile.frontend (#7920) 2025-12-09 12:08:46 +01:00
Aitor Moreno 7d36bc4025 Merge pull request #7907 from penpot/alotor-fix-export-text
🐛 Fix problem when exporting texts
2025-12-09 11:28:47 +01:00
Elena Torro b8feb6374d 🔧 Rebuild indices on zoom change, not pan 2025-12-09 11:26:03 +01:00
Elena Torro 0889df8e08 🔧 Skip slow operations on fast render 2025-12-09 11:26:03 +01:00
Andrey Antukh 4637aced8c Add support auto decoding and validation syntax for obj/reify 2025-12-09 11:13:06 +01:00
Andrey Antukh 9dfe5b0865 🐛 Fix inconsistencies on using obj/reify on plugins 2025-12-09 11:13:06 +01:00
Andrey Antukh 33bcc9544a Update frontend repl script 2025-12-09 11:13:06 +01:00
Andrey Antukh babd481b7f Make sm/coercer lazy 2025-12-09 11:13:06 +01:00
Andrey Antukh a9733c792d Make check-fn completly lazy 2025-12-09 11:13:06 +01:00
Belén Albeza 7be8ac3fd7 🐛 Fix internal error while importing a library 2025-12-09 11:10:32 +01:00
Pablo Alba b0351be724 🐛 Fix switch variants with paths 2025-12-09 11:08:55 +01:00
Elena Torro 9216d965ef 🔧 Update rendering settings to smooth render 2025-12-09 10:43:33 +01:00
Andrey Antukh d04fdb5fbd Make the dist bundle use consistent and cache-aware uris (#7911) 2025-12-09 08:05:28 +01:00
Ingrid Pigueron 4f3ca6422c 🌐 Add translations for: French
Currently translated at 96.8% (1931 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-08 19:40:14 +01:00
Nicola Bortoletto 1c03457fda 🌐 Add translations for: Italian
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-06 07:00:19 +00:00
Eva Marco 81e0e4f222 ♻️ Replace token form files (#7896)
* ♻️ Replace shadow form

* ♻️ Rename files and components

* ♻️ Replace offsetx and offsety names

* ♻️ Replace form file for new form component using new form system

* ♻️ Rename files and props
2025-12-05 17:04:07 +01:00
Andrey Antukh b8392b3731 🐛 Fix regression on sending team invitations (#7912) 2025-12-05 12:36:06 +01:00
Andrey Antukh 935728aa39 🔧 Backport build-tag github workflow from develop 2025-12-05 10:26:01 +01:00
Andrey Antukh 77dba477ca 🔧 Backport build-tag github workflow from develop 2025-12-05 10:25:03 +01:00
Eva Marco b6598d1f07 🐛 Fix scrollbar on color modal (#7906) 2025-12-05 09:55:41 +01:00
Yamila Moreno f13b3c8737 🔧 Fix bug in Github Actions (#7908) 2025-12-04 20:24:33 +01:00
alonso.torres 520e979363 🐛 Fix problem when exporting texts 2025-12-04 17:32:54 +01:00
Yamila Moreno a0f8559ffc 🔧 Add ci/cd for nitrate-module (#7905) 2025-12-04 16:02:29 +01:00
Xaviju bf1dc21c75 💄 Hide themes & sets panels when none active (#7902) 2025-12-04 14:11:57 +01:00
Alejandro Alonso 46c20a993f Merge pull request #7904 from penpot/niwinz-staging-fix-invitation-resend
🐛 Fix exception on resending invitation
2025-12-04 11:56:07 +01:00
Andrey Antukh 0e0106f69a 🐛 Add correct assertion on create-invitation fn 2025-12-04 11:38:32 +01:00
Andrey Antukh 19bb69cc60 Improve invalid schema error report 2025-12-04 11:38:16 +01:00
Alejandro Alonso 504eb70988 Merge pull request #7885 from penpot/niwinz-staging-bugfix-2
🐛 Make workspace palette reposition on left sidebar collapse
2025-12-04 11:19:20 +01:00
Andrey Antukh a38f425dd3 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-04 11:06:48 +01:00
Xaviju 75a2331edf 💄 Set low-emphasis color for both light/dark modes (#7884) 2025-12-04 11:04:07 +01:00
VKing9 74d4b9b045 🌐 Add translations for: Hindi
Currently translated at 100.0% (1994 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-04 11:00:32 +01:00
Alejandro Alonso c2b4c9907d Merge pull request #7886 from penpot/niwinz-staging-bugfix-3
🐛 Fix casing on a translation of export files modal option
2025-12-04 10:59:51 +01:00
Alejandro Alonso bd5bbcae26 Merge pull request #7894 from penpot/niwinz-staging-bugfix-4
🐛 Fix incorrect interaction betwen hower and scroll on assets sidebar
2025-12-04 10:58:54 +01:00
Andrey Antukh 84273508ad 🐛 Fix incorrect interaction betwen hower and scroll on assets sidebar 2025-12-04 10:56:29 +01:00
Andrey Antukh 9245ba6bc2 💄 Adapt component style for assets-local-library on sidebar assets 2025-12-04 10:55:57 +01:00
Andrey Antukh 4be046406d Pass direct args instead of a vector to toggle-values on sidebar assets 2025-12-04 10:55:57 +01:00
Alejandro Alonso 84c747cd31 Merge pull request #7883 from penpot/niwinz-staging-bugfix
🐛 Fix exception on paste text on comments input
2025-12-04 10:32:07 +01:00
Alejandro Alonso 0036a9a0cd Merge pull request #7865 from penpot/niwinz-staging-audit
 Add minor improvements to the audit module
2025-12-04 10:04:00 +01:00
Alejandro Alonso 2105c3a68c Merge pull request #7866 from penpot/niwinz-staging-fix-emails
🐛 Change internal ordering on how email parts are assembled
2025-12-04 09:56:22 +01:00
Belén Albeza 38efa88460 🐛 Fix unpublish library modal not scrolling file list (#7892)
* 🐛 Fix unpublish library modal not scrolling when the linked files list is too long

* 💄 Remove deprecated tokens in unpublish library modal

* 🔧 Update CHANGELOG
2025-12-03 22:41:20 +01:00
Pablo Alba 6e254c2cf4 🐛 Fix change of library on swap (#7898) 2025-12-03 22:40:23 +01:00
Andrey Antukh 416980f063 🐛 Fix issue on render template on dist bundle (#7899) 2025-12-03 20:48:02 +01:00
Andrey Antukh f76710296c Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 18:52:28 +01:00
Andrey Antukh 6251fa6b22 🐛 Close other open context menus on open a context menu (#7895) 2025-12-03 18:50:00 +01:00
alonso.torres aedd8cc11e 🐛 Fix problem when renaming variants in plugins 2025-12-03 17:42:17 +01:00
Andrey Antukh d1379c55f6 Make i18n translation files load on demand 2025-12-03 16:44:37 +01:00
Andrey Antukh b125c7b5a3 Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 13:55:01 +01:00
Andrey Antukh 496d37795b Adapt docker images nginx config template to latest changes (#7891) 2025-12-03 13:45:18 +01:00
Alonso Torres 2f0853f5cc 🐛 Fix problem with variant plugins api (#7890) 2025-12-03 13:27:32 +01:00
Juan de la CruzandAndrey Antukh 648e660bcf 🎉 Add new content and images for the slides of 2.12 (#7874)
* 🎉 Add new slide's content

* 🎉 Add new slides images

* 📎 Fix clj fmt

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-12-03 13:26:55 +01:00
Andrey Antukh 9f6899007a Merge remote-tracking branch 'origin/staging' into develop 2025-12-03 13:10:30 +01:00
Slava "nerfur" Voronzoff bee2f70bfa 🐛 Add missing amd64 option on arch detection code on mange.sh script
Signed-off-by: Slava "nerfur" Voronzoff <nerfur@gmail.com>
2025-12-03 13:09:49 +01:00
Pablo Alba 00f8eac8fa 🐛 Fix can't delete unsaved variant prop (#7878) 2025-12-03 13:03:17 +01:00
Dalai FelintoandDalai Felinto df7caacb45 🐛 Fix crash in token grid view due to tooltip validation (#7887)
The color tokens in grid view have a tooltip which is a map.
This is done so the frontend can render:

```
Name: foo
Resolved value: #000000
```

However the validation scheme for tooltips was only accepting functions
and strings.

---

How to reproduce the original (unreported) crash:
* Create a color token
* Create a shape, add a fill
* Pick a color, chose the Token options
* Click on the Grid View

Crash: `{:hint "invalid props on component tooltip*\n\n  -> 'content'
    should be a string\n"}`

Signed-off-by: Dalai Felinto <dalai@blender.org>
Co-authored-by: Dalai Felinto <dalai@blender.org>
2025-12-03 13:01:36 +01:00
Marina López 641df77834 🐛 Fix wrong board size presets in Android (#7888) 2025-12-03 12:52:47 +01:00
Marina López 49bbdfb257 🐛 Fix U and E icon displayed in project list (#7875)
* 🐛 Fix U and E icon displayed in project lis

* 🐛 Fix U and E icon displayed in project list
2025-12-03 12:50:51 +01:00
Aitor Moreno 4e84deca44 Merge pull request #7879 from penpot/elenatorro-12797-fix-update-spans
🐛 Fix paragraph with text spans with multiple styles
2025-12-03 11:30:17 +01:00
Aitor Moreno 0d21e52068 🐛 Fix applyStylesTo entire selection 2025-12-03 11:07:33 +01:00
alonso.torres 1b29e9a50f 🐛 Fix race condition with fix fonts patch 2025-12-03 10:39:05 +01:00
Andrey Antukh 94af978be8 🐛 Fix casing on a translation of export files modal option 2025-12-03 10:22:45 +01:00
Andrey Antukh feababe2a8 🐛 Make workspace palette reposition on left sidebar collapse 2025-12-03 09:56:14 +01:00
Andrey Antukh 5ef06685fc 💄 Add cosmetic improvements to workspace palette component 2025-12-03 09:38:23 +01:00
Elena Torro 9f567c3bf4 🐛 Fix italic variant 2025-12-03 08:59:25 +01:00
Elena Torro 1ba15e5d10 🐛 Do not merge fill styles 2025-12-03 08:55:11 +01:00
Andrey Antukh 57fcec5afc 🐛 Make from-synthetic-clipboard-event function return always a stream
Causes an execption on steam processing when it returns nil
2025-12-03 08:32:38 +01:00
Andrey Antukh 58f82da61e 🐛 Fix exception on paste text on comments input 2025-12-03 08:20:58 +01:00
Andrey Antukh a28c5b61ca 💄 Adapt viewport paste code codestyle
And remove some not necessary constructions
2025-12-03 08:09:13 +01:00
Yaron Shahrabani 60df56caa3 🌐 Add translations for: Hebrew
Currently translated at 99.6% (1988 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-02 21:00:32 +00:00
Andrey Antukh 53aad7bc15 Merge remote-tracking branch 'origin/staging' into develop 2025-12-02 17:43:34 +01:00
Andrey Antukh 9123d199b7 🐛 Fix scripts/fmt 2025-12-02 17:43:21 +01:00
alonso.torres 37e45a8bbf 🐛 Fix race condition with text and type 2025-12-02 17:28:20 +01:00
alonso.torres 3471d40f46 🐛 Fix problem with boolean shapes updates 2025-12-02 17:28:20 +01:00
Elena Torro c6b64a8e39 🐛 Fix selectAll on mixed span styles 2025-12-02 16:50:48 +01:00
Elena Torro 511e80c948 🐛 Fix merge fill styles when there are multiple fills 2025-12-02 16:50:04 +01:00
Elena Torró f5a640d104 Merge pull request #7876 from penpot/ladybenko-12805-slow-loading
🐛 Fix viewport not being fully drawn on first load until a mouse …
2025-12-02 15:31:43 +01:00
Belén Albeza 3ae7c514e4 🐛 Fix viewport not being fully drawn on first load until a mouse hover 2025-12-02 15:06:28 +01:00
Andrey Antukh 57297741f5 Merge remote-tracking branch 'origin/staging' into develop 2025-12-02 13:28:50 +01:00
Andrey Antukh eeaf28bb25 📎 Disable caddy logging 2025-12-02 13:27:09 +01:00
Dalai Felinto d63d692d34 🐛 Fix mask issues with component swap #7675
The logic to swap a component would delete the swapped out component
first before bringing in the new one.

In the process of doing so, the sanitization code would unmask the
group, now orphan of its mask shape component, when it was the first
element of the group.

The fix  was to pass an optional argument to the generate-delete-shapes
function to ignore mask in special cases like this.

Signed-off-by: Dalai Felinto <dalai@blender.org>
2025-12-02 12:31:44 +01:00
alonso.torres fad9ed1c48 🐛 Fix problem with reordering layers 2025-12-02 12:27:00 +01:00
alonso.torres 0caaefefea 🐛 Fix outline with single click text creation 2025-12-02 11:08:58 +01:00
Elena Torro b179aa79b1 🐛 Fix create empty text on click regression 2025-12-02 11:08:58 +01:00
Andrey Antukh 6b8091bb90 Make devenv https and http2 capable (#7871)
Making it more similar on how it runs on production
environments and improves large amount of files loading
thanks to http2.
2025-12-02 10:49:37 +01:00
Andrey Antukh fe72d0af82 Add self-signed cert to caddy (#7872) 2025-12-02 10:45:26 +01:00
Aitor Moreno 405ddb60d8 🐛 Fix letter spacing applied to paragraph 2025-12-02 10:45:19 +01:00
Luis de Dios ef68081d1d 🎉 Add prototype tab UI tweaks (#7832)
* 🎉 Add prototype tab UI tweaks

* 📎 PR changes
2025-12-02 10:44:16 +01:00
Madalena MeloandAndres Gonzalez bba02473d5 📚 Update subtitles in the new user guide cards (#7823)
Co-authored-by: Andres Gonzalez <andres.gonzalez79@gmail.com>
2025-12-02 09:21:05 +01:00
Andrey Antukh 4ed49cdc5d Make devenv https and http2 capable (#7871)
Making it more similar on how it runs on production
environments and improves large amount of files loading
thanks to http2.
2025-12-01 20:43:23 +01:00
Elena Torró 95c0d42d5b Merge pull request #7868 from penpot/alotor-fix-flex-tools
🐛 Fix visual feedback on padding/margin/gaps modified
2025-12-01 17:51:44 +01:00
alonso.torres 721b337511 🐛 Fix visual feedback on padding/margin/gaps modified 2025-12-01 16:31:15 +01:00
Elena Torró 359379be09 Merge pull request #7867 from penpot/azazeln28-add-text-editor-v2-tests-to-staging
 Add text editor v2 integration tests
2025-12-01 16:11:25 +01:00
Aitor Moreno 876d5783cf Add text editor v2 integration tests 2025-12-01 15:56:52 +01:00
Elena Torro 786f73767b 🔧 Normalize font attributes to support old formats 2025-12-01 14:59:24 +01:00
Andrey Antukh 50f9eedcdf Merge remote-tracking branch 'origin/staging' into develop 2025-12-01 14:33:38 +01:00
Andrey Antukh 77c9d8a2c8 🐛 Revert exporter dockerfile changes 2025-12-01 14:32:00 +01:00
Andrey Antukh 95b7784a42 🐛 Change internal ordering on how email parts are assembled
This fixes the html email rendering on gmail. Other clients (like proton,
emailcatcher) properly renders html independently of the order of parts
on the multipart email structure but gmail requires that html should be
the last one.
2025-12-01 14:27:21 +01:00
Andrey Antukh 4690f740b9 Add minor improvements to the audit module 2025-12-01 13:57:55 +01:00
Xaviju 529c4eb38a 💄 Avoid code tab overflow (#7854) 2025-12-01 11:37:37 +01:00
Andrey Antukh c3a9919c4d 🐛 Fix typo on exporter dockerfile 2025-12-01 11:19:41 +01:00
Eva Marco efe74e62e8 🎉 Replace font family form (#7825) 2025-12-01 11:17:25 +01:00
Juanfran 10a2732a55 Merge pull request #7863 from penpot/niwinz-staging-improve-yarn-independency
 Use setup script on exporter instead of direct commands
2025-12-01 10:13:58 +01:00
Eva Marco 456afe46de 🎉 Replace font family form (#7784) 2025-12-01 10:11:29 +01:00
Andrey Antukh 4282cdcd2c Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-01 10:11:06 +01:00
Andrey Antukh 964ef799c2 🔥 Remove core.spec usage on common and frontend 2025-12-01 09:30:21 +01:00
Andrey Antukh d34b6b88b6 Remove malli dev stuff from cljs build
It only used on backend.
2025-12-01 09:30:21 +01:00
Andrey Antukh 9a58f0e954 🔧 Disable code motion on shadow config 2025-12-01 09:30:21 +01:00
Andrey Antukh adaf8be56d Use sm/coercer on app.render entry point 2025-12-01 09:30:21 +01:00
Andrey Antukh 2f1b99fa53 ♻️ Use ESM target for build frontend 2025-12-01 09:30:21 +01:00
Andrey Antukh 5080fcc594 🔥 Remove unused require of edn reader on loggin ns 2025-12-01 09:30:21 +01:00
Andrey Antukh ea2d3758f0 Merge remote-tracking branch 'origin/staging' into develop 2025-12-01 09:28:49 +01:00
Andrey Antukh 40e3617138 Use setup script on exporter instead of direct commands 2025-12-01 09:23:11 +01:00
Alejandro Alonso e889413f26 🐛 Fix nested shadows clipping 2025-12-01 09:22:23 +01:00
Andrey Antukh b18c421415 📎 Update .gitignore 2025-12-01 09:20:33 +01:00
Andrey Antukh e7029f2182 Make automatic workflows not dependent on yarn 2025-12-01 08:17:52 +01:00
Elena Torró 115273b478 Merge pull request #7852 from penpot/alotor-flex-issues
🐛 Fix flex problems in new render
2025-11-28 14:10:42 +01:00
Elena Torró fdddd3284a Merge pull request #7859 from penpot/ladybenko-12801-fix-mismatched-fonts
🐛 Fix mismatch between fonts for rendered and selected text when no fallback fonts apply
2025-11-28 14:10:17 +01:00
Belén Albeza 51385a04a0 🐛 Fix mismatch between fonts for rendered and selected text when no fallback fonts apply 2025-11-28 13:54:17 +01:00
Alonso Torres 2c3becb408 🐛 Fix problem with plugins content attribute (#7835) 2025-11-28 13:41:27 +01:00
Belén Albeza f96ed8ccd6 Fix playwright tests 2025-11-28 13:25:13 +01:00
Belén Albeza bda5de5c1b 🔧 Update google fonts list 2025-11-28 13:25:13 +01:00
Juanfran 94c15916e2 Merge pull request #7857 from penpot/niwinz-develop-prepare-for-pnpm
 Make automatic workflows not dependent on yarn
2025-11-28 13:07:30 +01:00
Andrey Antukh ed0f3c3595 Make automatic workflows not dependent on yarn 2025-11-28 12:26:56 +01:00
alonso.torres 59f3b4db4c 🐛 Fix problem with auto-size and element margins 2025-11-28 12:12:19 +01:00
alonso.torres 7ee03ad911 🐛 Fix problem with grid layout editor 2025-11-28 12:12:09 +01:00
alonso.torres 130b8c8214 🐛 Fix problems with flex layout in new render 2025-11-28 10:49:55 +01:00
alonso.torres 0198d41757 🐛 Fix crash when cleanup 2025-11-28 10:44:54 +01:00
alonso.torres 567a955151 🐛 Fix problem with change gap/margin/padding 2025-11-28 10:44:38 +01:00
Xaviju a4e6aa0588 💄 Limit inspect layer info message to avoid overflow (#7847) 2025-11-28 10:19:02 +01:00
Nicola Bortoletto 34da754357 🌐 Add translations for: Italian
Currently translated at 98.9% (1973 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-11-28 05:00:28 +00:00
alonso.torres c2014a37b4 🐛 Fix problem when pasting elements in reverse flex layout 2025-11-27 18:02:34 +01:00
alonso.torres 6611fbd13b 🐛 Fix problem when drag+duplicate a full grid 2025-11-27 18:02:34 +01:00
Andrey Antukh b5a6867058 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 18:01:08 +01:00
Andrey Antukh 7fe20b65dc 🔧 Add more cache efficient configuration for devenv nginx 2025-11-27 17:59:12 +01:00
Andrey Antukh e5638cd769 ⬆️ Update clojure tools version on devenv 2025-11-27 17:58:56 +01:00
Eva Marco 8e79dfcb82 🐛 Fix input variant 2025-11-27 17:54:11 +01:00
Eva Marco 508db99a57 🐛 Restore empty field error on dimension, text-case and color forms 2025-11-27 17:54:11 +01:00
Andrey Antukh 3c6c9894da 🐛 Restore empty value error on border radius token form 2025-11-27 17:54:11 +01:00
Andrey Antukh 972b23e6c0 🐛 Fix incorect pred build on ::sm/text schema 2025-11-27 17:54:11 +01:00
Andrey Antukh 28f550d533 🔥 Remove commented code 2025-11-27 17:54:11 +01:00
Elena Torró 2b20f75fd4 Merge pull request #7837 from penpot/ladybenko-12719-fix-editor-unicode-fonts
🐛 Fix editor not using fallback fonts for selected text
2025-11-27 17:37:00 +01:00
Belén Albeza 4d6d7a6a3d 🐛 Fix emoji font not being used as fallback in text editor dom 2025-11-27 17:23:20 +01:00
Andrey Antukh 0f88253dd5 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 16:11:36 +01:00
Andrey Antukh db1ab7be69 📎 Run worker bundling serially on devenv 2025-11-27 16:09:15 +01:00
Andrey Antukh fcbe9d92dc 🐛 Fix unexpected exception on rendering feedback email
Looks like a bug on selmer library
2025-11-27 16:09:15 +01:00
Andrey Antukh 9998ce0bb4 🔥 Remove fipps direct dependency 2025-11-27 16:09:15 +01:00
Andrey Antukh 6061391c89 Don't require cljs.analyzer api under cljs on data.macros
Reduces the final production bundle size
2025-11-27 16:09:15 +01:00
Andrey Antukh eabf6e36ed Remove a level of indentation on subscriptions-dashboard tests 2025-11-27 16:09:15 +01:00
Andrey Antukh 04274e53fa 📎 Fix advanced compilation warnings related to jsdoc 2025-11-27 16:09:15 +01:00
Andrey Antukh 52dd9271a9 🐛 Encode header values as strings on audit archive task 2025-11-27 16:09:15 +01:00
andrés gonzález 8f5a81e179 📚 Add info about boolean variants (#7828) 2025-11-27 16:03:11 +01:00
Alonso Torres a940c08da9 🐛 Fix problem with worker bundling in development (#7844) 2025-11-27 14:13:48 +01:00
Alejandro Alonso 3de4473251 Merge pull request #7845 from penpot/elenatorro-fix-case
🐛 Fix editor vertical align default case
2025-11-27 14:00:12 +01:00
Andrey Antukh 0735140f07 🔧 Change concurrency rules on tests github workflow 2025-11-27 13:46:48 +01:00
Elena Torro dc8a07099d 🐛 Fix vertical align default case 2025-11-27 13:38:51 +01:00
Andrey Antukh 8e3996fbb0 🔧 Change concirrency rules on tests github workflow 2025-11-27 13:16:08 +01:00
Alonso Torres 67762d9450 🐛 Fix problem with worker bundling in development (#7844) 2025-11-27 13:02:47 +01:00
Elena Torró 90dcf04fb0 Merge pull request #7841 from penpot/superalex-fix-boolean-operators-no-selection
🐛 Fix boolean operators no selection
2025-11-27 12:50:16 +01:00
Belén Albeza f84c236e02 🐛 Fix text editor v2 not using fallback fonts for selected text 2025-11-27 12:26:39 +01:00
Alejandro Alonso 63959a22cc 🐛 Fix svg attrs 2025-11-27 12:23:46 +01:00
Alejandro Alonso 8840246425 🐛 Fix bleeding masks 2025-11-27 12:23:46 +01:00
Alejandro Alonso 62ec66cd15 🔧 Adding more e2e tests for nested frames with clipping 2025-11-27 12:23:46 +01:00
Alejandro Alonso e3b87390f6 🐛 Fix nested shadows clipping 2025-11-27 12:23:46 +01:00
Alejandro Alonso d9ab28e6ed 🐛 Fix nested clipping 2025-11-27 12:23:46 +01:00
Belén Albeza 9183dbbc43 🔧 Fix lint error (rust) 2025-11-27 11:51:05 +01:00
Andrey Antukh 74d00473e9 Add missing render-wasm to the ci workflow 2025-11-27 11:51:05 +01:00
Alejandro Alonso 1c70f5a36b 🐛 Fix boolean operatos shown when there is no selection 2025-11-27 11:22:15 +01:00
Andrey Antukh b23e0c0642 Add tempfile storage bucket handler test case (#7839) 2025-11-27 10:27:57 +01:00
Andrey Antukh 7f62652870 Merge remote-tracking branch 'origin/staging' into develop 2025-11-27 09:24:40 +01:00
Marina López db0cbbbc2e 🐛 Fix logic preventing incorrect trial flow in subscription modal (#7831) 2025-11-26 12:08:02 +01:00
alonso.torres 48304bd26f 🐛 Fix issue when exporting files 2025-11-26 12:04:34 +01:00
Elena Torro 60e32bbc71 🐛 Fix text editor vertical align 2025-11-26 11:46:47 +01:00
André Carvalhais 54451608dc 💄 Fix spelling of 'smtp' in email configuration section
Corrected the spelling of 'smtp' in the documentation.

Signed-off-by: André Carvalhais <carvalhais@live.com>
2025-11-26 08:11:27 +01:00
Andrey Antukh 78d31ab11a 🐳 Update devenv docker and compose files
Reuse the already builded imagemagick instead of building
it again on the devenv.
2025-11-26 07:44:56 +01:00
Andrey Antukh 0a80c47901 Merge remote-tracking branch 'origin/staging' into develop 2025-11-26 07:30:42 +01:00
Alejandro Alonso b7727122d5 Merge pull request #7829 from penpot/alotor-fixes
🐛 Fix problem with thumbnails in parallel
2025-11-26 07:21:49 +01:00
alonso.torres 8880f07a6a 🐛 Fix problem with thumbnails in parallel 2025-11-25 17:56:00 +01:00
Oğuz Ersen 39eafae251 🌐 Add translations for: Turkish
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-11-25 16:51:25 +00:00
Edgars Andersons e1e09b7f96 🌐 Add translations for: Latvian
Currently translated at 94.0% (1876 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-25 16:51:24 +00:00
Stephan Paternotte 3b39980f2f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-25 16:51:23 +00:00
Anton Palmqvist 223b12d2c7 🌐 Add translations for: Swedish
Currently translated at 99.6% (1987 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-25 16:51:21 +00:00
andrés gonzález aaca2c41d8 📚 Add metadescriptions to some help center pages (#7821) 2025-11-25 17:00:14 +01:00
Yamila Moreno 77f1046fc8 🔧 Add MT notification when a docker image with final tag is built (#7824) 2025-11-25 16:39:42 +01:00
Belén Albeza 33417a4b20 🐛 Fix svg attrs stroke-linecap stroke-linejoin fill-rule 2025-11-25 12:43:40 +01:00
Andrés Moya 2640889dc8 🐛 Fix backwards compatibility importing files with token themes 2025-11-25 10:56:33 +01:00
alonso.torres dd5f3396d1 🐛 Fix problem with layout z-index 2025-11-24 17:48:58 +01:00
Andrey Antukh dedeae8641 🐛 Fix incorrect subscription fetching after profile registration 2025-11-24 14:36:46 +01:00
Andrey Antukh a7552d412a Add explicit network asingation and alias on devenv compose 2025-11-24 14:36:46 +01:00
Aitor Moreno f58475a7c9 🐛 Fix pasting application/transit+json (#7812) 2025-11-24 14:36:24 +01:00
Marina López 00bbb0bfb6 ♻️ Add format and refactor payments 2025-11-24 11:41:03 +01:00
Andrey Antukh d93fe89c12 📎 Backport CI github workflog from develop 2025-11-24 10:48:51 +01:00
Andrey Antukh 553b73a83c ♻️ Replace CircleCI with Github Actions (#7789)
* ♻️ Replace circleci with github actions

* 📎 Add integration test sharding

* 📎 Reuse single build for integration tests shards
2025-11-24 10:44:04 +01:00
Andrey Antukh 00a45cb274 📎 Bump new version on changelog 2025-11-24 09:47:00 +01:00
Andrey Antukh 6e44330af4 Merge remote-tracking branch 'origin/develop' into staging 2025-11-24 09:42:45 +01:00
Andrey Antukh 624805fd6b Merge remote-tracking branch 'weblate/develop' into develop 2025-11-24 09:32:06 +01:00
Eva MarcoandXavier Julian 9b6bb77422 Materialize several tokens related flags (#7773)
* 📚 Add line to changelog

* ♻️ Remove typography types flag

* ♻️ Remove composite typography token flag

* ♻️ Remove token units flag

* 🎉 Activate by default two token flags

* ♻️ Update inspect tab tests to navigate to the right info tab

* 🐛 Fix test

---------

Co-authored-by: Xavier Julian <xavier.julian@kaleidos.net>
2025-11-24 09:26:05 +01:00
Yamila Moreno 9b8e04bb3c 🐳 Remove minio service from docker-compose.yml (#7809) 2025-11-24 08:15:36 +01:00
Edgars Andersons 2e919809c9 🌐 Add translations for: Latvian
Currently translated at 94.1% (1873 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-23 12:51:20 +00:00
Nicola Bortoletto 645e123e3a 🌐 Add translations for: Italian
Currently translated at 98.8% (1967 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-11-23 12:51:17 +00:00
Oğuz Ersen cfb94d17b6 🌐 Add translations for: Turkish
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-11-22 10:51:22 +00:00
Keunes e9cb409ca4 🌐 Add translations for: Dutch
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-22 10:51:19 +00:00
jonnysemon 8a0cd75257 🌐 Add translations for: Arabic
Currently translated at 56.6% (1128 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-22 10:51:17 +00:00
Pablo Alba fae488b15a 🐛 Fix after changing a variant property value, the value appears as empty (#7791) 2025-11-21 17:51:12 +01:00
Elena Torró b82828632e Merge pull request #7807 from penpot/alotor-fix-hover-text
🐛 Fix hover text
2025-11-21 15:35:41 +01:00
alonso.torres bf24e22588 🐛 Fix hover text 2025-11-21 14:27:15 +01:00
Alejandro Alonso 7399b4d423 📚 Remove wrong line on CHANGES 2025-11-21 14:21:14 +01:00
Alejandro Alonso 77b9eee6bd 🐛 Fix svg fills defined in svg-attrs with url or color format 2025-11-21 14:15:27 +01:00
Elena Torro 55896db49e 🔧 Check for emtpy/nil attrs when getting inline style 2025-11-21 14:10:23 +01:00
Elena Torró f4c569d619 Merge pull request #7802 from penpot/alotor-fix-text-data-problem
🐛 Fix problems with text editor size
2025-11-21 13:41:38 +01:00
alonso.torres ca2cf18a49 🐛 Fix problems with text editor size 2025-11-21 13:17:43 +01:00
Andrey Antukh 6e352c167c 🐛 Fix dev build of frontend 2025-11-21 13:02:44 +01:00
Andrey Antukh 3ec001de44 🔧 Add nitrate url to devenv nginx (#7800) 2025-11-21 12:30:49 +01:00
Elena Torró a1f11c89f2 Merge pull request #7799 from penpot/alotor-fix-text-data-problem
🐛 Fix problem with text data serialization
2025-11-21 12:30:35 +01:00
alonso.torres 33d70f0e45 🐛 Fix problem with text data serialization 2025-11-21 12:07:01 +01:00
Elena Torró 4f24a8f5f1 Merge pull request #7770 from penpot/ladybenko-12587-fix-text-editor-crash-empty
🐛 Fix crash when using a font family with a number in its name
2025-11-21 12:02:40 +01:00
Andrey Antukh b03cfffb9e Restore the dashboard thumbnail rendering using wasm (#7796)
* Revert "🐛 Rollback esm worker (#7792)"

This reverts commit 0120a5335b.

* 🐛 Fix incorrect manifest reading on building worker
2025-11-21 11:42:40 +01:00
Elena Torró 956ad88e51 Merge pull request #7795 from penpot/alotor-fix-paste-crash
🐛 Fix paste crash
2025-11-21 11:00:00 +01:00
Belén Albeza 76f5c73de6 Remove leftover console.log/trace 2025-11-21 10:59:15 +01:00
Belén Albeza c6dd3e0eeb Add missing param to cut handler 2025-11-21 10:28:48 +01:00
alonso.torres fde73f30b9 🐛 Fix paste crash 2025-11-21 09:51:54 +01:00
Edgars Andersons 9d35a4317c 🌐 Add translations for: Latvian
Currently translated at 93.6% (1864 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-21 05:51:27 +00:00
jonnysemon e7ccfeccbf 🌐 Add translations for: Arabic
Currently translated at 56.6% (1128 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-21 05:51:26 +00:00
Stephan Paternotte aa043d284f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1987 of 1990 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-21 05:51:21 +00:00
Alejandro Alonso 537dd171c0 Merge pull request #7793 from penpot/alotor-tiles-improvement
 Improve cache rendering
2025-11-20 18:50:24 +01:00
alonso.torres c2026918a4 Improve cache rendering 2025-11-20 17:33:37 +01:00
Alonso Torres 0120a5335b 🐛 Rollback esm worker (#7792) 2025-11-20 16:07:22 +01:00
Belén Albeza d0d2f43ca1 🐛 Fix text editor crash with font families with a number in their name 2025-11-20 15:22:40 +01:00
Alejandro Alonso 7e33a7c1a7 Merge pull request #7666 from penpot/azazeln28-feat-allow-disabling-rich-paste
🎉 Add an option to enable and disable HTML paste
2025-11-20 14:17:16 +01:00
Elena Torró c13b58f42a Merge pull request #7764 from penpot/superalex-fix-blurs
🐛 Fix shadows and blurs
2025-11-20 13:37:57 +01:00
alonso.torres a5c9f9e454 📚 Adds contributor to the changelog 2025-11-20 13:35:43 +01:00
Aitor Moreno d73be5832b 🎉 Add an option to enable and disable HTML paste 2025-11-20 13:33:51 +01:00
Alejandro Alonso e1f2fca4af Merge pull request #7771 from penpot/elenatorro-12541-improve-text-selection-and-cursor
 Improve text shape selection
2025-11-20 13:33:48 +01:00
Diana Veiga 37d5a31589 Drop zoom snap (#7774)
*  Remove const `zoom-half-pixel-precision`

* ♻️ Adjust usages
2025-11-20 13:28:45 +01:00
Luis de Dios 177bdaa72c 🐛 Fix variant toggle does not work for uppercase or mixed case (#7716)
* 🐛 Fix variant toggle does not work for uppercase or mixed case

* 📎 PR changes
2025-11-20 13:27:04 +01:00
Aitor Moreno 38ab2c61b9 Merge pull request #7782 from penpot/alotor-wasm-thumbnails
 Render WASM dashboard thumbnails
2025-11-20 13:12:26 +01:00
Marina López cc32b22e8a Add improvements to the payment flow (#7776)
*  Add improvements payment flow

* 📎 PR feedback

* 📎 Fix conflicts
2025-11-20 13:07:57 +01:00
Alejandro Alonso d331c5ad83 Merge pull request #7769 from penpot/niwinz-develop-exporter-refactor
 Remove exporter dependency on shared-fs on scaling
2025-11-20 12:44:34 +01:00
iPagar 6c6c2c3012 📚 Update copyright year on doc (#7502)
Signed-off-by: iPagar <iPagar@users.noreply.github.com>
2025-11-20 12:38:31 +01:00
Andrey Antukh 81632a03dd ♻️ Make exporter upload resources using backend management api
Instead of custon shared fs approach. This commit fixes the main
scalability issue of exporter removing the need of shared-fs
for make it work with multiple instances.
2025-11-20 12:20:13 +01:00
Andrey Antukh 4fddf3d986 ♻️ Make management key derivable from secret key
Still preserves the ability to set management
2025-11-20 12:20:13 +01:00
Andrey Antukh 57aa9a585b 🔧 Add explicit network alias for minio on devenv 2025-11-20 12:20:13 +01:00
Andrey Antukh f71f491590 🐛 Fix incorrect bearer token decoding 2025-11-20 12:20:13 +01:00
Andrey Antukh 6ae2401c5e ♻️ Change how shapes are validated after changes apply operation 2025-11-20 12:08:48 +01:00
Andrey Antukh 53d8a2d6d7 🔥 Remove obsolete code on :move-objects related to old components 2025-11-20 12:08:48 +01:00
Andrey Antukh bd65f3932e 🐛 Fix a race condition on move-object
That happens when an in-flight move-object change tries
to move object to an already deleted parent
2025-11-20 12:08:48 +01:00
alonso.torres 59845b756f Render WASM dashboard thumbnails 2025-11-20 11:56:25 +01:00
Alejandro Alonso b8c0c5c310 Merge pull request #7742 from penpot/alotor-plugins-improvements
 Plugin API improvements with images and indexes
2025-11-20 11:47:50 +01:00
Alejandro Alonso cfa8c21ee6 Merge pull request #7788 from penpot/elenatorro-fix-insert-shape-on-empty-frame
🐛 Fix insert shape on empty frame
2025-11-20 11:44:13 +01:00
Elena Torro 624bdaec88 Show text cursor in the entire text rect 2025-11-20 11:42:07 +01:00
Alejandro Alonso 24745bed40 🐛 Fix shadows and blurs for high levels of zoom 2025-11-20 11:25:23 +01:00
Eva Marco d26c08f8e2 ♻️ Replace token forms (#7759)
* 🎉 Create dimensions form

* 🎉 Create text-case form

* 🎉 Create color form

* ♻️ Remove unused code on form file
2025-11-20 11:04:39 +01:00
Elena Torro 36adbd9118 🐛 Fix insert shape on empty frame 2025-11-20 10:59:44 +01:00
Elena Torró 0a3fe9836a Merge pull request #7777 from penpot/superalex-fix-extrect-calculation
🐛 Fix extrect calculation
2025-11-20 09:57:59 +01:00
Andrey Antukh fef0c11503 🔧 Update tests github flow 2025-11-20 09:37:38 +01:00
Alejandro Alonso 7e858784a1 Merge pull request #7785 from penpot/niwinz-develop-binary-fills
🐛 Fix invalid fills schema when binary fills are used
2025-11-20 09:06:45 +01:00
Miguel de Benito Delgado 203368c2ee Add parameter to openPage to toggle new window behaviour (#7753)
*  Add parameter to openPage() to toggle opening a new tab/window

* 💄 Fix formatting
2025-11-20 08:05:08 +01:00
Alejandro Alonso 4f54469629 Merge pull request #7747 from penpot/niwinz-develop-storage-changes
 Make the binfile exportation process more reliable
2025-11-20 07:58:57 +01:00
Andrey Antukh 5343e799f8 🐛 Fix invalid fills schema when binary fills are used 2025-11-20 07:45:37 +01:00
Andrey Antukh 51e54a6bad 🐛 Fix incorrect project restoration on restoring file (#7778) 2025-11-19 18:24:24 +01:00
Aitor Moreno f609747322 🐛 Fix inert element error 2025-11-19 18:23:44 +01:00
Andrey Antukh 26ad039d99 ⬆️ Update playwright dependency on frontend 2025-11-19 18:23:44 +01:00
Andrey Antukh 3136096123 🔧 Add general improvements to integration tests
This commit marks as skip (temporal) several flaky/randomly-failing
tests.

It also moves the integration test execution from circleci to github
actions.
2025-11-19 18:23:44 +01:00
Andrey Antukh 122d3bc41c 💄 Add code formatting for js on frontend 2025-11-19 18:23:44 +01:00
Andrey Antukh 3b52051113 Fix closure compiler issues on clipboard js impl
With minor naming fixes
2025-11-19 18:23:44 +01:00
Aitor Moreno 32e1b55658 ♻️ Refactor clipboard 2025-11-19 18:23:44 +01:00
Andrey Antukh e9d177eae3 Make the binfile export process more resilent to errors
The current binfile export process uses a streaming technique. The
major problem with the streaming approach is the case when an error
happens on the middle of generation, because we have no way to
notify the user about the error (because the response is already
is sent and contents are streaming directly to the user
client/browser).

This commit replaces the streaming with temporal files and SSE
encoded response for emit the export progress events; once the
exportation is finished, a temporal uri to the exported artifact
is emited to the user via "end" event and the frontend code
will automatically trigger the download.

Using the SSE approach removes possible transport timeouts on export
large files by sending progress data over the open connection.

This commit also removes obsolete code related to old binfile
formats.
2025-11-19 17:28:55 +01:00
Andrey Antukh d42c65b9ca Improve logging on shape detach operation 2025-11-19 17:28:55 +01:00
Andrey Antukh 86ad56797b Simplify tempfile deletion handling
Mainly removes the jvm on-exit hook usage because it can lead
to slow stops and unnecesary memory consumption over the time
the jvm is running.
2025-11-19 17:28:55 +01:00
Andrey Antukh 63497b8930 Add tempfile bucket to the storage subsystem
This enables storing temporal files under storage subsystem. The
temporal objects (the objects that uses templfile bucket) will
always evaluate to "for deletion" after touched garbage collection;
and the deletion threshold will be 2 hours (the threshold is always
calculated from the instant when the touched garbage collector is
running).
2025-11-19 17:28:55 +01:00
Andrey Antukh 94719eebf8 ♻️ Make storage and other objects deletion task vclock aware
This simplifes the mental model on how it works and simplifies testing
of the related code.

This also normalizes storage object deletion in the same way as the
rest of objects in penpot (now future deletion date on storage object
also means storage object to be deleted).
2025-11-19 17:28:55 +01:00
Andrey Antukh 9532dea2c6 📎 Skip inspect integration tests (#7781) 2025-11-19 17:26:40 +01:00
Andrey Antukh 40e1e27bf0 🐛 Fix not covered case on schema decode fn on tokens-lib 2025-11-19 15:04:49 +01:00
Andrés Moya 4338f97e9f 🐛 Allow deleting the library in the undo change of add tokens-lib 2025-11-19 15:04:49 +01:00
Andrey Antukh 2c4ec43d5f 🐛 Fix invalid syntax on translation files 2025-11-19 15:03:26 +01:00
Andrey Antukh 3d782a322d 🐛 Fix issue related to labels.code on translations 2025-11-19 14:53:13 +01:00
Andrey Antukh 407d28d187 🌐 Rehash and sync translation files 2025-11-19 14:18:41 +01:00
Andrey Antukh bf582ec55f 🌐 Add several fixes on weblate merge 2025-11-19 13:25:11 +01:00
Andrey Antukh 858bc05ed5 Merge remote-tracking branch 'weblate/develop' into develop 2025-11-19 13:11:07 +01:00
Andrey Antukh cd01386210 📎 Set version 1.1.0 final to sdk/library 2025-11-19 13:04:29 +01:00
Xaviju 3b2bb5f225 ♻️ Follow translations guidelines on several inspect components (#7766)
Signed-off-by: Xaviju <xavier.julian@kaleidos.net>
2025-11-19 13:03:25 +01:00
Alejandro Alonso fe3bc96d0d Merge pull request #7772 from penpot/niwinz-develop-auth-bugfix
 Improvements to the auth internal flows changes
2025-11-19 12:46:10 +01:00
Alejandro Alonso 28f23f397e 🐛 Fix extrect calculation 2025-11-19 12:38:01 +01:00
Andrey Antukh a487dfe004 Add better approach for cookie token decoding
Remove unnecesary decoding for old tokens and add key identifier
and versioning to cookie tokens for handle future changes.
2025-11-19 07:47:52 +01:00
Andrey Antukh 4f29156929 📎 Add better formatting of public-uri on db report 2025-11-18 20:35:26 +01:00
Andrey Antukh ce2d3d1652 🐛 Fix incorrect handling of session renewal
A regression introduced in the prev auth refactor.
2025-11-18 20:35:16 +01:00
Andrey Antukh 3639ff9dbc 🔧 Update devenv logging configuration 2025-11-18 20:34:17 +01:00
Andrey Antukh ca5ec734a0 Merge remote-tracking branch 'origin/staging' into develop 2025-11-18 18:19:36 +01:00
Andrey Antukh b08da4c3ff Merge remote-tracking branch 'origin/main' into staging 2025-11-18 18:19:11 +01:00
Yamila Moreno c9bec3924d 🐳 Use the secret key both in the backend and the exporter (#7746) 2025-11-18 18:18:49 +01:00
Yamila Moreno 6e725a75e1 🐳 Use the secret key both in the backend and the exporter (#7746) 2025-11-18 18:17:34 +01:00
Anton Palmqvist 81c3b84972 🌐 Add translations for: Swedish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-18 14:52:28 +01:00
jonnysemon 5868f7f6b2 🌐 Add translations for: Arabic
Currently translated at 57.7% (1130 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-11-18 14:52:25 +01:00
Tiago José 653567d7de 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 71.6% (1402 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-11-18 14:51:51 +01:00
Alejandro Alonso ce651fa0a9 Merge pull request #7767 from penpot/alotor-fix-problem-compatibility
🐛 Fix problem with tainted canvas in thumbnails
2025-11-18 14:15:06 +01:00
alonso.torres e8a26ef83b 🐛 Fix problem with tainted canvas in thumbnails 2025-11-18 13:05:56 +01:00
alonso.torres 8fd17c9c84 🐛 Fix problem not checking feature flag 2025-11-18 13:05:29 +01:00
Xaviju 64b892f82d ♻️ Copy shorthands using user selected color space (#7752)
* ♻️ Copy shorthands using user selected color space

* ♻️ Add tests to ensure color space changes affect all properties
2025-11-18 10:54:10 +01:00
Alejandro Alonso 04185b3544 Merge pull request #7762 from penpot/alotor-fix-selection
🐛 Fix problem with selection and text shapes for new render
2025-11-18 10:39:36 +01:00
alonso.torres 0a01fc8af9 🐛 Fix problem with selection and text shapes for new render 2025-11-18 09:34:17 +01:00
Alejandro Alonso ae624b3728 Merge pull request #7760 from penpot/elenatorro-12533-fix-selection-and-paste-and-word-deletion
🐛 Fix text editor select all functionality and inner paste corner cases
2025-11-18 09:31:57 +01:00
Alejandro Alonso a48b719966 Merge pull request #7748 from penpot/elenatorro-12586-fix-offset-y-on-new-lines
🐛 Fix new lines spacing between paragraphs
2025-11-18 09:23:22 +01:00
Elena Torró 6425c0cb7d Merge pull request #7757 from penpot/superalex-fix-apply-shadow-and-blur-bounds
🐛 Fix apply shadow and blur bounds
2025-11-17 16:50:15 +01:00
Elena Torro 368f4cfe81 🐛 Fix text editor select all functionality and inner paste corner cases 2025-11-17 16:24:52 +01:00
Alejandro Alonso fdffa14d75 🐛 Fix apply shadow and blur bounds 2025-11-17 15:20:22 +01:00
Eva MarcoandAndrey Antukh 7fe965a870 🎉 Add new form system on workspace (#7738)
* 🎉 Add new form system on border-radius token modals

* ♻️ Create new namespace and separate components

* ♻️ Refactor submit button

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-11-17 13:44:56 +01:00
Anton Palmqvist d03f5c10fb 🌐 Add translations for: Swedish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-15 20:51:48 +00:00
Anton Palmqvist 3eb0f1c225 🌐 Add translations for: Swedish
Currently translated at 88.9% (1740 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-14 18:51:24 +01:00
Elena Torro 127fa931c7 🐛 Fix new lines spacing between paragraphs 2025-11-14 12:00:39 +01:00
Andrey Antukh 30413dbc66 Add small changes to the auth/login button label (#7754)
* 📎 Update changelog

*  Update login button label

* 📎 Adapt playwright tests
2025-11-14 11:35:10 +01:00
Andrey Antukh 2810ae681f ⬆️ Update yarn requirement on library module 2025-11-14 11:15:26 +01:00
Andrey Antukh d706bb7c8d 🐛 Fix validation issues with dtcg-node schema 2025-11-14 11:15:26 +01:00
Andrey Antukh ef271db879 🎉 Add addTokensLib method to the library 2025-11-14 11:15:26 +01:00
Andrey Antukh ec5e814a72 ⬆️ Update npm deps on library 2025-11-14 11:15:26 +01:00
Andrey Antukh c44fd2dd1d 💄 Use correct comments style on tokens-lib 2025-11-14 11:15:26 +01:00
Andrey Antukh 6aa797f51b Normalize token theme serialization to JSON 2025-11-14 11:15:26 +01:00
Andrés Moyaandalonso.torres 3cc54fd988 🎉 Add design tokens to plugins API (#7602)
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2025-11-14 11:14:56 +01:00
Xaviju 2233f34a15 🎉 Set default button behaviour as type button instead of submit (#7741) 2025-11-14 10:25:38 +01:00
Andrey Antukh 839bb470df Merge remote-tracking branch 'origin/staging' into develop 2025-11-14 09:55:14 +01:00
Eva Marco 450ce869ba 🐛 Fix gap on export section on sidebar 2025-11-14 09:08:33 +01:00
Xaviju 665587d492 ♻️ Review inspect tab UI (#7727)
* ♻️ Review inspect tab UI

* ♻️ Capitalize English strings and remove from styles

* ♻️ Set a minimum size por color space selector and adjust visually the UI

* 🐛 Fix error on hooks order when selecting texts

* 🐛 Set minim size to inspect tab element

* 🐛 Fix broken typography panel

* ♻️ Design review
2025-11-13 22:19:43 +01:00
Elena Torró 8aaa953604 Merge pull request #7730 from penpot/alotor-fixes-layouts
 Fix new render problems with layout
2025-11-13 16:38:20 +01:00
Marina López a2cb84ba0d Add improvements payment flow 2025-11-13 13:48:27 +01:00
alonso.torres 639952abc8 🐛 Fix problems with text positioning in layout 2025-11-13 12:31:26 +01:00
alonso.torres 2d63730bfa Improved performance in modifiers 2025-11-13 12:31:26 +01:00
alonso.torres c1638817b2 🐛 Fix problem with frame titles not moving 2025-11-13 12:31:26 +01:00
alonso.torres 76f6f71e02 🐛 Fix z-ordering for flex elements 2025-11-13 12:31:26 +01:00
alonso.torres 0a700864c9 🐛 Fix problem with grid layout modifiers 2025-11-13 12:31:26 +01:00
Yamila Moreno 04ce4c3233 🔧 Fix repository name in release.yml (#7731) 2025-11-13 11:42:33 +01:00
Andrey Antukh befcca86df 📚 Update changelog 2025-11-12 21:37:16 +01:00
Andrey Antukh b7bae3850b 🐛 Fix webp exportation on exporter docker image (#7739) 2025-11-12 21:31:19 +01:00
Elena Torró 3f05dae455 Merge pull request #7735 from penpot/superalex-fix-create-empty-text
🐛 Fix some text issues
2025-11-12 17:48:41 +01:00
alonso.torres 48c9fb5690 Add methods to plugins for modifying indices 2025-11-12 17:07:38 +01:00
alonso.torres 4cdf1eed0c 🐛 Add method to retrieve image data in plugins 2025-11-12 17:07:38 +01:00
Aitor Moreno 4a887840c6 Merge pull request #7737 from penpot/sueralex-fix-shadows-clipping
🐛 Fix shadows clipping
2025-11-12 16:58:06 +01:00
Elena Torró 10cf2c7f35 Merge pull request #7729 from penpot/ladybenko-12514-fix-font-variants
🐛 Fix downloading wrong font variant
2025-11-12 15:30:08 +01:00
Belén Albeza d048a251f1 🐛 Fix render of text baseline (wasm) 2025-11-12 14:59:57 +01:00
Belén Albeza 0b3fc6a663 🔧 Fix broken playwright tests (wasm render) 2025-11-12 14:48:31 +01:00
Andrey Antukh 363b4e3778 ♻️ Make the SSO code more modular (#7575)
* 📎 Disable by default social auth on devenv

* 🎉 Add the ability to import profile picture from SSO provider

* 📎 Add srepl helper for insert custom sso config

* 🎉 Add custom SSO auth flow
2025-11-12 12:49:10 +01:00
Andrey Antukh f248ab5644 🐛 Relax schema for importing plain path data related to curve-to command 2025-11-12 12:13:17 +01:00
Alejandro Alonso 33da6fbec2 🐛 Fix shadows clipping 2025-11-12 11:47:53 +01:00
Belén Albeza 07bede8ba2 🐛 Fix unicode ranges for codepoints that need surrogate pairs 2025-11-12 10:11:19 +01:00
Eva Marco 05bea14a88 🐛 Fix review selected colors (#7715)
* 🐛 Fix gap between token sets

* 🐛 Show token selected on color selecction modal
2025-11-12 10:04:29 +01:00
Alejandro Alonso 718f42aa94 🐛 Fix deselect and delete events for empty texts 2025-11-12 08:33:17 +01:00
Alejandro Alonso f2f8a488ad Merge pull request #7724 from penpot/elenatorro-12551-fix-blurs-and-shadows-bounding-box
🐛 Fix extrect calculation for shadows and blurs depending on the scale
2025-11-12 08:25:50 +01:00
Alejandro Alonso 7594f1883b 🐛 Fix create empty text 2025-11-12 08:20:58 +01:00
Belén Albeza 5c2dde7308 🐛 Fix font family not being updated when changed from dropdown 2025-11-11 15:52:18 +01:00
Belén Albeza 483a1bd703 🐛 Fix downloading wrong font variant 2025-11-11 14:44:56 +01:00
Andrey Antukh e1a275c7a9 Merge remote-tracking branch 'origin/staging' into develop 2025-11-11 14:07:07 +01:00
Andrey Antukh 96d9724516 📎 Update changelog 2025-11-11 14:04:04 +01:00
Andrey Antukh 8158f2956f Backport github release workflow from develop 2025-11-11 14:01:25 +01:00
Eva Marco e45994e836 🐛 Fix color row opacity (#7550) 2025-11-11 13:30:08 +01:00
Xaviju 83da59e03c Add composite shadow token to inspect tab (#7703) 2025-11-11 13:28:11 +01:00
Yamila Moreno fb21a98b0c Merge pull request #7706 from penpot/yms-fix-release-docker-images
🚧 Fix docker images arch during release
2025-11-11 13:21:21 +01:00
Elena Torro 23baf6d18b 🐛 Fix extrect calculation for shadows and blurs depending on the scale 2025-11-11 12:50:15 +01:00
Andrey Antukh 28cf67e7ff 🎉 Add management RPC API (#7700)
* 🎉 Add management RPC API

And refactor internal http auth flow

* 📎 Adjust final url namings

* 📚 Update changelog
2025-11-10 17:10:59 +01:00
Elena Torro 1b50c13c4d 🐛 Render shadows on nested shapes 2025-11-10 14:13:49 +01:00
Pablo Alba 7de95e108b 🐛 Fix crash when using decimal values for X/Y or width/height (#7722) 2025-11-10 11:28:00 +01:00
Luis de Dios c6b907d05c 📚 Improve switch component documentation (#7714) 2025-11-10 11:00:44 +01:00
Pablo Alba ffb4d6a890 🐛 Fix input confirmation behavior is not uniform 2025-11-10 09:50:26 +01:00
Ahmad HosseinBor 69c4a8932a 🌐 Add translations for: Persian
Currently translated at 40.2% (787 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2025-11-10 08:51:23 +01:00
Luis de Dios fa25307c05 🐛 Fix correct alignment of property names (#7717) 2025-11-09 17:52:11 +01:00
Xaviju 43a136a9e9 💄 Fix minor style details on DS select ghost variant (#7707) 2025-11-07 22:46:57 +01:00
Yamila Moreno 3ec4c96b48 🚧 Fix docker images arch during release 2025-11-07 17:50:09 +01:00
Eva Marco 2eaeb8e9a5 🐛 Fix flex children subgrid gap 2025-11-07 13:49:45 +01:00
Andrey Antukh 604f6ca024 🐛 Fix incorrect value coercing on legacy select component (#7710)
on managing values with select
2025-11-07 13:16:39 +01:00
Andrey Antukh e3cf70d3a8 Add URI to the report.txt (#7709) 2025-11-07 13:16:21 +01:00
Alejandro Alonso 6aedac35f2 🐛 Fix wasm erros when images are not found 2025-11-07 13:08:41 +01:00
Alejandro Alonso a11b0f54d7 🐛 Fix changing properties resizes the text box height 2025-11-07 12:34:51 +01:00
Belén Albeza ec0dc2931c Update copyright string in static page (#7701) 2025-11-07 10:54:27 +01:00
Andrey Antukh 9d65d11c91 Merge remote-tracking branch 'origin/staging' into develop 2025-11-07 10:43:27 +01:00
Luis de Dios f00fd1d5a8 🎉 Use toggle for switching boolean variant property names (#7564) 2025-11-07 09:47:57 +01:00
Alejandro Alonso d796dbb572 Merge pull request #7705 from penpot/niwinz-staging-fix-shadows
🐛 Restrict shadow colors to plain colors only
2025-11-06 16:10:02 +01:00
Andrey Antukh e979476b0e 🐛 Restrict shadow colors to plain colors only
Previously, shadows used a general-purpose color schema that allowed
to have gradients and images on the data structure. This commit fixes
that using a specific schema for shadow colors that only allows plain
colors.

A migration is added to clean up existing shadows with non-plain
colors.
2025-11-06 15:54:50 +01:00
Andrey Antukh 097897d8da Add better sse parser for backend tests 2025-11-06 15:54:50 +01:00
Alejandro Alonso ba092f03e1 🎉 Use Vec instead of Indexset 2025-11-06 14:16:07 +01:00
Alejandro Alonso 61202e1cab Merge pull request #7698 from penpot/elenatorro-fix-word-breaking-different-browsers
🔧 Fix cross-browser text issues
2025-11-06 12:34:22 +01:00
Elena Torro f496ba78f3 🔧 Fix cross-browser text issues 2025-11-06 12:20:02 +01:00
Alejandro Alonso b9a0c6d932 Merge pull request #7702 from penpot/alotor-tiles-fixes
 Removed some artifacts when tile rendering
2025-11-06 12:11:23 +01:00
alonso.torres a59ce2ed16 Removed some artifacts when tile rendering 2025-11-06 11:46:02 +01:00
Xaviju c221b9366f Add e2e tests to inspect tab (#7685) 2025-11-06 10:07:50 +01:00
Alejandro Alonso 8e0aa683a1 Merge pull request #7583 from penpot/niwinz-develop-backend-access-deleted-files
 Add RPC methods for enable access to deleted but recoverable projects and files
2025-11-06 06:44:56 +01:00
Alejandro Alonso 445d40b71c Merge pull request #7691 from penpot/alotor-improved-render-tiling
 Improve tile rendering updating
2025-11-05 17:29:25 +01:00
Alejandro Alonso 7889578ced 🎉 Use textures directly for images 2025-11-05 17:16:06 +01:00
alonso.torres a230d2fcf6 Improve tile rendering updating 2025-11-05 17:16:06 +01:00
Belén Albeza 78fde35df9 🔧 Upgrade storybook (#7693)
* 🔧 Upgrade to storybook 9.x

* 🔧 Upgrade to storybook 10.x

* 🔧 Update watch:storybook script so it builds its assets dependencies first

* 🔧 Use vitest for storybook tests (test-storybook was deprecated)
2025-11-05 17:15:19 +01:00
Eva Marco bb65782d08 🎉 Add sidebar css variables (#7645)
* 🎉 Add sidebar css variables

* 🎉 Explain more in depth the grid structure
2025-11-05 14:06:26 +01:00
Alejandro Alonso 02a1992a0a Merge pull request #7694 from penpot/niwinz-staging-runner-fixes
🐛 Fix precision issues on worker task scheduling mechanism
2025-11-05 12:18:23 +01:00
Andrey Antukh 1cce82f958 Merge remote-tracking branch 'origin/staging' into develop 2025-11-05 12:15:15 +01:00
Alejandro Alonso a576c0404a 🐛 Fix focus mode across page and file navigation (#7695) 2025-11-05 12:05:00 +01:00
Andrey Antukh 7d5c1c9b5f Make file-gc-scheduler task compatible with virtual clock
And simplify implementation
2025-11-05 10:47:31 +01:00
Andrey Antukh cd53d3659c 🐛 Truncate worker scheduled-at to milliseconds
The nanosecond precision has the problem with transit serialization
roundtrip used for pass data on the worker scheduler throught redis
and generates unnecesary rescheduling.
2025-11-05 10:47:31 +01:00
Andrey Antukh 132f7d6d3e ♻️ Add minor refactor on tokens main form (#7690) 2025-11-05 10:37:38 +01:00
Alejandro Alonso b2a9c55874 Merge pull request #7674 from penpot/elenatorro-12478-fix-new-lines
🐛 Fix new lines issues
2025-11-05 10:13:41 +01:00
Alejandro Alonso d610e7c892 Merge pull request #7671 from penpot/niwinz-develop-path-data-fix
🐛 Relax schema for importing plain path data related to curve-to command
2025-11-05 10:04:03 +01:00
Alejandro Alonso 1b5557759a Merge pull request #7687 from penpot/ladybenko-12440-fix-corrupt-files
🐛 Fix wasm crash when loading a file with missing font assets
2025-11-05 07:57:39 +01:00
Belén Albeza 8148da58ed 🐛 Fix wasm crash when loading a file with missing font assets 2025-11-05 07:47:16 +01:00
Alejandro Alonso 537f681944 Merge pull request #7692 from penpot/niwinz-develop-logging-improvements
 Remove unnecesary report on duplicate email error validation
2025-11-05 07:46:16 +01:00
Alejandro Alonso 9e7ec594ca Merge pull request #7680 from penpot/niwinz-staging-file-export-fix
🐛 Fix race condition on file export process
2025-11-05 07:45:26 +01:00
Alejandro Alonso 7c529eedd4 Merge pull request #7682 from penpot/niwinz-staging-worker-runner-exceptions
🐛 Fix incorrect status return on worker runner
2025-11-05 07:44:28 +01:00
Alejandro Alonso 500c5c81d4 Merge pull request #7686 from penpot/elenatorro-12499-fix-nested-blur
🐛 Fix children blur rendering
2025-11-05 07:27:43 +01:00
Alejandro Alonso 6ea69c94ee 🎉 Improve big images performance 2025-11-04 22:02:34 +01:00
Andrey Antukh 9b3f68ad14 Remove unnecesary report on duplicate email error validation 2025-11-04 20:34:25 +01:00
Andrey Antukh 34363320ae Merge branch 'main' into staging 2025-11-04 16:49:53 +01:00
Andrey Antukh 092a5139e3 🐛 Fix incorrect token sets migration (#7673) 2025-11-04 16:49:08 +01:00
Andrey Antukh 4a01121043 Merge tag '2.11.0-RC3' 2025-11-04 16:43:32 +01:00
Elena Torro 564ad8adba 🐛 Fix children blur rendering 2025-11-04 15:37:49 +01:00
Andrey Antukh 78e2d6fec3 🐛 Relax schema for importing plain path data related to curve-to command 2025-11-04 12:59:26 +01:00
Andrey Antukh c850f101d3 Merge remote-tracking branch 'origin/staging' into develop 2025-11-04 12:49:57 +01:00
Andrey Antukh 49721c0bcd Add better logging context report on worker runner 2025-11-04 12:44:38 +01:00
Andrey Antukh c214cc1544 🐛 Do not process runner result if no result returned 2025-11-04 12:44:38 +01:00
Andrey Antukh eaabe54c4b 💄 Check the runner task exists as first condition 2025-11-04 12:44:38 +01:00
Eva Marco 21fb38e5bd 🐛 Fixes some problems with dropdowns and token inputs (#7640)
* 🐛 Fix apply color token on strokes

* 🐛 Fix size and position of some numeric inputs

* 🐛 Fix padding token application

* ♻️ Fix ci

* 🐛 Fix selected color tick

* 🐛 Fix comments and design review
2025-11-04 12:39:41 +01:00
Luis de Dios 37aa59b164 🐛 Fix hidden advanced frame grid options menu (#7681) 2025-11-04 11:57:52 +01:00
Elena Torro 24e4ece323 🐛 Fix line-height rendering on empty lines 2025-11-04 11:25:14 +01:00
Andrey Antukh cbae3dca34 Simplify the approach for return streamable body
Removing unnecesary syntax overhead with simplier abstraction
2025-11-04 10:56:05 +01:00
Andrey Antukh 8307b699bf 🐛 Remove a race condition on file export
Caused when file is deleted in the middle of an exportation. The
current export process is not transactional, and on file deletion
several queries can start return not-found exception because of
concurrent file deletion.

With the changes on this PR we allow query deleted files internally
on the exportation process and make it resilent to possible
concurrent deletion.
2025-11-04 10:56:05 +01:00
Andrey Antukh cd6865f54b ⬆️ Update yetti dependency
Bugfixes
2025-11-04 10:56:05 +01:00
Elena Torro e673035817 🔧 Filter out empty paragraph content 2025-11-04 09:57:13 +01:00
Stas Haas f6e77c09b3 🌐 Add translations for: German
Currently translated at 90.4% (1770 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-11-03 21:51:14 +01:00
Elena Torro 87fc71b55d 🐛 Ignore non-frequent chars 2025-11-03 17:08:38 +01:00
Elena Torro b76bfa2197 🐛 Fix width on rotation 2025-11-03 16:37:17 +01:00
Andrey Antukh 88493f6805 🐛 Fix incorrect query for subscription editors (#7672)
Default teams should be present on the query results
2025-11-03 16:14:24 +01:00
Pablo Alba 69bbdad570 🐛 Fix nested variant in a component doesn't keep inherited overrides (3) 2025-11-03 15:36:37 +01:00
Pablo Alba df4279bdee Revert "🐛 Fix nested variant in a component doesn't keep inherited overrides (2)"
This reverts commit 7c6515aa7b.
2025-11-03 15:36:37 +01:00
Alejandro Alonso c8c901ee4c Merge pull request #7670 from penpot/ladybenko-fix-broken-wasm-test
 Fix broken wasm test
2025-11-03 14:49:29 +01:00
Belén Albeza 8f0e5e36e9 Fix broken wasm test 2025-11-03 14:18:15 +01:00
Andrés Moya a5e9f7229b 💄 Fix tests nesting 2025-11-03 14:02:29 +01:00
Andrés Moya 5f22220a8b 🐛 Add test to catch a fixed bug and avoid regressions 2025-11-03 14:02:29 +01:00
Luis de Dios 6c7661b04d 🐛 Fix add missing use in SCSS 2025-11-03 12:47:25 +01:00
Alejandro Alonso b867f276f2 Merge pull request #7665 from penpot/superalex-fix-texts-migrations
🐛 Fix texts migrations
2025-11-03 12:20:16 +01:00
Alejandro Alonso da8d7a78cf 🐛 Add migration for texts with fills only in position-data 2025-11-03 12:06:41 +01:00
Alejandro Alonso ec4936f5fe 🐛 Fix 0006 migration for strings that should be uuids 2025-11-03 12:06:41 +01:00
Alejandro Alonso dd9ec54bd1 Merge pull request #7664 from penpot/alotor-performance-improvements
 Improve boolean calculations
2025-11-03 12:04:36 +01:00
Alejandro Alonso 3ad4b0a453 Merge pull request #7657 from penpot/elenatorro-12448-fix-text-tabs
🐛 Fix tabs rendering
2025-11-03 11:53:20 +01:00
Belén Albeza 83cd9c3db6 🔧 Fix rust linter errors 2025-11-03 11:45:05 +01:00
Andrey Antukh 399feec032 ⬆️ Update rust to 1.91 2025-11-03 11:45:00 +01:00
Andrey Antukh 481fa44f18 Merge remote-tracking branch 'origin/staging' into develop 2025-11-03 11:41:13 +01:00
Andrey Antukh 42c9f2123d Merge pull request #7663 from penpot/niwinz-staging-update-rust
⬆️ Update rust to 1.91
2025-11-03 11:26:01 +01:00
Elena Torro d18a018236 🔧 Fix tab rendering with the text editor 2025-11-03 11:02:28 +01:00
Belén Albeza 4ab6ecec21 🔧 Fix rust linter errors 2025-11-03 10:43:35 +01:00
alonso.torres b39c00fbf6 Improve boolean calculations 2025-11-03 09:50:29 +01:00
Andrey Antukh 8a0fddf1e4 ⬆️ Update rust to 1.91 2025-11-03 09:10:40 +01:00
Luis de Dios 95fdd75030 🐛 Fix misaligned right sidebar menus 2025-11-03 08:34:09 +01:00
Andrey Antukh 54489c4285 🔧 Add better regex for commit checker 2025-11-01 18:46:26 +01:00
Stas Haas e7b8ad8ee2 🌐 Add translations for: German
Currently translated at 89.3% (1747 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-11-01 10:51:52 +00:00
Andrey Antukh 6815806669 Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 18:15:12 +01:00
Andrey Antukh febe87aa7b 🐛 Fix incorrect checksum of the jdk on dockerfiles 2025-10-31 18:01:55 +01:00
Andrey Antukh 83763b46ce Add RPC methods for manage deleted files
This includes: get already deletedf files, restore deleted files
and permanently delete files marked for deletion.
2025-10-31 16:07:23 +01:00
Andrey Antukh 1ddc196484 Make the get-projects return deleted projects
And adapt the frontend code to properly filter
deleted projects on appropriate pages
2025-10-31 16:07:23 +01:00
Andrey Antukh 37d4844518 💄 Add minor cosmetic changes to get-project-files rpc method 2025-10-31 16:07:23 +01:00
Andrey Antukh 76e610dd06 🔥 Remove duplicated functions from file tests namespace 2025-10-31 16:07:23 +01:00
Andrés Moya 99e8b22672 🐛 Fix theme validation when still no tokens library exists 2025-10-31 16:04:50 +01:00
Andrey Antukh 65adbfaadb Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 14:50:59 +01:00
Andrey Antukh 0581c60800 ⬆️ Update jdk and node on docker images 2025-10-31 14:50:12 +01:00
Andrey Antukh 7e92408807 ⬆️ Update jdk and node on devenv 2025-10-31 14:50:12 +01:00
Marina López 03eeeda44f Add improvements payment flow 2025-10-31 14:15:22 +01:00
Alejandro Alonso 2f33009e69 Merge pull request #7655 from penpot/superalex-add-migration-to-fix-text-attrs-with-blank-strings
📎 Add migration to fix text attrs with blank strings
2025-10-31 13:58:42 +01:00
Alejandro Alonso 1d5c407456 📎 Add migration to fix text attrs with blank strings 2025-10-31 13:30:46 +01:00
Alejandro Alonso aa15232cc7 Merge pull request #7648 from penpot/alotor-performance-improvements
 Add performance improvements for wasm render
2025-10-31 12:22:14 +01:00
Andrey Antukh f53935f5df Merge remote-tracking branch 'origin/staging' into develop 2025-10-31 12:13:29 +01:00
alonso.torres de04026dc8 After review changes 2025-10-31 12:04:52 +01:00
alonso.torres f3b914534f Add scale_content to shapes_pool 2025-10-31 11:56:28 +01:00
alonso.torres fcc9282304 Fix problems with SVGraw and modifiers 2025-10-31 11:56:28 +01:00
alonso.torres 122619b197 Support for booleans dynamic transforms 2025-10-31 11:56:28 +01:00
alonso.torres dbf9bdceb5 Removed modifiers from code 2025-10-31 11:56:28 +01:00
Alejandro Alonso f6eb492329 🐛 Fig shapes pool extending size 2025-10-31 11:56:28 +01:00
Alejandro Alonso c66a8f5dc5 Improve shapes pool performance 2025-10-31 11:56:28 +01:00
alonso.torres ed4df73e42 Changes to modifiers 2025-10-31 11:56:28 +01:00
alonso.torres 59e745e9ab Improve performance of group bounds 2025-10-31 11:56:28 +01:00
alonso.torres d4b4d943c6 Store bounds inside the shape 2025-10-31 11:56:28 +01:00
alonso.torres e4b4f1bd08 Removed all_ancestors traversals 2025-10-31 11:56:28 +01:00
alonso.torres e58b2453b1 Removed method set_selrect_for_current_shape 2025-10-31 11:56:28 +01:00
alonso.torres e9230b8b54 Change internal data type for tiles 2025-10-31 11:56:28 +01:00
alonso.torres 9d7cac5e73 Improved performance of children ancestors 2025-10-31 11:56:28 +01:00
alonso.torres 17fefcf0bc Changes WASM serialization mechanism 2025-10-31 11:56:28 +01:00
Alejandro Alonso 4367bd2dc6 Merge pull request #7651 from penpot/niwinz-email-bugfix-1
🐛 Fix regression on sending quote notification email
2025-10-31 11:23:39 +01:00
Miguel de Benito Delgado 6e2b2e8924 📚 Update increase/decrease font shortcuts (#7652) 2025-10-31 11:21:53 +01:00
Andrey Antukh f3805e3b70 🐛 Fix regression on sending quote notification email 2025-10-31 10:56:33 +01:00
David Barragán Merino 262937c421 📚 Add recommendations for valkey/redis configuration 2025-10-31 10:45:33 +01:00
Alejandro Alonso 15ee75a692 Merge pull request #7647 from penpot/elenatorro-remove-unnecessary-text-properties
🔧 Remove unused text attrs
2025-10-31 09:56:12 +01:00
Alonso Torres 942e3300dd 🐛 Fix problem when checking usage with removed teams (#7638) 2025-10-31 09:22:31 +01:00
Elena Torro eaa3904a3a 🔧 Remove unused text attrs 2025-10-31 09:22:01 +01:00
Alejandro Alonso 0c66b5db73 📎 Set stronger text validation (#7646) 2025-10-31 09:19:53 +01:00
Elena Torró cc40448cb5 Merge pull request #7644 from penpot/azazeln28-fix-empty-text-attr-defaults
🐛 Fix empty text attr defaults
2025-10-31 09:00:19 +01:00
Alejandro Alonso 6a2029ca3b 🐛 Fix error comment message after the demo account creation (#7615) 2025-10-31 08:56:34 +01:00
David Barragán Merino f32913adcf 📚 Adapt doc with the storage settings changes (#7607) 2025-10-31 08:56:06 +01:00
Juan de la Cruz d906f05a6f 🎉 Add 2.11 release slides and images (#7606) 2025-10-31 08:54:19 +01:00
Yamila Moreno 2402334fb2 Merge pull request #7641 from penpot/yms-add-bundle-version-to-docker-metadata
🚧 Add bundle version to Docker metadata
2025-10-30 15:12:46 +01:00
Elena Torró c3e2621ed5 Merge pull request #7643 from penpot/superalex-serialize-font-weight-properly
🐛 Serialize font weight properly
2025-10-30 14:59:47 +01:00
Yamila Moreno d37695d7a5 🚧 Add bundle version to Docker metadata 2025-10-30 14:36:23 +01:00
Aitor Moreno fadbe24aaa 🐛 Fix empty text attr defaults 2025-10-30 14:16:30 +01:00
Alejandro Alonso 9d29d5e8cc 🐛 Serialize font weight properly 2025-10-30 14:15:57 +01:00
Florian Schroedl e681f95a70 Add box shadow token 2025-10-30 14:05:42 +01:00
Alejandro Alonso 5c8b401037 Merge pull request #7637 from penpot/elenatorro-12439-fix-fonts-default-values
🐛 Fix default font size in text spans
2025-10-30 11:14:20 +01:00
Elena Torro 9dfb0ebe84 🐛 Fix default font size in text spans 2025-10-29 17:23:29 +01:00
Alejandro Alonso 08162c825d Merge pull request #7633 from penpot/superalex-options-button-does-not-work-for-comments-created-in-the-lower-part-of-the-screen-with-an-active-reply-field
🐛 Fix options button does not work for comments created in the lower part of the screen
2025-10-29 16:18:21 +01:00
Alejandro Alonso bc700334ca 🐛 Fix options button does not work for comments created in the lower part of the screen 2025-10-29 16:17:57 +01:00
Alejandro Alonso 133590f19c Merge pull request #7635 from penpot/alotor-fix-paste-position
🐛 Fix paste without selection sends the new element in the back
2025-10-29 16:16:17 +01:00
alonso.torres 66c5a0570e 🐛 Fix paste without selection sends the new element in the back 2025-10-29 16:15:55 +01:00
Andrés Moya 94cbf9d8f2 🎉 Add integration test to check new validation 2025-10-29 15:40:45 +01:00
Andrés Moya 70143f8ae3 🐛 Fix theme renaming and small refactor tokens forms validation 2025-10-29 15:40:45 +01:00
Xaviju 6c824651df 🎉 Add copy shortands button to panels (#7580)
* 🎉 Add copy shorthands button to panels
* 🎉 Add shorthand for strokes
* 🎉 Add shorthand for fonts
* 🎉 Add shorthand for borders
* 🎉 Add shorthand for padding
* 🎉 Add shorthand for grid
* 🎉 Add shorthand for layout element
* 🐛 Refactor to fix hook rendering
2025-10-29 13:51:36 +01:00
David Barragán Merino 1b81ddebb4 🐛 Fix some paths and add missed nginx config file for the storybook docker image 2025-10-29 13:46:29 +01:00
David Barragán Merino 6076df5c80 🎉 Detach storybook from the frontend build process 2025-10-29 13:45:54 +01:00
Alejandro Alonso 6d2d66a079 Merge pull request #7634 from penpot/alotor-fix-editable-label
🐛 Fix problem with certain text input and drag/drop
2025-10-29 12:50:03 +01:00
Alejandro Alonso 239af4fb82 🐛 Fix problem with text grow types 2025-10-29 12:40:11 +01:00
alonso.torres 0ad4a9ca7e 🐛 Fix problem with certain text input and drag/drop 2025-10-29 12:35:13 +01:00
David Barragán Merino 034463e63a 🐛 Fix some paths and add missed nginx config file for the storybook docker image 2025-10-29 12:02:32 +01:00
Eva Marco aadc1aac1c 🐛 Fix some error translations 2025-10-29 11:14:20 +01:00
Elena Torró 2cdc76f1af Merge pull request #7573 from penpot/superalex-select-boards-to-export-to-pdf
 Select boards to export to PDF
2025-10-29 10:13:32 +01:00
alonso.torres 23f49237f8 🐛 Fix problem with plugins generating code for pages different than current one 2025-10-29 10:08:41 +01:00
Xaviju 93fb54c116 Enable single color-space selector for styles and computed… (#7525)
*  Enable single color-space selector for styles and computed tab
2025-10-29 09:53:37 +01:00
Alejandro Alonso 7565bb8d24 Merge pull request #7466 from penpot/bameda-storybook-detachment
🎉 Detach storybook from the frontend build process
2025-10-29 09:53:18 +01:00
Alejandro Alonso 0d394ee962 Merge pull request #7593 from penpot/eva-fix-some-translations
🐛 Fix some error translations
2025-10-29 09:36:06 +01:00
Alejandro Alonso c4bebc1b0a Merge pull request #7625 from penpot/elenatorro-12374-fix-remove-selection
🐛 Fix text selection
2025-10-29 09:35:56 +01:00
Elena Torro 6edc29dce2 🐛 Fix text selection 2025-10-29 09:20:51 +01:00
Eva Marco d773e3a966 🐛 Fix some error translations 2025-10-29 09:20:07 +01:00
Alejandro Alonso e18aef1d39 Merge pull request #7610 from mdbenito/fix/conflicting-selection-shortcut-text-shape
🐛 Fix conflicting shortcut in text editor
2025-10-29 08:45:05 +01:00
Alejandro Alonso b033690239 Merge pull request #7618 from penpot/andy-docs-typography-token
📚 Add typography token to the user guide
2025-10-29 08:22:20 +01:00
Alejandro Alonso 9f732eb45a Merge pull request #7595 from penpot/esther-moreno-user-guide-new-architecture
📚 New architecture in user guide
2025-10-29 08:02:45 +01:00
Alejandro Alonso 474453a503 Merge pull request #7594 from penpot/eva-fix-dropdown-submenu
🐛 Fix submenu visibility
2025-10-29 07:53:06 +01:00
Alejandro Alonso c3d40659a9 Merge pull request #7600 from penpot/elenatorro-12344-fix-different-text-span-font-sizes
🔧 Add support for text spans of different sizes
2025-10-29 07:25:49 +01:00
David Barragán Merino 15e2b35afc 🎉 Detach storybook from the frontend build process 2025-10-28 20:58:50 +01:00
Elena Torró ad15887d57 Merge pull request #7623 from penpot/superalex-fix-nested-fills-for-shapes-with-svg-attrs
🐛 Fix nested fills for shapes with svg attrs
2025-10-28 16:01:27 +01:00
Elena Torró d01f921344 Merge pull request #7624 from penpot/superalex-fix-prevent-rendering-of-unused-fill-slots-in-shapes
🐛 Prevent rendering of unused fill slots in shapes
2025-10-28 15:54:51 +01:00
Alejandro Alonso 9e035ec4fe Merge pull request #7605 from penpot/ladybenko-fix-text-playground-crash
🔧 Fix text-related playgrounds (wasm)
2025-10-28 14:11:52 +01:00
Alejandro Alonso fbacdf0351 🔧 Fix shapes-related playgrounds (wasm) 2025-10-28 14:09:24 +01:00
Alejandro Alonso 3f4d699395 🐛 Prevent rendering of unused fill slots in shapes 2025-10-28 13:35:34 +01:00
Alejandro Alonso 1626371337 Merge pull request #7619 from penpot/elenatorro-11889-fix-text-span-selection
🐛 Fix data-itype for text spans
2025-10-28 13:33:19 +01:00
Marina López 4d8a70f1fa Improvements payments 2025-10-28 12:36:32 +01:00
Alejandro Alonso 14d5de29da 🐛 Fix nested fills for shapes with svg attrs 2025-10-28 12:25:57 +01:00
Esther Moreno df718c940f 📚 New architecture in user guide 2025-10-28 11:04:09 +01:00
Elena Torro 80c78d9cd4 🐛 Fix pasting text within an existing text 2025-10-28 09:39:52 +01:00
Pablo Alba e2ce226814 🐛 Fix remove flex button doesn’t work within variant 2025-10-28 09:38:38 +01:00
Elena Torro 28c4c1a286 🐛 Fix data-itype for text spans 2025-10-27 16:55:51 +01:00
alonso.torres f64105ad08 🐛 Fix problem with changing gap in flex layout 2025-10-27 16:29:59 +01:00
Andres Gonzalez a346d29d76 📚 Add typography token to the user guide 2025-10-27 15:18:14 +01:00
Ingrid Pigueron ccb7b41b3a 🌐 Add translations for: French
Currently translated at 98.3% (1923 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-27 12:02:59 +00:00
Elena Torro 2c37c5c8ed 🔧 Add support for text spans of different sizes 2025-10-27 12:43:41 +01:00
Andrés Moya ed767d9a5b 🐛 Fix library update notificacions showing when they should not 2025-10-27 11:14:41 +01:00
Miguel de Benito Delgado 57bfca4062 🐛 Maintain selection after font change 2025-10-26 12:02:20 +00:00
Miguel de Benito Delgado e9dcd64463 🐛 Fix conflicting shortcut in text editor 2025-10-26 11:11:50 +00:00
Belén Albeza b498056c01 🔧 Fix text-related playgrounds (wasm) 2025-10-24 14:35:28 +02:00
Andrés Moya 81f851cad4 🔧 Deactivate debug traces for fonts module 2025-10-24 11:49:43 +02:00
Pablo Alba 245190f4f9 🐛 Fix variant validation when nil 2025-10-24 10:59:16 +02:00
Alejandro Alonso 479ce99b32 Improve setting svg attrs in wasm 2025-10-24 10:35:30 +02:00
Alejandro Alonso 6290b88d2e Merge pull request #7601 from penpot/alotor-fix-text-grow-type-problem
🐛 Fix problem with text grow types
2025-10-24 09:45:47 +02:00
Elena Torró dba718b850 Merge pull request #7484 from penpot/superalex-fix-text-line-height-values-are-wrong
🐛 Text line-height values are wrong
2025-10-24 08:49:35 +02:00
alonso.torres 7c1205018b 🐛 Fix problem with text grow types 2025-10-23 17:39:18 +02:00
Elena Torró 89763d7c5a Merge pull request #7554 from penpot/azazeln28-fix-text-editor-v2-issues
🐛 Fix text editor v2 render integration issues
2025-10-23 15:55:54 +02:00
Aitor Moreno 7f6af6179b 🐛 Fix paste when collpaseNode is a br 2025-10-23 15:06:32 +02:00
Aitor Moreno ceb184782f 🐛 Fix text editor paste inline/paragraph 2025-10-23 15:06:01 +02:00
Aitor Moreno 247c5c3700 Merge pull request #7588 from penpot/elenatorro-fix-text-tests
🔧 Fix text align selrect
2025-10-23 15:04:48 +02:00
Pablo Alba 0882c448f6 📎 Cleanup log files 2025-10-23 14:34:26 +02:00
Belén Albeza f8cebb9d63 🐛 Fix scroll bar in design tab (#7582)
* 🐛 Fix scroll bar in design tab

* ♻️ Remove deprecated css tokens in options.scss
2025-10-23 14:11:11 +02:00
Alejandro Alonso 1e248c7177 🐛 Fix demo accounts creation 2025-10-23 13:45:11 +02:00
Elena Torró 351a35dad6 Merge pull request #7574 from penpot/azazeln28-refactor-text-struct-naming
♻️ Rename textleafs and inline to textspan
2025-10-23 13:39:36 +02:00
Aitor Moreno eb088c31c1 🔧 Rename textleafs and inlines to keep coherence between render and editor 2025-10-23 13:04:21 +02:00
Belén Albeza 45af469a11 🐛 Fix invite selection copy
* 🐛 Fix selected invitations copy not being localized/pluralized

*  Add integration test for team invites + fixes unaccessible dom
2025-10-23 12:04:34 +02:00
Eva Marco 232f2271d3 🐛 Fix submenu visibility 2025-10-23 11:52:03 +02:00
Elena Torro a30315c91c 🔧 Fix text align selrect and update regression tests 2025-10-23 11:44:40 +02:00
Pablo Alba 04542e1e66 Add variants to plugins API 2025-10-23 10:52:10 +02:00
Alejandro Alonso 36c986d8e8 🐛 Fix file doesn’t open after deleting the library used in it 2025-10-23 09:51:10 +02:00
Alejandro Alonso 38c3b2eaba Merge pull request #7584 from penpot/alotor-fix-flex-issue
🐛 Fix problem with flex type conversions
2025-10-22 16:49:56 +02:00
alonso.torres 98e91ecda5 🐛 Fix problem with flex type conversions 2025-10-22 16:03:51 +02:00
Alejandro Alonso 54ac64db4b Merge pull request #7578 from penpot/supealex-fix-selected-colors-children-shapes-in-multiple-selection
🐛 Fix selected colors not showing colors from children shapes in multiple selection
2025-10-22 15:18:58 +02:00
Alejandro Alonso 30ca6bf6ff 🐛 Fix selected colors not showing colors from children shapes in multiple selection 2025-10-22 14:53:06 +02:00
David Barragán Merino 81a364dfc4 🐳 Set default values for maxmemory and maxmemory-policy in Valkey 2025-10-22 13:43:30 +02:00
Pablo Alba c6b9954af8 🐛 Fix nested variant in a component doesn't keep inherited overrides 2025-10-22 13:35:22 +02:00
Alejandro Alonso f120cf82d3 Select boards to export to PDF 2025-10-22 13:19:10 +02:00
Belén Albeza 7ec335ae96 🐛 Fix export element crashing the app 2025-10-22 13:02:55 +02:00
Eva Marco 8dcc46aba8 🐛 Fix color row opacity (#7550) 2025-10-22 12:20:58 +02:00
Xaviju 058a555594 🎉 Add shadow panel to inspect styles tab (#7566) 2025-10-22 12:17:14 +02:00
Luis de Dios e073b89604 🐛 Fix property input remains editable after keeping default property name (#7549)
* 🐛 Fix property input remains focused when keeping default property name

* 📎 PR changes
2025-10-22 10:48:03 +02:00
Elena Torró 140290cd60 Merge pull request #7556 from penpot/ladybenko-12362-fix-underline-spacing
🐛 Fix underline not matching spacing/thickness
2025-10-22 10:21:48 +02:00
Pablo Alba 5e6af5aea9 🐛 Fix text override is lost after switch 2025-10-22 09:43:12 +02:00
Alejandro Alonso 5df2a740b9 Merge pull request #7571 from penpot/superalex-fix-cleaning-nested-fills
🐛 Fix cleaning nested fills
2025-10-21 17:05:00 +02:00
Pablo Alba fd596a1371 🐛 Fix incorrect behavior of Alt + Drag for variants 2025-10-21 17:02:10 +02:00
Belén Albeza 87221eb7db 🐛 Fix underline not matching spacing/thickness 2025-10-21 16:57:02 +02:00
Aitor Moreno 69f2e131d7 Merge pull request #7570 from penpot/elenatorro-12386-fix-missplaced-text-strokes
🐛 Fix texts offset-y calculation when there are multiple lines and stroke paints
2025-10-21 16:46:53 +02:00
Alejandro Alonso 69da63e01c 🐛 Fix cleaning nested fills 2025-10-21 16:45:53 +02:00
Elena Torro dc689f9756 🐛 Fix texts offset-y calculation when there are multiple lines and stroke paints 2025-10-21 16:13:40 +02:00
Marina López 82e1a5003c 🐛 Fix condition report feedback 2025-10-21 15:29:06 +02:00
Andrey Antukh 024697ff87 Add cause stack trace logging on ui error boundary 2025-10-21 15:04:53 +02:00
alonso.torres fc4b717287 🐛 Fix problem with CI 2025-10-21 14:45:59 +02:00
Marina López 9e8cdc8a3f 🐛 Fix search shortcut 2025-10-21 13:28:31 +02:00
Elena Torro a51fd009bc 🔧 Improve text tiles intersection on changes 2025-10-21 13:21:02 +02:00
David Barragán Merino f795f20ef8 📎 Notify about failures in releasing and creating docker images 2025-10-21 13:16:04 +02:00
Eva Marco ca21e7e8b4 🐛 Fix font size placeholder 2025-10-21 12:27:15 +02:00
alonso.torres 93e7f2950b 🐛 Fix problem with multiple selection and shadows 2025-10-21 12:27:14 +02:00
Xaviju d0e5d0d952 Display resolved value for composite typography tokens (#7537)
*  Display resolved value for composite typography tokens tooltip

*  Add missing key to iteration
2025-10-21 11:30:34 +02:00
Andrey Antukh e4c07e0ec0 Merge staging into develop 2025-10-21 11:23:59 +02:00
alonso.torres 068caf2784 Merge remote-tracking branch 'origin/staging' into develop 2025-10-21 11:19:21 +02:00
Xavier Julian 436bc23da4 🐛 Remove duplicated tokens on stroke panel 2025-10-21 10:56:38 +02:00
Pablo Alba 579de6558a 🐛 Fix on copy instance inside a components chain touched are missing 2025-10-21 10:37:07 +02:00
Pablo Alba 2d45cba36c 🐛 Fix Restoring a variant from another file makes it overlap 2025-10-21 10:09:50 +02:00
Alejandro Alonso cf21ffb30f Merge pull request #7528 from penpot/alotor-set-children
 Add set_children granular methods for performance
2025-10-21 09:57:22 +02:00
Elena Torró 7a2fe232d5 Merge pull request #7527 from penpot/ladybenko-12329-break-editor-word
🐛 Make internal DOM of text editor v2 break words as the render engine does
2025-10-21 09:41:15 +02:00
Alejandro Alonso 9e17a0e65d 🐛 Fix unread comments 2025-10-21 09:30:01 +02:00
Pablo Alba 220c27c354 🐛 Fix nested variant in a component doesn't keep inherited overrides (2) 2025-10-20 18:28:10 +02:00
Pablo Alba b0e4257e56 📚 Remove wrong line on CHANGES 2025-10-17 14:20:31 +02:00
Pablo Alba b3cb7df33c 🐛 Fix nested variant in a component doesn't keep inherited overrides 2025-10-17 14:01:54 +02:00
Pablo Alba fec420b6e9 🐛 Fix variants not syncronizing tokens on switch 2025-10-17 13:46:49 +02:00
Eva Marco 35af5455a0 🐛 Fix dropdown width (#7534) 2025-10-17 13:39:29 +02:00
AlexTECPlayz 597fba79cc 🌐 Add translations for: Romanian
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-17 13:07:28 +02:00
Luis de Dios 216b2d3072 🐛 Fix drag & drop functionality is swapping instead or reordering (#7489)
* 🐛 Fix drag & drop functionality is swapping instead or reordering

* ♻️ SCSS improvements
2025-10-17 12:12:34 +02:00
Eva Marco bbc6709943 🐛 Fix applying color tokens on a row from colorpicker (#7524) 2025-10-17 11:10:10 +02:00
Eva Marco 14f6e22610 🐛 Fix composite token placeholders (#7526)
* 🐛 Fix composite token placeholders

* 📚 Recover some translations
2025-10-17 10:57:32 +02:00
Eva Marco 2f27a78bc0 🐛 Fix color row erros from review (#7516)
* 🐛 Fix text Ellipsis on token color row

* 🐛 Fix show token only on first stroke

* 🐛 Fix detach broken token
2025-10-17 10:39:41 +02:00
Marina López f5761066a9 Merge pull request #7511 from penpot/marina-improve-users-give-feedback
 Improve the way users give us feedback
2025-10-17 10:19:34 +02:00
Andrey Antukh 3665bccaed Merge remote-tracking branch 'origin/staging' into develop 2025-10-17 09:43:30 +02:00
Andrey Antukh fbbee98c3d Add proper backend integration of for new feedback form 2025-10-17 09:40:27 +02:00
Marina López 854ad5bb4d Improve the way users give us feedback 2025-10-17 09:39:58 +02:00
Andrey Antukh a32f44a62c 🐛 Use correct error boundary fallback on ui ns 2025-10-17 09:39:58 +02:00
Andrey Antukh 95f58ffda5 Allow add attachements on emails 2025-10-17 09:39:58 +02:00
Andrey Antukh e8e27c25c0 💄 Fix naming on several components under ui ns
Following the current naming convention
2025-10-17 09:39:58 +02:00
Andrey Antukh 42c416e3cb 📎 Add user feedback defaults to backend scripts/_env 2025-10-17 09:39:58 +02:00
Andrés Moya 5ad04e0f4c 🐛 Fix error when selecting set in theme 2025-10-16 16:17:16 +02:00
Belén Albeza 9f4db4479c 🐛 Make internal DOM of text editor v2 break words as the render engine does 2025-10-16 15:55:53 +02:00
alonso.torres 66997d2bc9 Add set_children granular methods for performance 2025-10-16 15:33:08 +02:00
Alejandro Alonso 7350329658 🐛 Filter svg attrs supported in wasm 2025-10-16 14:48:22 +02:00
Elena Torró 544b118925 Merge pull request #7361 from penpot/azazeln28-feat-dom-textarea-position
🎉 Text Editor DOM textarea position
2025-10-16 14:30:41 +02:00
Alejandro Alonso 8ceb909cda Merge pull request #7490 from penpot/elenatorro-12258-fix-text-shapes-intersection
🐛 Fix text tiles intersection
2025-10-16 14:23:41 +02:00
Elena Torro af54e6ccc2 🔧 Fix text layout extrect intersection and refactor calculate_extrect function 2025-10-16 14:00:59 +02:00
Elena Torro 6ef0b8fd16 🔧 Update fixed visual tests 2025-10-16 14:00:54 +02:00
Alejandro Alonso 4a6d143a15 Merge pull request #7522 from penpot/alotor-render-performance
 New render small performance optimizations
2025-10-16 13:45:49 +02:00
David Barragán Merino 07dedbd3bb 📎 Fix registry uri
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2025-10-16 13:06:34 +02:00
Aitor Moreno 7ca8bf32b2 🎉 Set DOM text editor element caret 2025-10-16 12:59:24 +02:00
alonso.torres 2e6fb1b9c5 New render small performance optimizations 2025-10-16 12:40:29 +02:00
AlexTECPlayz 43b03b9714 🌐 Add translations for: Romanian
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-16 12:07:26 +02:00
Alejandro Alonso 8e8d46b314 🐛 Fix performance macros disabled in production mode 2025-10-16 12:01:29 +02:00
Eva Marco e964f9820e 🐛 Fix tooltip position of proportion lock button (#7519) 2025-10-16 11:40:19 +02:00
Pablo Alba d933e91c6c 🐛 Fix variants not syncronizing tokens on switch 2025-10-16 11:21:18 +02:00
Eva Marco 24264e7d8a 🎉 Replace numeric inputs on layout menus (#7488)
* 🎉 Replace numeric inputs on layout menus

* 🎉 Add numeric inputs on gaps

* ♻️ Refactor scss file

* 🐛 Fix comments
2025-10-16 10:05:25 +02:00
Xaviju 5b77df997b 🐛 Remove color validation for non-color tokens in strokes (#7513) 2025-10-16 09:54:50 +02:00
Xaviju 968274096d 🐛 Fix visual glitch in transparent background for swatch component (#7509) 2025-10-16 09:54:33 +02:00
Alejandro Alonso 392e3ac34e Merge pull request #7507 from penpot/elenatorro-11974-fix-resize-on-blocked-shapes
🐛 Fix resize on blocked shapes from the viewport
2025-10-16 09:17:13 +02:00
Alejandro Alonso 9266ace537 Merge pull request #7514 from penpot/ladybenko-12293-fix-scroll-inspect
🐛 Fix scrollbar in the inspect tab
2025-10-16 07:07:56 +02:00
Elena Torro e8336a401e 🐛 Do not show resize controls when a selected shape is blocked 2025-10-16 06:43:34 +02:00
Elena Torro 18048a4b2e 🐛 Do not select a blocked frame when clicking its name 2025-10-16 06:42:45 +02:00
Elena Torro e28d4eaff1 🐛 Do not allow to resize a shape that is blocked from the viewport 2025-10-16 06:42:45 +02:00
Alejandro Alonso fae574796f Merge pull request #7512 from penpot/elenatorro-11963-change-font-styles-label
🌐 Add font-style label on workspace assets
2025-10-16 06:36:39 +02:00
Belén Albeza b057ed1b9a 🐛 Fix scroll on inspect tab 2025-10-15 15:30:27 +02:00
Elena Torro db59209b21 🌐 Add font-style label on workspace assets 2025-10-15 14:41:15 +02:00
Alejandro Alonso a62f1fb46f Merge pull request #7505 from penpot/elenatorro-review-extrect
🔧 Fix extrect and selrect debug interactivity
2025-10-15 14:02:22 +02:00
Alejandro Alonso ffd2aa03a9 Merge pull request #7510 from penpot/alotor-bugfix-pan-with-guides
🐛 Fix pan cursor not disabling viewport guides
2025-10-15 13:36:12 +02:00
Xaviju f1ebcaf635 Add composite typography token to text panel (#7496) 2025-10-15 13:21:30 +02:00
alonso.torres 6a4d0f05bc 🐛 Fix pan cursor not disabling viewport guides 2025-10-15 13:02:56 +02:00
Alejandro Alonso 4d751c5acd Merge pull request #7504 from penpot/azazeln28-feat-webgl-error
🎉 Add debug and alert when WebGL is unsupported
2025-10-15 12:51:45 +02:00
Alejandro Alonso 2c5abb0cbf Merge pull request #7506 from penpot/niwinz-staging-hotfix-6-comments-threads
 Add minor comment threads queries optimization
2025-10-15 12:04:42 +02:00
Andrey Antukh 7f6bffdbfc Add minor comment threads queries optimization 2025-10-15 11:45:24 +02:00
Andrey Antukh 9a5efe8671 Merge remote-tracking branch 'origin/staging' into develop 2025-10-15 11:32:15 +02:00
Andrey Antukh e0dd8247d4 Merge branch 'main' into staging 2025-10-15 11:25:36 +02:00
Andrey Antukh 1f65e2f560 Merge branch 'staging' into develop 2025-10-15 11:22:03 +02:00
Andrey Antukh abad6a15bc Merge branch 'main' into staging 2025-10-15 11:21:43 +02:00
Alejandro Alonso bf6874a96d Merge pull request #7460 from penpot/ladybenko-12205-cap-fills-text
 Cap the amount of text fills
2025-10-15 11:17:32 +02:00
Alejandro Alonso 7cdb1925d6 Merge pull request #7497 from penpot/ladybenko-12287-text-fills
🐛 Fix adding/removing identical text fills
2025-10-15 11:00:00 +02:00
Alejandro Alonso aec4464749 Merge pull request #7498 from penpot/niwinz-staging-hotfix-4
 Make worker subsystem more resilent to redis restarts
2025-10-15 10:39:04 +02:00
Alejandro Alonso 1d14644250 Merge pull request #7494 from penpot/niwinz-staging-hotfix-3
 Use system clock for check invitation expiration
2025-10-15 10:23:48 +02:00
Alejandro Alonso fad148e6a6 📎 Reorder jvm opts on _env 2025-10-15 10:15:06 +02:00
Elena Torro 3c05067c99 🔧 Fix extrect and selrect debug interactivity 2025-10-15 10:10:24 +02:00
Aitor Moreno bbb78904fb 🎉 Add debug and alert when WebGL is unsupported 2025-10-15 10:06:02 +02:00
Alejandro Alonso 879caf66eb Merge pull request #7493 from penpot/niwinz-staging-hotfix-2
🐛 Fix corrner case on error report validation
2025-10-15 09:51:52 +02:00
Andrey Antukh 4daf086214 📎 Backport circleci config from develop 2025-10-15 09:33:42 +02:00
Andrey Antukh c8b3a41117 📎 Backport all github workflows from develop 2025-10-15 09:33:42 +02:00
Andrey Antukh c9dcc8a4ee 🐛 Add migration for clearing :objects nil from components
on the local library
2025-10-15 09:33:40 +02:00
Stephan Paternotte 4739c4730c 🌐 Add translations for: Dutch
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-14 19:08:02 +02:00
Edgars Andersons 603bb860ba 🌐 Add translations for: Latvian
Currently translated at 95.3% (1866 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-14 19:08:01 +02:00
Yaron Shahrabani 55d9ca1439 🌐 Add translations for: Hebrew
Currently translated at 99.4% (1945 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-14 19:07:59 +02:00
Oğuz Ersen a2f397c329 🌐 Add translations for: Turkish
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-14 19:07:57 +02:00
Roman D ada4e72c27 🌐 Add translations for: Russian
Currently translated at 78.2% (1530 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-14 19:07:54 +02:00
Andrey Antukh 17376dfa3f Add mark-file-as-trimmed srepl helper 2025-10-14 19:04:02 +02:00
Andrey Antukh 8d65e1cc94 Simplify reset-password srepl helper 2025-10-14 19:04:02 +02:00
Andrey Antukh d4de367499 🐛 Fix ::sm/set schema validation
It has several corner cases where set specific type
is not checked. It also now checks for ordered type
specifically when ordered is specified
2025-10-14 19:04:02 +02:00
Andrey Antukh 25521b18ff Make the restriction errors report as warning to logging 2025-10-14 19:04:02 +02:00
Andrey Antukh 39bdf026ca 🐛 Fix corrner case on error report validationt 2025-10-14 19:03:59 +02:00
Andrey Antukh 1b6a833166 🐛 Add migration for fix fills on position-data 2025-10-14 18:11:42 +02:00
Andrey Antukh 928dcf5cb8 🐛 Fix migrations set decoding on binfile import
that causes incorrect migration ordering on the table
2025-10-14 18:11:42 +02:00
Elena Torró 12e91751c3 🔧 Improve debugging UI (#7500) 2025-10-14 17:29:04 +02:00
David Barragán Merino 0f0c8466be 📎 Add bundle version to the metadata in S3 2025-10-14 16:30:31 +02:00
Belén Albeza 204d0dfb9d Add integration test to check fills limit in UI 2025-10-14 16:14:53 +02:00
Belén Albeza e4a3fc3940 Limit the amount of text fills passed to wasm 2025-10-14 16:05:03 +02:00
Andrey Antukh 5ae173f01c Make worker subsystem more resilent to redis restarts 2025-10-14 15:48:54 +02:00
Belén Albeza 840c1f59bc 🐛 Fix adding/removing identical text fills 2025-10-14 15:34:44 +02:00
Francis Santiago 29dc99deae 🔧 Replace ADD with COPY in Dockerfiles to fix hadolint warnings 2025-10-14 13:53:48 +02:00
Andrey Antukh f7b3913c71 Use system clock for check invitation expiration
instead of db time
2025-10-14 13:35:50 +02:00
alonso.torres 025f0d2fdb 🐛 Fix problem with tiles and transform 2025-10-14 12:51:48 +02:00
Andrey Antukh 85591bd579 Merge branch 'main' into staging 2025-10-14 12:30:21 +02:00
Andrey Antukh b3ae54775b 📎 Update changelog 2025-10-14 12:26:56 +02:00
Eva Marco 045aa7c788 🎉 Add tokens to color selection (#7447)
* 🎉 Add tokens to color row

* 🎉 Add color-token to stroke input

* 🐛 Fix change token on multiselection with groups

* 🎉 Create token colors on selected-colors section

* ♻️ Fix comments
2025-10-14 11:17:14 +02:00
Alejandro Alonso 497282d964 Merge pull request #7491 from penpot/niwinz-staging-fix-fills-position-data
🐛 Add migration for fix fills on position-data
2025-10-14 10:45:25 +02:00
Andrey Antukh 362bb7d2f6 🐛 Add migration for fix fills on position-data 2025-10-14 10:34:19 +02:00
Alejandro Alonso b4cd955484 🐛 Text line-height values are wrong 2025-10-14 07:53:37 +02:00
Andrey Antukh 55353b80a2 🔧 Simplfy circleci workflow dependencies 2025-10-13 18:54:47 +02:00
Andrey Antukh c46ab38d58 🔧 Add missing construction_worker to commit checker regex 2025-10-13 18:54:40 +02:00
Andrey Antukh 0f3ca67773 🔧 Simplfy circleci workflow dependencies 2025-10-13 18:53:39 +02:00
Andrey Antukh 1c06c87acf 🔧 Add missing construction_worker to commit checker regex 2025-10-13 18:53:39 +02:00
Andrey Antukh d532558bab Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 18:41:18 +02:00
Andrey Antukh a4192ce835 🐛 Fix incorrect file data migration from db to legacy-db 2025-10-13 18:41:02 +02:00
Andrey Antukh d3e28a8307 🐛 Set correct name to tokens lib data reader 2025-10-13 18:41:02 +02:00
Andrey Antukh 3122917872 Add more file-grained validation for tokens lib migration helpers 2025-10-13 18:41:02 +02:00
Andrey Antukh 95df07a364 Add strong file schema validation after file data migration 2025-10-13 18:41:02 +02:00
Andrey Antukh 71ed845307 Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 16:35:53 +02:00
Andrey Antukh c9761684c1 🐛 Fix regression introduced on duplicate-id on token-sets commit 2025-10-13 16:35:17 +02:00
Andrey Antukh dd35c82824 Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 15:19:07 +02:00
Andrey Antukh 515b381f66 🐛 Fix random failure of tokens lib test 2025-10-13 15:16:53 +02:00
Andrés Moya 160873c63e 🐛 Remove duplicated token set ids 2025-10-13 15:16:53 +02:00
Andrey Antukh fc35dc77ce 🐛 Enable migrations on calculate file library summary 2025-10-13 14:52:39 +02:00
Andrey Antukh b5648e1241 ⬆️ Update yarn to 2.10.3 on frontend module 2025-10-13 14:52:39 +02:00
Andrey Antukh d07e00da21 🐛 Fix incorrect filtering of unread comment threads
Do not return threads for deleted files
2025-10-13 14:52:39 +02:00
Xaviju 253605f6cc 🎉 Add text panel to inspect styles tab (#7413) 2025-10-13 14:20:55 +02:00
Xaviju 2548bec651 🐛 Fix visual glitch in background for swatch component (#7468) 2025-10-13 14:20:14 +02:00
Andrey Antukh da5da00bd4 📚 Update changelog 2025-10-13 14:08:33 +02:00
Andrey Antukh 72b44240b1 Merge branch 'develop' into staging 2025-10-13 13:30:43 +02:00
Andrey Antukh b21f79490b Merge remote-tracking branch 'origin/staging' into develop 2025-10-13 13:22:26 +02:00
Andrey Antukh db09eacd4c Merge remote-tracking branch 'origin/main' into staging 2025-10-13 13:22:07 +02:00
Andrey Antukh 731afb0e70 📚 Update changelog 2025-10-13 13:19:18 +02:00
Pablo Alba 1080251d9a Add missing variant-switch tracking event 2025-10-13 13:01:02 +02:00
Andrey Antukh ea5bfbd72d 📎 Add token-typography-composite config flag to defaults 2025-10-13 12:54:27 +02:00
David Barragán Merino b10dcb2d63 🐳 Prevent error if config.js is a bind mounted file 2025-10-13 12:42:23 +02:00
Andrey Antukh c4cd665594 📎 Enable redis-cache flag on devenv start scripts 2025-10-13 12:32:29 +02:00
Andrey Antukh 1eb6f33bdd Enable optional caching of results for file summary RPC methods 2025-10-13 12:32:29 +02:00
Andrey Antukh 62dffd30a4 ♻️ Refactor redis internal API
The main idea behind this refactor is make the
API less especialized for specific use of out internal
submidules and make it more general and usable
for more general purposes (per example cache)
2025-10-13 12:32:29 +02:00
Andrey Antukh 12a4934c41 Allow pass :load-data? false to get-file 2025-10-13 12:32:29 +02:00
Andrey Antukh e3bd9148f2 Add ::sm/atom to schemas 2025-10-13 12:32:29 +02:00
Alejandro Alonso 2b7bd8fa5c 🐛 Fix deleted files are accesible 2025-10-13 12:24:05 +02:00
Andrey Antukh 5717708b56 ♻️ Refactor file storage
Make it more scallable and make it easily extensible
2025-10-13 12:24:05 +02:00
Andrey Antukh 27bed84543 Improve netty io executor shutdown 2025-10-13 12:24:05 +02:00
Andrey Antukh c6529f9585 🐛 Fix corner case on worker runner 2025-10-13 12:24:05 +02:00
Andrey AntukhandBelén Albeza 30e139ed10 🔥 Remove binary fills flag (#7462)
* 🐛 Add missing IEmptyableCollection protocol impl for wasm Shape

* 🔥 Remove frontend-binary-fills flag

*  Fix fill-limit integration tests

---------

Co-authored-by: Belén Albeza <belen@hey.com>
2025-10-13 12:14:25 +02:00
Andrey Antukh 0aadc3b6b3 Add management shared key authentication 2025-10-13 11:49:58 +02:00
Andrey Antukh 21a7ecb3fe 🔥 Remove obsolete code with object wrapping on components 2025-10-13 11:36:22 +02:00
Andrey Antukh 65a2b10875 Make component path mandatory on validations
And ensure it already present with a migration
2025-10-13 11:36:22 +02:00
Xavier Julian 9e676a7ab2 🐛 Fix unnecesarily overcomplated key generation 2025-10-13 11:30:47 +02:00
Xavier Julian 3d1933411b 🐛 Fix broken layout when multiple fill/strokes with tokens 2025-10-13 11:30:47 +02:00
VKing9 a44f1bb09c 🌐 Add translations for: Hindi
Currently translated at 92.2% (1805 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-10-13 11:26:55 +02:00
Henrik Allberg 65e59c8857 🌐 Add translations for: Swedish
Currently translated at 81.5% (1595 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-10-13 11:26:55 +02:00
Црнобог 2833854d8d 🌐 Add translations for: Serbian
Currently translated at 70.3% (1377 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2025-10-13 11:26:54 +02:00
Alejandro Alonso 19e367e112 🌐 Add translations for: Yoruba
Currently translated at 60.3% (1181 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-10-13 11:26:54 +02:00
Alejandro Alonso 3f731f57e6 🌐 Add translations for: Hausa
Currently translated at 63.7% (1247 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2025-10-13 11:26:54 +02:00
Stephan Paternotte b740ee254e 🌐 Add translations for: Dutch
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-13 11:26:53 +02:00
Sebastiaan Pasma 97d9480c8b 🌐 Add translations for: Dutch
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-13 11:26:53 +02:00
Edgars Andersons f1fcc77f74 🌐 Add translations for: Latvian
Currently translated at 94.6% (1852 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:26:52 +02:00
Ņikita K d078c49fe7 🌐 Add translations for: Latvian
Currently translated at 94.6% (1852 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:26:52 +02:00
Denys Kisil 9d30a1c1e9 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 93.4% (1827 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-10-13 11:26:52 +02:00
Zvonimir Juranko 3b9f732b16 🌐 Add translations for: Croatian
Currently translated at 82.2% (1608 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-10-13 11:26:51 +02:00
Dário 0a9c191582 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 80.6% (1578 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-10-13 11:26:51 +02:00
Amerey.eu cc6175d39c 🌐 Add translations for: Czech
Currently translated at 81.9% (1603 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-13 11:26:50 +02:00
Mikel Larreategi b424c0f84b 🌐 Add translations for: Basque
Currently translated at 59.3% (1161 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/
2025-10-13 11:26:50 +02:00
Radek Sawicki 29495474b1 🌐 Add translations for: Polish
Currently translated at 58.0% (1136 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/
2025-10-13 11:26:49 +02:00
Nicola Bortoletto 7c0bd4ac9a 🌐 Add translations for: Italian
Currently translated at 99.4% (1945 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-13 11:26:49 +02:00
william chen b98f5a9851 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 82.2% (1608 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-10-13 11:26:48 +02:00
Yaron Shahrabani 4d823af46d 🌐 Add translations for: Hebrew
Currently translated at 99.2% (1942 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-13 11:26:48 +02:00
Linerly ecec2db29e 🌐 Add translations for: Indonesian
Currently translated at 87.2% (1706 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-10-13 11:26:47 +02:00
Alejandro Alonso 55d2d53a22 🌐 Add translations for: Arabic
Currently translated at 57.0% (1115 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-10-13 11:26:46 +02:00
Mahmoud A. Rabo 86f7bec171 🌐 Add translations for: Arabic
Currently translated at 57.0% (1115 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-10-13 11:26:45 +02:00
AlexTECPlayz 5c6d296e60 🌐 Add translations for: Romanian
Currently translated at 65.6% (1284 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-13 11:26:45 +02:00
George Lemon bda6c61a11 🌐 Add translations for: Romanian
Currently translated at 65.6% (1284 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-10-13 11:26:44 +02:00
Allan Nordhøy 3aa966e553 🌐 Add translations for: Norwegian Bokmål
Currently translated at 8.3% (164 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nb_NO/
2025-10-13 11:26:44 +02:00
Marius 9ce0b9c86e 🌐 Add translations for: German
Currently translated at 89.0% (1741 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-13 11:26:43 +02:00
Pablo Alba 42ab42fb56 🌐 Add translations for: German
Currently translated at 89.0% (1741 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-13 11:26:43 +02:00
Hugo Figueira 7b60d386fb 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 64.9% (1271 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-10-13 11:26:42 +02:00
Anonymous ae60a7260c 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.6% (1813 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-13 11:26:42 +02:00
Çağlar Yeşilyurt bfacdc414f 🌐 Add translations for: Turkish
Currently translated at 93.7% (1833 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-13 11:26:42 +02:00
Vint Prox 29628eea0a 🌐 Add translations for: Russian
Currently translated at 73.1% (1430 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-13 11:26:41 +02:00
The_BadUser 6d7723c36b 🌐 Add translations for: Russian
Currently translated at 73.1% (1430 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-13 11:26:40 +02:00
Anonymous 710008ee9e 🌐 Add translations for: Greek
Currently translated at 26.1% (511 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/el/
2025-10-13 11:26:40 +02:00
Pablo Alba a15be5c2d0 🌐 Add translations for: French
Currently translated at 94.7% (1853 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-13 11:26:40 +02:00
Aryiu 45a09928b3 🌐 Add translations for: Catalan
Currently translated at 54.8% (1073 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ca/
2025-10-13 11:26:39 +02:00
Hosted Weblate bae5196d1e 🌐 Merge branch 'origin/develop' into Weblate. 2025-10-13 11:24:12 +02:00
Edgars Andersons 91fe7b2dd6 🌐 Add translations for: Latvian
Currently translated at 94.8% (1856 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-13 11:23:18 +02:00
Oğuz Ersen df6448e32e 🌐 Add translations for: Turkish
Currently translated at 93.9% (1837 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-13 11:23:16 +02:00
Luis de Dios a9a9245ab6 🐛 Fix component number has no singular translation string (#7469) 2025-10-13 11:22:25 +02:00
Eva Marco e18f20666b 🔧 Add scss refactor as element on PR checklist (#7480) 2025-10-13 11:21:12 +02:00
Eva Marco adafe0648c ♻️ Fix component schemas (#7481) 2025-10-13 11:20:43 +02:00
Andrey Antukh 1b9deecefc Make the binfile import process more resilient (#7464)
on small inconsistencies on file media object references
2025-10-13 11:13:10 +02:00
Nicola Bortoletto 666410602b 🌐 Add translations for: Italian
Currently translated at 99.6% (1949 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-11 07:07:23 +02:00
Stephan Paternotte c956600d64 🌐 Add translations for: Dutch
Currently translated at 99.8% (1953 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-08 18:07:31 +00:00
Edgars Andersons 2100c8a115 🌐 Add translations for: Latvian
Currently translated at 94.5% (1850 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-08 18:07:29 +00:00
Yaron Shahrabani d4fd246622 🌐 Add translations for: Hebrew
Currently translated at 99.4% (1946 of 1956 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-08 18:07:25 +00:00
David Barragán Merino 71fd6640af 👷 Automate publication of docker images in a new release 2025-10-08 17:15:10 +02:00
Alonso Torres 40e9a78f67 Add telemetry for events performance measures (#7457) 2025-10-08 14:03:09 +02:00
Alejandro Alonso 551a25661f Improve ancestors modifiers performance 2025-10-08 12:10:18 +02:00
Luis de Dios 544bedf7c2 🎉 Reorder properties for a component (#7429)
* 🎉 Reorder properties when a component with variants is selected

* 🎉 Reorder properties when a single variant is selected

* ♻️ Refactor SCSS and component structure

* 📚 Update changelog

* 📎 PR changes (styling)

* 📎 PR changes (functionality)
2025-10-08 11:27:01 +02:00
Andrey Antukh 4937580585 🌐 Rehash and validate translation files 2025-10-07 18:38:28 +02:00
VKing9 dba0f11670 🌐 Add translations for: Hindi
Currently translated at 92.6% (1810 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-10-07 18:38:28 +02:00
Henrik Allberg 776af8ea22 🌐 Add translations for: Swedish
Currently translated at 81.7% (1597 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-10-07 18:38:28 +02:00
Црнобог 38e0d0035f 🌐 Add translations for: Serbian
Currently translated at 70.6% (1380 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/
2025-10-07 18:38:28 +02:00
Anonymous f0c01d8714 🌐 Add translations for: Yoruba
Currently translated at 60.5% (1184 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-10-07 18:38:28 +02:00
Anonymous 678c4acdbc 🌐 Add translations for: Igbo
Currently translated at 26.4% (516 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2025-10-07 18:38:28 +02:00
Anonymous 9b76048c2f 🌐 Add translations for: Malay
Currently translated at 34.6% (678 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ms/
2025-10-07 18:38:28 +02:00
Anonymous 2cf98745d7 🌐 Add translations for: Hausa
Currently translated at 63.9% (1250 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2025-10-07 18:38:28 +02:00
Stephan Paternotte 267acc5b80 🌐 Add translations for: Dutch
Currently translated at 97.1% (1898 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-10-07 18:38:28 +02:00
Anonymous ae38c8e840 🌐 Add translations for: Latvian
Currently translated at 94.6% (1850 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-07 18:38:27 +02:00
Edgars Andersons 1128303fa1 🌐 Add translations for: Latvian
Currently translated at 94.6% (1850 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-10-07 18:38:27 +02:00
Denys Kisil 5d1981047d 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 93.5% (1828 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-10-07 18:38:27 +02:00
al0cam 0dae2a3c24 🌐 Add translations for: Croatian
Currently translated at 82.3% (1610 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-10-07 18:38:27 +02:00
TheScientistPT 766accde29 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 80.8% (1580 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-10-07 18:38:27 +02:00
Anonymous c5f8bd5bea 🌐 Add translations for: Czech
Currently translated at 82.1% (1605 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-07 18:38:27 +02:00
Amerey.eu 4edfcba350 🌐 Add translations for: Czech
Currently translated at 82.1% (1605 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-10-07 18:38:27 +02:00
Nicola Bortoletto ce20059a4c 🌐 Add translations for: Italian
Currently translated at 96.9% (1895 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-10-07 18:38:27 +02:00
william chen 27fa0c0721 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 82.3% (1610 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-10-07 18:38:27 +02:00
Yaron Shahrabani ee83a07674 🌐 Add translations for: Hebrew
Currently translated at 96.7% (1891 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-10-07 18:38:27 +02:00
Linerly 57123569eb 🌐 Add translations for: Indonesian
Currently translated at 87.4% (1708 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-10-07 18:38:27 +02:00
Anonymous d112e83b0d 🌐 Add translations for: German
Currently translated at 89.2% (1743 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-07 18:38:27 +02:00
Stas Haas b259ca2cd1 🌐 Add translations for: German
Currently translated at 89.2% (1743 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-10-07 18:38:27 +02:00
Rick Benetti 56db7078ae 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 65.1% (1273 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-10-07 18:38:27 +02:00
Anonymous 7a75002cc9 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
DoubleCat 77ad1f57be 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
Jun Fang 9f881e49e5 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 92.9% (1817 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-10-07 18:38:27 +02:00
Anonymous 238578f243 🌐 Add translations for: Turkish
Currently translated at 73.1% (1429 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-10-07 18:38:27 +02:00
The_BadUser 49d9dd7161 🌐 Add translations for: Russian
Currently translated at 73.2% (1432 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-07 18:38:27 +02:00
Vin be28a310f7 🌐 Add translations for: Russian
Currently translated at 73.2% (1432 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-10-07 18:38:27 +02:00
Anonymous 460aafbedf 🌐 Add translations for: French
Currently translated at 94.8% (1854 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-07 18:38:27 +02:00
Corentin Noël 9ba995edd9 🌐 Add translations for: French
Currently translated at 94.8% (1854 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-10-07 18:38:27 +02:00
Deleted User 85b15e4896 🌐 Add translations for: Spanish
Currently translated at 97.0% (1896 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-10-07 18:38:27 +02:00
Anonymous 1420e8a59c 🌐 Add translations for: Spanish
Currently translated at 97.0% (1896 of 1954 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-10-07 18:38:27 +02:00
Hosted Weblate 3aa647e1f6 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-10-07 18:38:27 +02:00
Pablo AlbaandAndrey Antukh 11b7b458bf 🐛 Auto-width changes to fixed when switching variants (#7449)
* 🐛 Auto-width changes to fixed when switching variants

* 📎 Fix typo on changes

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-10-07 18:29:18 +02:00
Andrey Antukh cfa607f57f Merge remote-tracking branch 'weblate/develop' into develop 2025-10-07 18:23:36 +02:00
Andrey Antukh cea0143327 Add extra tenant validation for virtual clock dbg pannel 2025-10-07 18:21:13 +02:00
Andrey Antukh b8158ffec8 Merge remote-tracking branch 'origin/staging' into develop 2025-10-07 16:03:17 +02:00
Pablo Alba 640894acd8 🐛 Fix Restoring a variant from another file makes it overlap (#7448) 2025-10-07 16:01:59 +02:00
Alejandro Alonso 90bfae3ec1 Merge pull request #7456 from penpot/elenatorro-12248-fix-shadows-order
🔧 Always return shadows in reverse order
2025-10-07 13:16:49 +02:00
Alejandro Alonso 73ed5f8bc5 Merge pull request #7402 from penpot/niwinz-develop-enhancements-3
 Add additional http middlewares
2025-10-07 13:00:30 +02:00
Andrey Antukh 2c1a8b59ba Add client header check middleware
As an additional csrf protection for API requests
2025-10-07 12:47:14 +02:00
Andrey Antukh 47d9c6f282 Add sec-fetch metadata middleware support 2025-10-07 12:47:14 +02:00
Andrey Antukh 14d53c224f 🔥 Remove unused auth-data cookie asignation 2025-10-07 12:47:12 +02:00
Elena Torró a1b8eb7074 Merge pull request #7454 from penpot/elenatorro-12247-fix-line-height-min-value
🔧 Allow line height values from 0 to 1
2025-10-07 12:29:16 +02:00
Alejandro Alonso e0f9bbb23f Merge pull request #7432 from penpot/niwinz-develop-virtual-clock
🎉 Add virtual clock support
2025-10-07 12:27:49 +02:00
Elena Torro 9b16a6bbd1 🔧 Always return shadows in reverse order 2025-10-07 12:26:51 +02:00
Marina López fede63ac0b Update Design System template in carousel 2025-10-07 12:14:12 +02:00
Eva Marco ea1ab7c23b 🐛 Fix modal title on edit token (#7453)
* 🐛 Fix modal title on edit token

* 🐛 Fix font size on numeric input

* ♻️ Update color token border on hover

* ♻️ Use swatch component on token pill
2025-10-07 12:11:40 +02:00
Andrey Antukh 61d9b57bc7 ♻️ Refactor internal tokens API
Mainly make it receive the whol cfg/system instead only props. This
makes the api more flexible for a future extending without the need
to change the api again.
2025-10-07 12:08:00 +02:00
Andrey Antukh bd63598185 🎉 Add virtual clock implementation 2025-10-07 12:08:00 +02:00
Eva Marco 31af6aebbd 🐛 Fix swatch error on text without fills (#7451) 2025-10-07 10:08:46 +02:00
Andrey Antukh cc5f86bc84 🐛 Set correct path if is not provided on sdk addComponent method 2025-10-07 09:57:41 +02:00
Elena Torro 68cd7075c0 🔧 Allow line height values from 0 to 1 2025-10-07 09:28:24 +02:00
Andrey Antukh 11ff64b362 Merge tag '2.10.1-RC3' 2025-10-06 12:12:27 +02:00
Andrey Antukh 57a7b5b1da Merge remote-tracking branch 'origin/staging' into develop 2025-10-06 12:12:05 +02:00
Andrey Antukh cf24bdd7a8 Merge remote-tracking branch 'origin/main' into staging 2025-10-06 12:10:44 +02:00
Andrey Antukh 683db071d6 Merge remote-tracking branch 'origin/staging' into develop 2025-10-06 12:09:57 +02:00
Andrey Antukh d3943b9162 Merge branch 'niwinz-staging-library-changes' into staging 2025-10-06 11:59:17 +02:00
Andrey Antukh 613acd5b29 📎 Update version on library/package.json file 2025-10-06 11:56:23 +02:00
Andrey Antukh 987dea8048 📚 Update changelog 2025-10-06 11:53:50 +02:00
Andrey Antukh 6b0d0a302f Enable variant attrs on SDK addComponent method 2025-10-06 11:53:50 +02:00
Andrey Antukh 588eb0b4fa ⬆️ Update shadow-cljs dependency on sdk 2025-10-06 11:53:50 +02:00
Andrey Antukh b30cb0e084 Allow pass variant related attrs on add-component change 2025-10-06 11:53:50 +02:00
Andrey Antukh 9244501c6e Add variants/v1 feature to default features emited by sdk 2025-10-06 11:53:50 +02:00
Alonso Torres d5b743c604 🐛 Fix problem with text in plugins (#7446) 2025-10-06 11:19:33 +02:00
Andrey Antukh e38dd21307 ⬆️ Update exported dependencies 2025-10-06 10:56:04 +02:00
Andrey Antukh 8c91109c63 📚 Update changelog 2025-10-06 09:38:01 +02:00
Andrey Antukh c3eabbdb25 📎 Enable the fdata/objects-map feature by default
Using config flags
2025-10-06 09:38:01 +02:00
Andrey Antukh 67661674e2 Make deleted fonts fixer to run with more granular stragegy
Instead of running it on all the file, only run it to local library
and the current page, reducing considerably the overhead of analyzing
the whole file on each file load.

It stills executes for page each time the page is loaded, and add
some kind of local cache for not doing repeated work each time page
loads is pending to be implemented in other commit.
2025-10-06 09:38:01 +02:00
Andrey Antukh c70e7f3876 Add logging to frontend repo namespace 2025-10-06 09:38:01 +02:00
Andrey Antukh 0295f0f7c8 Add better workspace file indexing strategy
Improve file indexes initialization on workspace.

Instead of initialize indexes for all pages only initialize
indexes for the loaded page.
2025-10-06 09:38:01 +02:00
Andrey Antukh 54bb879cb6 📎 Add several missing imports on repl related namespaces 2025-10-06 09:38:01 +02:00
Andrey Antukh b72704e54b Set explicit no-buffering for sse responses 2025-10-06 09:19:48 +02:00
Natacha 362a31dd22 Add typography composite token to changelog (#7434)
Signed-off-by: Natacha <natachamenjibar@gmail.com>
2025-10-06 09:17:29 +02:00
Yamila Moreno 6c6ec7a620 Merge pull request #7433 from penpot/yms-improve-self-host-documentation
📚 Improve self-host documentation
2025-10-03 13:24:37 +02:00
Yamila Moreno 21a7d30c5e 📚 Improve self-host documentation 2025-10-03 13:21:06 +02:00
Andrés Moya 52fef6c318 🐛 Fix sync errors when there is a broken swap slot 2025-10-03 12:51:33 +02:00
Eva Marco fc8029abf7 🐛 Fix some errors from reviews (#7421)
* 🐛 Fix errors con colorpicker

* 🐛 Fix modal size

* 🐛 Fix form padding

* 🐛 Fix edit modal title

* 🐛 Fix resolved value message

* 🐛 Fix CI
2025-10-03 11:25:56 +02:00
Eva Marco 44f6c2f83c 🎉 Show tokens on color inputs (#7377)
* 🎉 Add tokens to color row

* 🎉 Add color-token to stroke input

* 🐛 FIx change token on multiselection with groups

* 🔧 Add config flag

* 🐛 Fix comments
2025-10-03 11:19:01 +02:00
Eva Marco a4f20564af 🐛 Fix numeric input errors detected on review (#7427)
* 🐛 Fix review errors

* 📚 Add docs for numeric input component

* 🐛 Sort tokens alphabetically
2025-10-03 10:14:25 +02:00
Eva Marco 93d4b19477 🐛 Fix shadow inputs (#7426) 2025-10-03 10:13:27 +02:00
Aitor Moreno 7dd26dee13 Merge pull request #7428 from penpot/elenatorro-fix-shadows-order
🐛 Fix shadows order
2025-10-02 17:16:58 +02:00
Elena Torró 4594635009 Merge pull request #7399 from penpot/ladybenko-12164-handle-font-404
🐛 Fix internal error when fonts return 404 (wasm)
2025-10-02 16:42:58 +02:00
Elena Torro 7e852cb3ac 🐛 Fix shadows order 2025-10-02 16:40:20 +02:00
Belén Albeza 6e82b0f1ba 🐛 Fix shadow serialization (#7423) 2025-10-02 15:17:01 +02:00
Florian Schroedl 472148ff9d 🐛 Fix empty values re-triggering validation 2025-10-02 14:31:55 +02:00
Florian Schroedl d01df7738a ♻️ Extract composite component wrapper 2025-10-02 14:31:55 +02:00
Xaviju 73222f22d0 🎉 Add stroke panel to inspect styles tab (#7408) 2025-10-02 13:58:08 +02:00
Andrey Antukh b90aba0f95 Merge tag '2.10.0' 2025-10-02 12:37:58 +02:00
Florian Schroedl 17fe012f7e 🐛 Fix form not being saveable when editing composite token and switching tabs 2025-10-02 10:48:14 +02:00
Belén Albeza 60f45d1fd7 🐛 Fix internal error crash when attempting to download a font resource that returns 404 2025-10-02 09:58:38 +02:00
alonso.torres 979b4276ca 🐛 Fix problem with component swapping panel 2025-10-02 09:10:21 +02:00
Elena Torró a32fe40528 Merge pull request #7409 from penpot/ladybenko-fix-wasm-playwright-ci
🔧 Fix Playwright config in CI to include the wasm build
2025-10-02 09:03:12 +02:00
Natacha b602df549e Add new shadow icons (#7416)
*  Adds new shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

*  Add shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

*  Adds shadow icons

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

* 📎 Fix wrong svg

Signed-off-by: Natacha <natachamenjibar@gmail.com>

---------

Signed-off-by: Natacha <natachamenjibar@gmail.com>
2025-10-01 17:11:19 +02:00
Luis de Dios 7f1ab08ec8 🐛 Fix use a pointer cursor for adding variant from the viewport (#7410) 2025-10-01 17:01:07 +02:00
Luis de Dios 1263ea11fa 🐛 Fix order of component menu options in assets tab (#7388)
* 🐛 Reorder component menu options in assets tab

* ♻️ Use new component syntax

* 📚 Add bugfix to changelog

* ♻️ Code restructuring and SCSS improvements
2025-10-01 17:00:27 +02:00
Yamila Moreno ce26c52b30 👷 Automate docker images creation 2025-10-01 14:54:00 +02:00
Yamila Moreno 5c8b3ac3d6 👷 Automate docker images creation 2025-10-01 13:41:03 +02:00
Aitor Moreno bd4d576172 Merge pull request #7412 from penpot/elenatorro-fix-loop-all-ancestors
🐛 Break loop when no parent is present
2025-10-01 13:36:16 +02:00
Elena Torro e10169b3db 🐛 Break loop when no parent is present 2025-10-01 12:43:56 +02:00
Elena Torró f119a9548d Merge pull request #7411 from penpot/azazeln28-fix-issue-12185-wrong-text-width-height-layout
🐛 Fix wrong text auto width/height layout
2025-10-01 12:40:58 +02:00
Aitor Moreno c097aef152 🐛 Fix wrong text auto width/height layout 2025-10-01 12:27:38 +02:00
Andrey Antukh 000fa51c73 🐛 Fix zip handling on exporter 2025-10-01 11:56:57 +02:00
Belén Albeza d815494ffa 🔧 Fix playwright config to do a wasm build 2025-10-01 11:27:13 +02:00
Andrey Antukh a25ba6b482 📎 Fix incorrect regex for match merge and revert commits 2025-10-01 11:07:21 +02:00
Andrey Antukh e8434c3370 📎 Update devenv tmux script to start exporter using yarn 2025-10-01 10:59:41 +02:00
Andrey Antukh 7cf4ec2792 ♻️ Make the exporter build as esm module 2025-10-01 10:58:03 +02:00
Andrey Antukh 365ce25996 Merge remote-tracking branch 'origin/staging' into develop 2025-10-01 10:50:19 +02:00
Andrey Antukh 01ef55e4f4 Revert " Add minor improvement to cljs impl logging"
This reverts commit 960b76f760.
2025-10-01 10:48:24 +02:00
Andrey Antukh 3b81c1d750 Revert "♻️ Make the exporter build as esm module"
This reverts commit d0f34f06a9.
2025-10-01 10:47:47 +02:00
Elena Torró 40b34da788 Merge pull request #7269 from penpot/azazeln28-feat-caret-position
🎉 Feat caret position
2025-10-01 09:43:03 +02:00
Aitor Moreno 732c79b7b5 🎉 Add function to retrieve caret position 2025-10-01 09:18:46 +02:00
Andrey Antukh d0f34f06a9 ♻️ Make the exporter build as esm module 2025-10-01 08:10:37 +02:00
Andrey Antukh 23d5bdd20b 🐛 Add missing poppler-tools dependency on devenv 2025-10-01 08:10:37 +02:00
Andrey Antukh 9f2dc06c95 Add missing srepl helper for disable objects-map feat 2025-10-01 08:10:37 +02:00
Andrey Antukh 62563d28d0 📎 Bump library version to 1.0.9
Mainly fixes dependencies declaration on package.json file
2025-09-30 21:55:17 +02:00
Andrey Antukh 21e2ee9904 🐛 Fix dependencies on library 2025-09-30 21:53:04 +02:00
brian mwenda e6c418eb9c 🐛 Improve auto-width to fixed conversion logic in layout contexts
Signed-off-by: Brian Mwenda <brian@nathandigital.com>
2025-09-30 21:48:03 +02:00
Luis de Dios de5ff227d2 🎉 Create variant from the viewport (#7357)
* 🎉 Create variant from the viewport

* ♻️ Use DS styles and new component syntax

* 📎 PR changes
2025-09-30 18:15:17 +02:00
Florian Schroedl 0f67730198 🐛 Dont forward default-value for mismatching tab-type 2025-09-30 14:27:40 +02:00
Florian Schroedl 3da02e2b6b 🐛 Fixes resolved values being prefilled for existing referenced composite token 2025-09-30 14:27:40 +02:00
Florian Schroedl ab80021fb1 🐛 Fix performance issue on font-family 2025-09-30 14:27:40 +02:00
Xaviju f31e9b8ac9 🎉 Add blur panel to inspect styles tab (#7397) 2025-09-30 13:08:52 +02:00
Andrey Antukh 7d16515eb7 Add minor enhacements to logging on frontend (#7401)
*  Add logging consistency enhacements on fonts loading

*  Disable data evens ns logging

*  Simplify flags logging on application initialization

*  Improve features logging
2025-09-30 11:59:41 +02:00
Pablo Alba cd9ba482e3 🐛 Load dependant libraries, and don't allow unload them 2025-09-30 09:55:21 +02:00
David Barragán Merino dff1ca23d3 📚 Update changelog 2025-09-29 18:08:28 +02:00
Andrey Antukh c363d4d937 📎 Bump library version 2025-09-29 13:44:14 +02:00
Andrey Antukh de25a24a6d 🐛 Fix backend repl start issue with jdk 24 2025-09-29 13:35:48 +02:00
Andrey Antukh accc9a173f Merge remote-tracking branch 'origin/staging' into develop 2025-09-29 13:24:31 +02:00
Andrey Antukh 2d364dde5c Add several minor enhacements to features subsystem
Mainly fixes the team non-inheritable features handling and
removes unnecesary/duplicate checks.
2025-09-29 13:23:16 +02:00
Andrey Antukh c892a9f254 Integrate objects-map usage on backend and frontend 2025-09-29 13:23:16 +02:00
Andrey Antukh aaae35fb51 🎉 Add multiplatform impl of ObjectsMap
The new type get influentiated by the ObjectsMap impl on backend
code but with simplier implementation that no longer restricts keys
to UUID type but preserves the same performance characteristics.

This type encodes and decodes correctly both in fressian (backend)
and transit (backend and frontend).

This is an initial implementation and several memory usage
optimizations are still missing.
2025-09-29 13:23:16 +02:00
Andrey Antukh 960b76f760 Add minor improvement to cljs impl logging
Mainly reduce the emmited code, that will contribute to reduce the
bundle size and also adds timestamp to the default output.
2025-09-29 13:23:16 +02:00
Andrey Antukh d921e7eaa3 📎 Add not-empty generator to schema generator ns 2025-09-29 13:23:16 +02:00
Andrey Antukh 49f06b25fa 📚 Update changelog 2025-09-29 13:23:01 +02:00
Andrey Antukh 5ffb7ae2ec Add warning on using deprecated storage config 2025-09-29 13:23:01 +02:00
Andrey Antukh 27945ace65 Revert deprecated storage config cleaning 2025-09-29 13:23:01 +02:00
María ValderramaandEva Marco e39bf0b439 Invitations management improvements (#7230)
*  Invitations management improvements

* 📎 Change invite email subject

* 📎 Update icon usage

* ♻️ Fix css file

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
2025-09-29 13:18:57 +02:00
Alonso Torres deee7f7334 Merge pull request #7366 from penpot/niwinz-develop-page-data-type
 Add several enhancements for reduce workspace file load time
2025-09-29 12:43:34 +02:00
Xaviju 20d61cbce2 Create ghost variant for select DS component (#7392) 2025-09-29 12:24:20 +02:00
Andrés Moya 9ad8d3fd08 🔧 Make small improvements from PR comments 2025-09-29 12:16:42 +02:00
Andrés Moya 4c35571336 🔧 Read and modify token themes by id 2025-09-29 12:16:42 +02:00
Andrés Moya 37679b7ec6 🔧 Organize token changes API 2025-09-29 12:16:42 +02:00
Andrés Moya 194eded930 🔧 Unify path name helper functions 2025-09-29 12:16:42 +02:00
Andrés Moya 4e607d8da2 💄 Clarify and reorder interfaces 2025-09-29 12:16:42 +02:00
Andrés Moya f5fd978a07 🔧 Retrieve tokens from library and not from set 2025-09-29 12:16:42 +02:00
Andrés Moya b28be62845 🔧 Fix rebase problems 2025-09-29 12:16:42 +02:00
Andrés Moya d76a5c615c 🔧 Modify token sets by id instead of name and review usage 2025-09-29 12:16:42 +02:00
Andrés Moya 03e05da41e 💄 Normalize some attributes of changes 2025-09-29 12:16:42 +02:00
Andrés Moya 5f886e141a 💄 Minor changes 2025-09-29 12:16:42 +02:00
Andrés Moya 021b8f81ca 🔧 Read token sets by id instead of name 2025-09-29 12:16:42 +02:00
Andrey Antukh f32112544e Make deleted fonts fixer to run with more granular stragegy
Instead of running it on all the file, only run it to local library
and the current page, reducing considerably the overhead of analyzing
the whole file on each file load.

It stills executes for page each time the page is loaded, and add
some kind of local cache for not doing repeated work each time page
loads is pending to be implemented in other commit.
2025-09-29 12:07:49 +02:00
Andrey Antukh 27e311277a Add logging to frontend repo namespace 2025-09-29 12:07:49 +02:00
Andrey Antukh b9030fcc73 Add better workspace file indexing strategy
Improve file indexes initialization on workspace.

Instead of initialize indexes for all pages only initialize
indexes for the loaded page.
2025-09-29 12:07:49 +02:00
Andrey Antukh e1519f0ee4 Integrate objects-map usage on backend and frontend 2025-09-29 12:07:48 +02:00
Andrey Antukh 7fefe6dbc8 🎉 Add multiplatform impl of ObjectsMap
The new type get influentiated by the ObjectsMap impl on backend
code but with simplier implementation that no longer restricts keys
to UUID type but preserves the same performance characteristics.

This type encodes and decodes correctly both in fressian (backend)
and transit (backend and frontend).

This is an initial implementation and several memory usage
optimizations are still missing.
2025-09-29 12:06:56 +02:00
Andrey Antukh fdf70ae9c1 Fix docstring on common.weak ns function 2025-09-29 12:06:56 +02:00
Andrey Antukh 528315b75c 📎 Add not-empty generator to schema generator ns 2025-09-29 12:06:56 +02:00
Andrey Antukh 42d03a0325 📎 Add several missing imports on repl related namespaces 2025-09-29 12:06:56 +02:00
Andrey Antukh 0346c48b03 Add several minor enhacements to features subsystem
Mainly fixes the team non-inheritable features handling and
removes unnecesary/duplicate checks.
2025-09-29 12:06:56 +02:00
Andrey Antukh 1d54fe2e24 Add support for emit messages without waiting response on worker 2025-09-29 12:06:56 +02:00
Andrey Antukh 255f5af2e3 Add several enhacements to buffer namespace
The changes are just for completenes.
2025-09-29 12:06:56 +02:00
Andrey Antukh eea65b12dd Add minor improvement to cljs impl logging
Mainly reduce the emmited code, that will contribute to reduce the
bundle size and also adds timestamp to the default output.
2025-09-29 12:06:56 +02:00
Andrey Antukh d4b7f231c7 🔧 Add missing config for :rewind: on commit checker 2025-09-29 12:05:09 +02:00
Andrey Antukh 473066cf5c 🔧 Add missing config for :rewind: on commit checker 2025-09-29 12:04:37 +02:00
andrés gonzález d1607fbe54 💄 Update Help Center images (#7266) 2025-09-29 11:54:47 +02:00
Xaviju 5e84bda404 🎉 Add SVG panel to inspect styles tab (#7373) 2025-09-29 09:53:15 +02:00
Andrey Antukh c1058c7fdb ♻️ Add minor refactor for internal concurrency model
Replace general usage of virtual threads with platform threads
and use virtual threads for lightweight procs such that websocket
connections. This decision is made mainly because virtual threads
does not appear on thread dumps in an easy way so debugging issues
becomes very difficult.

The threads requirement of penpot for serving http requests
is not very big so having so this decision does not really affects
the resource usage.
2025-09-26 14:35:06 +02:00
Andrey Antukh 9d907071aa ⬆️ Update dependencies (#7330)
* ⬆️ Update to JDK25 on the devenv

* ⬆️ Update dependencies

* 🔥 Remove unused flag from devenv backend startup scripts

*  Enable shenandoah gc on backend scripts/repl
2025-09-26 13:43:43 +02:00
Elena Torró c32b94abcf Merge pull request #7343 from penpot/elenatorro-12118-support-large-svg-files
🐛 Fix parsing large paths with multiple subpaths
2025-09-26 13:35:17 +02:00
Elena Torro 9d8ad0ea6e 🐛 Fix parsing large paths with multiple subpaths 2025-09-26 13:04:47 +02:00
Yamila Moreno 2b1e107a44 Merge pull request #7390 from penpot/yms-add-curl-dependency
🐳 Add curl to the backend image
2025-09-26 11:40:42 +02:00
Yamila Moreno 2196318cfc 🐳 Add curl to the backend image 2025-09-26 11:23:02 +02:00
Yamila Moreno b3d1701698 Merge pull request #7355 from penpot/yms-docker-update-nginx-entrypoint
🐳 Improve Docker nginx
2025-09-26 10:49:24 +02:00
Yamila Moreno 042bd03beb 🐳 Improve Docker nginx 2025-09-26 10:31:23 +02:00
andrés gonzález cce1dd86a2 💄 Change variants video source to peertube (#7387) 2025-09-26 10:21:41 +02:00
Juan de la Cruz a39a127f03 🐛 Fix underline text in template card at carrusel 2025-09-26 09:56:05 +02:00
Pablo Alba bd665f70bf 💄 Add new library modal UI tweaks 2025-09-25 22:56:27 +02:00
Eva Marco e184a9a8b9 🐛 Fix context menu on spacing tokens (#7382) 2025-09-25 17:28:46 +02:00
Elena Torró 9b90236b72 Merge pull request #7385 from penpot/elenatorro-improve-image-load-performance
🔧 Improve image parsing performance
2025-09-25 17:20:49 +02:00
Elena Torro bf6cdf729d 🔧 Improve image parsing performance 2025-09-25 17:17:42 +02:00
Belén Albeza 361bdb4a04 ♻️ Decouple serialization from text/layout models" (#7360)
* ♻️ Move text serialization code to wasm module

* ♻️ Add serializer for TextAlign

* ♻️ Add serializers for TextDirection and TextDecoration

* ♻️ Add serializer for TextTransform

* ♻️ Remove unused font_style from TextLeaf model

* ♻️ Refactor parsing of TextLeaf from bytes

* ♻️ Decouple tight serialization of Paragraph
2025-09-25 16:54:07 +02:00
andrés gonzález 58c6c94cb8 📚 Update boards info at the user guide (#7383) 2025-09-25 16:36:35 +02:00
Elena Torró 3827aa6bd4 Merge pull request #7344 from penpot/elenatorro-11542-truncate-long-font-names-on-fonts-menu
🔧 Use two lines text ellipsis on custom font names
2025-09-25 15:25:50 +02:00
Xaviju adf7b0df50 🎉 Add visibility panel to inspect styles tab (#7362) 2025-09-25 12:52:43 +02:00
Elena Torro 97b4491a27 🔧 Use two lines text ellipsis on custom font names 2025-09-25 12:49:33 +02:00
andrés gonzález ecee7ecfc7 📚 Update workspace info at the user guide (#7376) 2025-09-25 12:24:59 +02:00
Xavier Julian 015bd9e453 🎉 Inspect styles tab: fill panel 2025-09-25 11:31:15 +02:00
Belén Albeza 49d5987b15 💄 Add deprecated namespace and fix import for remaining scss files (#7379) 2025-09-25 11:27:10 +02:00
Belén Albeza a5e4de97e3 💄 Use deprecated prefix for deprecated scss vars and mixins (#7375) 2025-09-25 09:22:25 +02:00
Alonso Torres 378be9473d 🐛 Fix problem with export size (#7374) 2025-09-25 08:50:31 +02:00
Juan de la Cruz 412cf61d7d 🐛 Remove translations form inspect tab text properties (#7369) 2025-09-25 08:48:41 +02:00
Juan de la Cruz 754a1b6fa2 🐛 Fix loading tips wording (#7368) 2025-09-25 08:48:10 +02:00
Eva Marco a4ada6dc8a 🐛 Add default flags for tokens (#7367) 2025-09-25 08:47:04 +02:00
Elenzakaleidos ec94d08f4a 🎉 Update README.md with Variants (#7353)
Update the Readme with new text and image that include Variants as feature

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2025-09-25 08:46:02 +02:00
Alonso Torres b6b2d28464 🐛 Fix problem with flow not being deleted (#7371) 2025-09-24 18:06:26 +02:00
Elena Torro 32770c685a 🐛 Do not add shadows on hidden children 2025-09-24 14:42:57 +02:00
andrés gonzález b770145436 💄 Update variants video at the user guide (#7363) 2025-09-24 13:41:20 +02:00
Eva Marco 441dc33e38 ♻️ Add shortcut to scss import paths (#7364)
* 🎉 Add config for shortcut imports

* ♻️ Change import paths
2025-09-24 11:18:34 +02:00
Eva MarcoandAndrey Antukh 3f87e768a7 ♻️ Fix color token reviews (#7322)
* ♻️ Fix some review changes

* 🐛 Fix more errors

* 🎉 Create token from colorpicker fixed

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-09-24 11:13:52 +02:00
David Barragán Merino 09e9340ba6 💄 Fix a description and remove an unused event 2025-09-24 09:38:42 +02:00
David Barragán Merino d5ff7b4144 📎 Add DevEnv builder workflow 2025-09-23 23:26:11 +02:00
David Barragán Merino ef0aee0a09 📎 Automatically publish github release and docker images with final version tags 2025-09-23 23:25:52 +02:00
Andrey Antukh 1e9682376e Merge remote-tracking branch 'origin/staging' into develop 2025-09-23 12:20:49 +02:00
Andrey Antukh 11b75408fe 🐛 Fix regression on importing binfile-v1 files (#7359) 2025-09-23 11:38:33 +02:00
Andrey Antukh 59f7ede4ff 🐛 Add migration for properly decode all position data on text shapes 2025-09-23 11:34:24 +02:00
Pablo Alba c9b61745a0 🎉 Switch several variant copies at the same time 2025-09-23 11:31:57 +02:00
Andrés Moya 8954b05d76 🐛 Fix error exporting a file with deleted tokens (#7356) 2025-09-22 17:41:31 +02:00
Aitor Moreno 974b76d7bd Merge pull request #7267 from penpot/azazeln28-feat-text-layout
🎉 Add internal TextContent layout data
2025-09-22 16:21:06 +02:00
Aitor Moreno f505fcfa0d 🎉 Add internal TextContent layout data 2025-09-22 16:01:23 +02:00
Belén Albeza e4d610d503 ♻️ Decouple shapes serialization from model (rust) (#7328)
* ♻️ Move shape type serialization to wasm module

* ♻️ Refactor serialization of constraints and vertical alignment into wasm module

* ♻️ Refactor serialization and model of shape blur

* ♻️ Refactor bool serialization to the wasm module

* ♻️ Split wasm::layout into submodules

* ♻️ Refactor serialization of AlignItems, AlignContent, JustifyItems and JustifyContent

* ♻️ Refactor serialization of WrapType and FlexDirection

* ♻️ Refactor serialization of JustifySelf

* ♻️ Refactor serialization of GridCell

* ♻️ Refactor serialization of AlignSelf

* 🐛 Fix AlignSelf not being serialized

* ♻️ Refactor handling of None variants in Raw* enums

* ♻️ Refactor serialization of grid direction

* ♻️ Refactor serialization of GridTrack and GridTrackType

* ♻️ Refactor serialization of Sizing

* ♻️ Refactor serialization of ShadowStyle

* ♻️ Refactor serialization of StrokeCap and StrokeStyle

* ♻️ Refactor serialization of BlendMode

* ♻️ Refactor serialization of FontStyle

* ♻️ Refactor serialization of GrowType
2025-09-22 13:47:54 +02:00
Andrey Antukh cb4c155b32 📎 Uncomment previously commented migrations 2025-09-22 11:38:52 +02:00
Andrey Antukh 0b346e02ff 🐛 Fix incorrect options pass on decode-file 2025-09-22 11:30:42 +02:00
Madalena Melo 5c23a678cc Merge pull request #7342 from penpot/madalenapmelo-kp-patch-1
📚 Add reference to the Teams section on the Dashboard section
2025-09-22 11:23:29 +02:00
Andrey Antukh 946f641917 📎 Disable possible problematic migrations 2025-09-22 11:12:43 +02:00
David Barragán Merino fb3923924b 📎 Change the name of some action workflows 2025-09-22 09:58:26 +02:00
Florian Schroedl c882e8347a Add line-height to composite typography token 2025-09-22 09:52:56 +02:00
Pablo AlbaandLuis de Dios c1fd1a3b42 📚 Add variants doc for SDK (#7351)
* 📚 Add variants doc for SDK

* 📚 Spelling & style improvements

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2025-09-21 22:15:27 +02:00
Eva Marco b1fe32baea ♻️ Remove deprecated @import from scss files (#7347)
* 🐛 Fix import warnings 1 of 2

* 🐛 Fix import warnings 2 of 2

* 🐛 Fix visual tests and format files

* 🐛 Fix mixed declarations on scss
2025-09-19 11:50:08 +02:00
Andrey Antukh fb7a7d02da Merge pull request #7205 from penpot/niwinz-measures-tokens-backup
♻️ Replace numeric inputs on measure options
2025-09-19 11:44:17 +02:00
Eva Marco 20dfc2a216 🐛 Fix typo on event name (#7350) 2025-09-19 11:40:53 +02:00
Eva Marco d7d2d36e0a ♻️ Replace measure inputs for numeric input component 2025-09-19 11:28:22 +02:00
Andrey Antukh 07904bcc5d ♻️ Add needed changes to get tokens from sidebar
This reverts commit afe149f702148d86d1dea6cb6a537917ce7202aa.
2025-09-19 10:26:29 +02:00
Andrey Antukh 9686075104 🐛 Fix translations 2025-09-18 12:02:45 +02:00
María Valderrama 436e0e847d 🐛 Fix current version on sidebar 2025-09-18 11:56:47 +02:00
Eva Marco d50b070a64 🎉 Add usefull mixins to DS (#7340) 2025-09-18 10:47:55 +02:00
Andrey Antukh 80cb48fd6a Merge remote-tracking branch 'origin/staging' into develop 2025-09-18 10:44:21 +02:00
Andrey Antukh e88039e46a 🐛 Fix future linter issues on wasm shape impl 2025-09-17 16:53:02 +02:00
Andrey Antukh 3c45a8d0b4 Allow delete subscriptions on profile deletion request 2025-09-17 16:53:02 +02:00
Aitor Moreno c9d71f3b2d 🐛 Fix conflicting shortcuts (text alignment) (#7339) 2025-09-17 16:52:44 +02:00
Madalena Melo 49c6efbc22 📚 Add reference to the Teams section on the Dashboard section
https://tree.taiga.io/project/penpot/task/11806

Signed-off-by: Madalena Melo <madalena.melo@kaleidos.net>
2025-09-17 16:17:24 +02:00
Andrey Antukh 9f37175775 🐛 Fix incorrect path data content initialization on pluings api 2025-09-17 15:19:41 +02:00
andrés gonzález 5ed870cc6e 📚 Update shortcuts docs (#7341) 2025-09-17 14:13:00 +02:00
Andrey Antukh 4fb1c7a630 Merge remote-tracking branch 'origin/staging' into develop 2025-09-17 13:46:49 +02:00
Pablo Alba 2a3d7e470d 📚 Update changelog with variants info (#7335) 2025-09-17 13:45:59 +02:00
Eva Marco f654eb2dcd 🐛 Fix font weight input placehoder (#7338) 2025-09-17 13:44:11 +02:00
Belén Albeza c21d705143 🐛 Remove shortcuts for inc/dec line height and letter spacing (#7337) 2025-09-17 12:35:39 +02:00
Andrey Antukh 85c1750706 🐛 Fix backend last migration naming (#7333) 2025-09-17 10:47:14 +02:00
Luis de Dios e2151409bf 🐛 Fix wrong number of components in the library modal (#7332) 2025-09-17 09:25:23 +02:00
Luis de Dios 4fe6cfc57a 🐛 Fix focus the first property value when creating a variant (#7329) 2025-09-16 23:25:18 +02:00
David Barragán Merino fd37fdde93 📎 Add release action workflow 2025-09-16 18:06:06 +02:00
David Barragán Merino 66b1d5b7bd Merge remote-tracking branch 'origin/staging' into develop 2025-09-16 16:26:23 +02:00
Andrés Moya 2eed7444b7 🔧 Add migration to automatically fix validation errors 2025-09-16 16:11:58 +02:00
Xavier Julian 2bf7a9dd5f ♻️ Remove unneeded fn parameters 2025-09-16 14:17:14 +02:00
Xavier Julian 7bacd8fbca ♻️ Refactor defmulti fn into case switches 2025-09-16 14:17:14 +02:00
Luis de Dios ef376fbb7b Add shortcut for creating variant to the shortcuts panel (#7319)
*  Add shortcut for creating variant to the shortcuts panel

* ♻️ Update components to new rumext syntax

* 🐛 Fix unique "key" prop error for each child in a list

* ♻️ Remove deprecated icons and CSS cleanup

* 📎 PR changes
2025-09-16 14:06:47 +02:00
Aitor Moreno b883882a32 🐛 Fix onboarding select keyboard interaction (#7295) 2025-09-16 13:59:15 +02:00
Pablo Alba 18d5b84b00 🐛 Fix variants events (#7320)
* 🐛 Add missing event add-component-to-variant

* 🐛 Fix event apply-tokens, param applied-to-variant

* 🐛 Fix missing case on event "add new variant"

* 🐛 Fix event combine-as-variants

* 🐛 Fix event variant-edit-property-name

* 🐛 On variants events, change trigger for origin

* 🐛 Split combine-as-variants to not have an optional first parameter
2025-09-16 13:09:23 +02:00
Belén Albeza e5e11b6383 🔧 Autogenerate serialization values for wasm enums (#7296)
* 🔧 Autogenerate serialization values for wasm enums

* 🔧 Add serializer values to the wasm api

*  Avoid converting to a clojure map the serializer js object

* 🔧 Update watch script for autoserialized enums

* 🐛 Fix missing serializer values
2025-09-16 12:29:14 +02:00
Eva Marco 01e963ae35 🐛 Fix font name hot update (#7316) 2025-09-16 12:23:41 +02:00
Eva Marco 90a80c4b63 🐛 Fix Uppercase on add token button (#7314) 2025-09-16 12:05:55 +02:00
VKing9 1bd45d3f8a 🌐 Add translations for: Hindi
Currently translated at 95.2% (1825 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-09-16 12:02:02 +02:00
Andrey Antukh b56f237780 Merge remote-tracking branch 'origin/staging' into develop 2025-09-16 11:38:58 +02:00
Xavier Julian 4970ae3eb4 💄 Align tokens panel vertically to the top 2025-09-16 11:38:33 +02:00
Aitor MorenoandAndrey Antukh c62fadac47 🐛 Fix fast move with distance (#7302)
* 🐛 Fix fast move with distance

* 📎 Remove duplicated shourtcuts

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-09-16 11:36:21 +02:00
Andrés Moya a264f84e6c 🔧 Deactivate validation temporarily 2025-09-16 11:34:38 +02:00
Elena Torró 2e21f084fc 🐛 Fix boolean operations on rotated shapes (#7309) 2025-09-15 14:46:56 +02:00
Xavier Julian 55513b9ae5 🎉 Inspect styles tab: layout element panel 2025-09-15 13:39:00 +02:00
Luis de Dios 9311ee4c87 🐛 Fix show in assets panel option for component of variants (#7311) 2025-09-15 13:13:12 +02:00
Eva Marco 07d0062645 🐛 Fix sets shown without color tokens (#7312) 2025-09-15 10:38:06 +02:00
Xavier Julian f4b38af649 Display border-radius as logical properties in inspect tab 2025-09-15 09:46:01 +02:00
Andrey Antukh 6e7bcd1243 Merge remote-tracking branch 'origin/staging' into develop 2025-09-12 16:55:25 +02:00
Andrey Antukh b8c8579ff5 Merge pull request #7310 from penpot/niwinz-staging-update-jdk
⬆️ Update node and jdk (patch)
2025-09-12 16:54:49 +02:00
Andrey Antukh 82295c79d4 ⬆️ Update jdk to 24.0.2+12 2025-09-12 16:47:25 +02:00
Andrey Antukh 5174591058 ⬆️ Update nodejs to v22.19.0 2025-09-12 16:44:26 +02:00
Andrés Moya ed3fc5b8b2 🐛 Fix detaching a nested copy inside a main component (#7304)
* 🐛 Fix detaching a nested copy inside a main component

* 💄 Rename functions for more semantic precission
2025-09-12 16:00:01 +02:00
Pablo Alba f5f9157786 🐛 Fix paste behavior according to the selected element 2025-09-12 15:17:26 +02:00
Juan de la Cruz 658303fa36 🎉 Add 2.10 release slides (#7293) 2025-09-12 15:06:18 +02:00
Andrey Antukh 6cb0cb7f98 Merge remote-tracking branch 'origin/staging' into develop 2025-09-12 14:49:52 +02:00
Andrey Antukh 84013c21fa Merge pull request #7308 from penpot/niwinz-staging-update-deps
⬆️ Update dependencies
2025-09-12 14:47:53 +02:00
Andrey Antukh f3062ade39 ⬆️ Update jakarta.mail dependency 2025-09-12 14:28:14 +02:00
Andrey Antukh ca19d4deeb ⬆️ Update postgresql jdbc dependency 2025-09-12 14:28:14 +02:00
Andrey Antukh dfceccca3d ⬆️ Update aws s3 sdk version
This transitivelly updates the netty library version
that comes with fixes for several security issues
2025-09-12 14:28:14 +02:00
Luis de Dios 9e2d3b1fa1 🐛 Fix position of annotation for variants (#7306) 2025-09-12 14:05:36 +02:00
Florian Schroedl 4dc0f3b4eb 🐛 Fix make-token throwing because of error in name, keep preview value 2025-09-12 13:49:25 +02:00
andrés gonzález 215288b6b4 📚 Update Design Tokens doc (#7265) 2025-09-12 11:15:23 +02:00
andrés gonzález 2e2c3e7bac 📚 Add doc for variants (#7258) 2025-09-12 11:15:03 +02:00
Xavier Julian 0210b310b7 🎉 Inspect styles tab: layout panel 2025-09-12 10:27:41 +02:00
Stas Haas c77efc657c 🌐 Add translations for: German
Currently translated at 91.7% (1758 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-09-12 10:03:18 +02:00
Eva Marco ce1e44eda4 ♻️ Refactor set titles (#7301) 2025-09-12 08:46:05 +02:00
Marina López 48825e1e59 Show current penpot version 2025-09-11 13:18:42 +02:00
Florian Schroedl 61cfe2d142 🐛 Fix font-family being split up when restoring from backup value 2025-09-11 12:33:26 +02:00
Eva Marco 2d68f4dfd3 🐛 Fix icons (#7299) 2025-09-11 09:42:11 +02:00
Elena Torró 1e23937aa5 Merge pull request #7291 from penpot/superalex-fix-boolean-and-group-shadows
🐛 Fix boolean and group shadows
2025-09-11 09:27:56 +02:00
Eva Marco aecaf51953 Add color token on colorpicker (#7197)
*  Add token aplication to colorpicker

* 🐛 Change fn name

* 🐛 Change scss from file

* 🐛 Change color for direct-color

* 🐛 Remove vector from fns

* 🐛 Fix CI

* 🐛 Change color-option name

* 🐛 Fix comments

* 🐛 Remove sets without color tokens
2025-09-11 09:13:43 +02:00
Alejandro Alonso da05d6b67d 🐛 Fix boolean and group shadows 2025-09-10 15:59:39 +02:00
Alejandro Alonso 99a100ad63 Merge pull request #7264 from penpot/elenatorro-12002-draw-shadows-and-blurs-on-texts-on-surfaces
🐛 Fix text shadows and blur and refactor text rendering
2025-09-10 15:50:33 +02:00
Elena Torró bd3bcb4b18 Merge pull request #7284 from penpot/superalex-fix-blend-mode
🐛 Fix updating blend mode for shapes
2025-09-10 15:03:17 +02:00
Elena Torró 534c7864fc Merge pull request #7285 from penpot/superalex-fix-cornder-radius
🐛 Fix corner radius
2025-09-10 14:59:06 +02:00
Elena Torro 4bd2eba573 🐛 Fix text shadows and blur and refactor text rendering 2025-09-10 14:20:24 +02:00
Xavier Julian 563f608255 🐛 Display token themes as a string 2025-09-10 13:55:54 +02:00
Alejandro Alonso 382b5e7e3a Merge remote-tracking branch 'origin/staging' into develop 2025-09-10 12:33:54 +02:00
Eva Marco a503f8ae93 ♻️ Refactor composite token UI (#7287)
* ♻️ Refactor composite token UI

* 🐛 Fix comments
2025-09-10 12:16:39 +02:00
Xavier Julian e1935fb3fb 🎉 Inspect styles tab: geometry panel 2025-09-10 11:01:19 +02:00
Alejandro Alonso 7ac44009d5 Merge pull request #7288 from penpot/luis-12042-context-menu-variant
🐛 Fix create a variant using the contextual menu
2025-09-09 16:27:35 +02:00
Luis de Dios f22eef5bf6 🐛 Fix create a variant using the contextual menu 2025-09-09 15:59:04 +02:00
Florian Schroedl b3763dec3f Typography import-export 2025-09-09 13:30:38 +02:00
Alejandro Alonso 7c61049103 Merge pull request #7257 from penpot/azazeln28-fix-issue-11992-cannot-move-elements-up-or-down
🐛 Fix moving elements up or down while pressing alt
2025-09-09 11:07:58 +02:00
Alejandro Alonso fe819c6ec4 Merge pull request #7286 from penpot/azazeln28-fix-text-editor-v1-paste
🐛 Fix text editor v1 paste HTML
2025-09-09 11:05:17 +02:00
Aitor Moreno 1a4594a615 🐛 Fix text editor v1 paste HTML 2025-09-09 10:48:15 +02:00
Alejandro Alonso 41751d60d2 🐛 Fix corner radius 2025-09-09 10:24:56 +02:00
Yamila Moreno 8bd0edca46 Merge pull request #7282 from penpot/yms-update-ci
📎 Update CI
2025-09-09 09:30:18 +02:00
David Barragán Merino d2bff2853f Merge pull request #7283 from penpot/bameda-upgrade-docker-images-dependencies
🐳 Update the version of node and nginx-unprivileged
2025-09-09 09:29:36 +02:00
Alejandro Alonso e2f22b86c7 🐛 Fix updating blend mode for shapes 2025-09-09 09:19:09 +02:00
Aitor Moreno ff96f7be85 🐛 Fix moving elements up or down while pressing alt 2025-09-09 09:19:00 +02:00
Alejandro Alonso 108b5ab225 🐛 Fix missing filter-icon 2025-09-09 09:05:42 +02:00
Alejandro Alonso a403af7ebd 🐛 Fix plugin installation link 2025-09-09 08:47:09 +02:00
Alejandro Alonso 43a238a896 Merge remote-tracking branch 'origin/staging' into develop 2025-09-09 08:40:35 +02:00
Alejandro Alonso e3c9588c1c Merge pull request #7279 from penpot/palba-variants-events2
🎉 Add "advanced" events to variants
2025-09-09 07:03:22 +02:00
Alejandro Alonso 25b63e5675 Merge pull request #7280 from penpot/palba-fix-variants-duplicate
🐛 Fix bad selection after variant duplicate
2025-09-09 06:49:34 +02:00
David Barragán Merino 6c59d633cd 🐳 Update the version of node and nginx-unprivileged 2025-09-08 18:36:31 +02:00
Yamila Moreno daa408e291 📎 Update CI 2025-09-08 16:51:05 +02:00
Andrés Moya bb0a891638 📚 Update changelog 2025-09-08 16:49:27 +02:00
Florian Schrödl 8aed47dad3 Allow references to other typography tokens (#7251) 2025-09-08 16:45:18 +02:00
Pablo Alba c5bd183f73 🐛 Fix bad selection after variant duplicate 2025-09-08 16:33:38 +02:00
Pablo Alba 06441063f2 Add "advanced" events to variants 2025-09-08 15:33:14 +02:00
Elena Torró 0e23c9f6ab Merge pull request #7278 from penpot/superalex-fix-fill-stroke-opacity-shouldnt-affect-shadows
🐛 Fix fills and strokes opacity shouldn't affect shadows
2025-09-08 14:08:20 +02:00
Alejandro Alonso 8fff9afee6 🐛 Fix fills and strokes opacity shouldn't affect shadows 2025-09-08 13:04:52 +02:00
Alejandro Alonso 9f11a2cb32 🐛 Fix context menu shape ids (#7277)
This reverts commit 1929ee36ed.
2025-09-08 11:59:54 +02:00
Xavier Julian ff55318c04 🎉 Inspect styles tab: variants panel 2025-09-08 11:59:33 +02:00
Elena Torró 41b7957eff Merge pull request #7274 from penpot/superalex-refactor-drop-shadows
🐛 Fixing nested shadows
2025-09-08 11:38:19 +02:00
Alejandro Alonso 053b2c6248 Merge pull request #7253 from penpot/marina-payments-incorrect-date-plan
🐛 Fix incorrect date displayed for support plan
2025-09-08 11:22:48 +02:00
Alejandro Alonso 7e52aadb98 🐛 Fixing nested shadows 2025-09-08 11:20:03 +02:00
Marina López fad058ee59 🐛 Fix incorrect date displayed for support plan 2025-09-08 11:06:57 +02:00
Alejandro Alonso 69f41c300f Merge pull request #7199 from penpot/elenatorro-11844-fix-font-long-names
🐛 Fix custom font-long names overflow
2025-09-08 10:48:54 +02:00
Elena Torro 18c5e0b9a8 🐛 Fix font long name overflow 2025-09-08 10:31:35 +02:00
Alejandro Alonso 568c2fd9d7 Merge pull request #7271 from penpot/eva-bugfixing-release
🐛 Fix several bugs
2025-09-08 10:23:05 +02:00
Luis de Dios 794eb78aca ♻️ Refactor icon namespaces (#7262)
* ♻️ Rename old icons as deprecated

* ♻️ Take icons from the namespace for the icon component

* ♻️ Take icons from the namespace for the icon-button component
2025-09-08 10:02:33 +02:00
Florian Schrödl e761bcac85 🐛 Fix text-transform not unapplyng text-case token (#7272) 2025-09-08 09:34:58 +02:00
Eva Marco 1929ee36ed 🐛 Fix ungroup option missing 2025-09-08 09:18:43 +02:00
Nicola Bortoletto 26f123f466 🌐 Add translations for: Italian
Currently translated at 99.7% (1912 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-06 09:02:02 +02:00
DoubleCat d9f186524d 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 95.6% (1834 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-09-06 09:02:00 +02:00
Eva Marco 84cccd1b79 🐛 Fix alignment row on single shape selection 2025-09-05 13:54:46 +02:00
Eva Marco e66d44ca81 🐛 Fix wrong icon on path layer 2025-09-05 13:54:19 +02:00
Eva Marco 2f3b464715 🎉 Add base font size flag (#7270) 2025-09-05 13:36:32 +02:00
Pablo Alba 286e477ad5 🐛 Change variants nesting error texts 2025-09-05 12:34:16 +02:00
Andrés Moya 6e6749f42e 🔧 Add unit tests to apply layout tokens 2025-09-05 11:11:48 +02:00
Andrés Moya 7b6aa0c12a 🐛 Unapply layout item tokens when moving out of a layout 2025-09-05 11:11:48 +02:00
Nicola Bortoletto 5f33ce9ef6 🌐 Add translations for: Italian
Currently translated at 98.2% (1883 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-04 19:01:57 +02:00
Pablo Alba 409f95ac17 Add basic variants events (#7249)
*  Add basic variants events

*  MR changes
2025-09-04 17:00:09 +02:00
Florian Schroedl 25950be077 🐛 Fix when font-weight is a computed int (math resolver) 2025-09-04 13:06:57 +02:00
Florian Schroedl 5230d54551 🐛 Fix when font-weight is a computed int (math resolver) 2025-09-04 12:23:43 +02:00
Alejandro Alonso a79be05261 🐛 Fix selection and devtools problem (#7259) 2025-09-04 09:29:38 +02:00
Alejandro Alonso 9eda1d0d78 Merge pull request #7256 from penpot/ladybenko-8371-fix-iconsistent-naming
🐛 Fix inconsistent naming for Flatten
2025-09-04 07:34:10 +02:00
Belén Albeza f6c4f800c4 📚 Update changelog 2025-09-04 07:13:30 +02:00
Belén Albeza f363d6a801 Add integration test for Flatten menu option 2025-09-04 07:13:16 +02:00
Belén Albeza e88ce0d52f 🐛 Unify flatten naming 2025-09-04 07:13:16 +02:00
Alejandro Alonso fe5fe7a933 Merge pull request #7252 from penpot/mavalroot-typos
🐛 Fix typos
2025-09-04 07:05:37 +02:00
Belén Albeza 9c77296858 🔧 Make the watch script to compile the debug css when not in production env (#7250) 2025-09-03 13:45:11 +02:00
Xavier Julian 34da6b64df 🎉 Inspect styles tab tokens panel 2025-09-03 13:01:38 +02:00
María Valderrama 699cc147b5 🐛 Fix typos 2025-09-03 11:20:12 +02:00
Corentin Noël 4becd35e52 🌐 Add translations for: French
Currently translated at 97.6% (1871 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-09-03 09:02:01 +00:00
Luis de Dios b1d792a757 🐛 Fix icons do not appear in swap panel and annotations (#7240)
* 🐛 Fix icons do not appear in swap panel and annotations

* 📎 PR changes
2025-09-03 10:57:47 +02:00
Florian Schroedl 18e6842e35 ♻️ Revert trigger interactive via actionize and propagation 2025-09-03 10:10:34 +02:00
Florian Schroedl c4481be39f ♻️ Revert trigger interactive via actionize and propagation 2025-09-03 09:42:40 +02:00
Andrés Moya 0df420d353 🐛 Fix setting shape size to zero 2025-09-03 08:57:26 +02:00
Elena Torró f60b6a4869 Merge pull request #7247 from penpot/ladybenko-11983-textlayout-module
♻️ Refactor into new textlayout module
2025-09-02 17:17:12 +02:00
Belén Albeza 3e02dc550f ♻️ Create type alias for ParagraphBuilderGroup 2025-09-02 15:32:10 +02:00
Belén Albeza 1cf0de395c ♻️ Rename get_children to children (Paragraph) 2025-09-02 15:30:54 +02:00
Belén Albeza d40b68c004 ♻️ Refactor and rename ParagraphBuilder instantiating from TextContent 2025-09-02 15:22:05 +02:00
Belén Albeza 50b9e8c6e6 ♻️ Rename TextContent::get_width to TextContent::width 2025-09-02 15:07:13 +02:00
Belén Albeza d25f9cd4bd ♻️ Move auto_width and auto_height to their own textlayout module 2025-09-02 15:03:46 +02:00
Pablo Alba dac2d31b35 🐛 Don't allow a variant switch when that will provoke a components loop 2025-09-02 15:03:38 +02:00
Florian Schroedl bedb98ad9f Add context menu for typography 2025-09-02 13:19:45 +02:00
Elena Torró 5f37601122 🐛 Fix different fonts on texts shadows (#7214)
* 🐛 Fix different fonts on texts shadows

* 🔧 Refactor text rendering and move text-decoration logic outside

* 🔧 Use transparency correctly
2025-09-02 12:56:07 +02:00
Pablo Alba 1d3a1a094a 🐛 Missing component copy options on the context menu 2025-09-02 12:49:53 +02:00
Andrés Moya 9652996f07 🐛 Add validation for text shapes with wrong register of overrides 2025-09-02 12:49:41 +02:00
Pablo Alba 796aaed11e 🐛 Fix prop creation on variants move layer 2025-09-02 10:01:30 +02:00
Alejandro Alonso 1da69cfa38 📎 Add next release entries to the changelog 2025-09-01 11:10:09 +02:00
Luis de Dios 2a2735cd67 💄 Adjust design details of some components (#7225) 2025-09-01 11:02:36 +02:00
Nicola Bortoletto b0712b6dc5 🌐 Add translations for: Italian
Currently translated at 96.1% (1844 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-09-01 11:02:03 +02:00
Yaron Shahrabani cc31ee50df 🌐 Add translations for: Hebrew
Currently translated at 99.5% (1908 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-09-01 11:02:01 +02:00
Elena Torró 0552b6e713 Merge pull request #7189 from penpot/azazeln28-feat-text-playground-fonts
🎉 Add multiple fonts to text editor WASM playground
2025-09-01 10:54:40 +02:00
Alejandro Alonso 30e655b1da Merge pull request #7236 from penpot/eva-fix-padding-sidebar
🐛 Fix wrong spacing
2025-09-01 10:35:21 +02:00
Alejandro Alonso 0aeecc6268 Merge pull request #7234 from penpot/palba-variants-activate-for-everyone
🎉 Activate variants for everyone
2025-09-01 10:27:44 +02:00
Eva Marco 5cec006a76 🐛 Fix wrong spacing 2025-09-01 10:15:52 +02:00
Pablo Alba 307e06372b 🎉 Activate variants for everyone 2025-09-01 09:58:14 +02:00
Xaviju 9f24e76c27 🌐 Convert translation single word to label (#7220) 2025-09-01 08:44:06 +02:00
Xavier Julian 7954eaf529 🎉 Inspect styles tab attributes container box 2025-08-31 14:58:11 +02:00
Stephan Paternotte 63456d2b75 🌐 Add translations for: Dutch
Currently translated at 99.8% (1915 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-31 06:05:48 +00:00
Stephan Paternotte 6a4a22c77a 🌐 Add translations for: Dutch
Currently translated at 99.8% (1915 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-29 17:01:57 +02:00
Edgars Andersons 32ad35aa19 🌐 Add translations for: Latvian
Currently translated at 97.3% (1867 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-08-29 17:01:56 +02:00
Yaron Shahrabani e1522f1e8a 🌐 Add translations for: Hebrew
Currently translated at 99.1% (1900 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-08-29 17:01:54 +02:00
Corentin Noël 05093a32f3 🌐 Add translations for: French
Currently translated at 97.4% (1868 of 1917 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-08-29 17:01:53 +02:00
Eva Marco fe406b577e 📚 Add comment to not translated string 2025-08-29 14:26:52 +02:00
Eva Marco 76c03af024 ♻️ Fix how files are used on scss files (#7208) 2025-08-29 13:55:50 +02:00
Andrey Antukh fe5cdcbdc7 🐛 Do not render assets sidebar context menu if it is not open (#7222)
This commit is a workaround to an issue that happens when you performing
multiple selection of several shapes (including components) and an
exception is raised of max depth of updates. The issue is still not
solved, we justo do not render the context menu when user performs
the selection on the workspace.

That issue/exception happens only we have context-menu* component
rendered independently of its visibility and dev-tools open
2025-08-29 13:53:55 +02:00
Florian Schroedl 2479a06f9a 🐛 Fix token create not disabled when creating token without value 2025-08-29 13:41:38 +02:00
Florian Schroedl fc5e4a821b ♻️ Use interactive update functions only on user actions 2025-08-29 13:38:41 +02:00
Florian Schroedl 3cdbc27de9 Unapply font-weight token when changing font-family 2025-08-29 13:38:41 +02:00
Florian Schroedl a8ed1371d4 Find closest variant when updating font family 2025-08-29 13:38:41 +02:00
Andrey Antukh e7bac41c37 Merge pull request #7202 from penpot/niwinz-develop-sidebar-refactor-2
♻️ Refactor right sidebar state management
2025-08-29 12:10:51 +02:00
Andrey Antukh dede2a8f8e 💄 Fix JS files formatting issues 2025-08-29 11:25:58 +02:00
Andrey Antukh e94abad3eb Add efficiency refactor for layer-menu* 2025-08-29 11:25:58 +02:00
Andrey Antukh f8bc6e12a9 Improve efficiency of border radius menu 2025-08-29 11:25:58 +02:00
Andrey Antukh a2c3208af9 🐛 Fix regression on not updating measures ui on moving frames 2025-08-29 11:25:58 +02:00
Andrey Antukh a303df9c34 ♻️ Refactor right sidebar state management
Also removing duplicated refs and improve efficiency of
several other refs used on sidebar.
2025-08-29 11:25:58 +02:00
Kelp bda24f3829 📎 Update Changelog to add font weight token (#7216)
Add font weight to changes.md

Signed-off-by: Kelp <5446186+NatachaMenjibar@users.noreply.github.com>
2025-08-29 11:04:36 +02:00
Elena Torró 94fd3119e8 Merge pull request #7175 from penpot/superalex-fix-clipping
🐛 Fix clipping
2025-08-29 11:03:07 +02:00
María Valderrama c8091b42a7 🐛 Fix navigation arrows in Libraries & Templates carousel (#7217) 2025-08-29 10:51:00 +02:00
Xaviju 41a859b444 🌐 Recover lost variant translation (#7215) 2025-08-29 09:50:33 +02:00
Aitor Moreno 1cf20f7604 Merge pull request #7203 from penpot/elenatorro-fix-text-decoration-leaf-position
🐛 Fix line height on texts and improve text decoration rendering
2025-08-28 16:21:40 +02:00
Andrey Antukh 629541bc9d 🐛 Fix incorrect recursion on looking boolean parent (#7212) 2025-08-28 16:13:14 +02:00
Alejandro Alonso 44245d1b5f 🐛 Fix position-data validator (#7194) 2025-08-28 15:46:23 +02:00
Luis de Dios a8692c72c6 Show create variant shortcut also for stand-alone components (#7195) 2025-08-28 15:45:53 +02:00
Elena Torró 7b7da59ca9 Merge pull request #7200 from penpot/ladybenko-11552-fix-font-styles
🐛 Fix new inline text styles not being applied correctly
2025-08-28 15:34:54 +02:00
Elena Torro b78e3159d1 📚 Add wasm playground internal docs 2025-08-28 15:10:56 +02:00
Luis de Dios 2106028350 🐛 Fix color of variant property names in the design tab (#7204) 2025-08-28 12:51:04 +02:00
Andrey Antukh c2e7f9dc42 ⬆️ Update base deps (#7207) 2025-08-28 12:50:39 +02:00
Andrey Antukh 8568098c5e 🌐 Validate and rehash translation files 2025-08-28 12:49:35 +02:00
VKing9 c8bc1ef1d2 🌐 Add translations for: Hindi
Currently translated at 94.8% (1816 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-08-28 12:46:50 +02:00
Stephan Paternotte 59bd434d05 🌐 Add translations for: Dutch
Currently translated at 97.5% (1869 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-28 12:46:50 +02:00
Edgars Andersons 3cf1e53340 🌐 Add translations for: Latvian
Currently translated at 96.0% (1839 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-08-28 12:46:50 +02:00
Denys Kisil 24f0956630 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 95.8% (1835 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-08-28 12:46:49 +02:00
Nicola Bortoletto 91991d5b32 🌐 Add translations for: Italian
Currently translated at 95.3% (1826 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-08-28 12:46:49 +02:00
Yaron Shahrabani 447b6ed1ab 🌐 Add translations for: Hebrew
Currently translated at 97.3% (1864 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-08-28 12:46:49 +02:00
Stas Haas 336486fecd 🌐 Add translations for: German
Currently translated at 89.2% (1710 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-08-28 12:46:48 +02:00
Jun Fang bbd417c119 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 91.8% (1758 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-28 12:46:48 +02:00
Ingrid Pigueron e3ffd45a18 🌐 Add translations for: French
Currently translated at 96.4% (1847 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-08-28 12:46:48 +02:00
Unreal Vision 52a4a46ebd 🌐 Add translations for: French
Currently translated at 96.4% (1847 of 1915 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-08-28 12:46:48 +02:00
Elena Torro 025423a75e 🐛 Fix line height on texts and improve text decoration rendering 2025-08-28 12:40:23 +02:00
Hosted Weblate a6f17e35dd 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-08-28 12:37:27 +02:00
Andrey Antukh 9a8cac3cac Merge remote-tracking branch 'weblate/develop' into develop 2025-08-28 12:36:51 +02:00
Aitor Moreno 884b857d17 🐛 Fix paste RTF crashes text editor (#7196) 2025-08-28 11:53:39 +02:00
Florian Schroedl a20bbeff79 🔧 Add ff for typography composite token 2025-08-28 10:42:58 +02:00
Eva Marco 2a5f1f870b 🐛 Fix padding of input field component (#7198) 2025-08-28 10:30:33 +02:00
Luis de Dios cf5303a39c ♻️ Title bar refactor (#7201) 2025-08-28 09:51:53 +02:00
Andrey Antukh bf1e26c4e6 Merge pull request #7182 from penpot/niwinz-measures-menu-changes
♻️ Add efficiency refactor for sidebar
2025-08-28 08:40:44 +02:00
Andrey Antukh 4713d943d1 ♻️ Add efficiency refactor for workspace sidebars
The main changes are for right sidebar but left sidebar is also
slightly affected beacuse of the move where the active tokes are
resolved.
2025-08-27 17:56:35 +02:00
Eva Marco df083cb315 🐛 Fix corner case on tooltip positioning 2025-08-27 17:56:35 +02:00
Eva Marco 6401b25964 💄 Format tab-switcher stories jsx file 2025-08-27 17:56:35 +02:00
Andrey Antukh 65f4adc68e Add minor efficiency enhancements to numeric-input* 2025-08-27 17:56:35 +02:00
Andrey Antukh dfab472522 💄 Add minor cosmetic change to shape layout type helper 2025-08-27 17:56:35 +02:00
Belén Albeza 9759adf747 🐛 Fix new inline text styles not being applied correctly 2025-08-27 16:52:57 +02:00
Andrey Antukh 9ae1a08573 🐛 Make the app.common.time/inst nil safe 2025-08-27 14:17:11 +02:00
Ingrid Pigueron ddab2cab14 🌐 Add translations for: French
Currently translated at 98.1% (1867 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-08-27 14:15:14 +02:00
Andrey Antukh cf1a8fff65 Merge remote-tracking branch 'origin/develop' into develop 2025-08-27 13:19:53 +02:00
Andrey Antukh 45be4769d7 Merge remote-tracking branch 'origin/staging' into develop 2025-08-27 13:19:06 +02:00
María Valderrama a68c41709a 🐛 Fix misleading affordance in saved versions (#7192) 2025-08-27 13:01:54 +02:00
Xaviju 4290bce718 🎉 Inspect tab layout switcher and computed feature (#7166) 2025-08-27 13:01:01 +02:00
Pablo Alba 3dd237002b 🐛 Fix combine as variants from assets tab selects wrong components (#7190) 2025-08-27 13:00:28 +02:00
Andrey Antukh e0fb112bfb 📎 Update changelog 2025-08-27 12:52:24 +02:00
Andrey Antukh 2629537fd3 Merge branch 'subscriptions-enhancements' into staging 2025-08-27 12:51:34 +02:00
Marina López cb7711f637 🐛 Fix integration tests 2025-08-27 12:30:48 +02:00
Andrey Antukh a114e9adb0 Add logging for management update-customer method 2025-08-27 12:02:39 +02:00
Andrey Antukh 5fed5fa158 Add transactions support on management api 2025-08-27 12:00:03 +02:00
Andrey Antukh 6c8873c7f5 🐛 Ensure for-update locking is used on updating profile props 2025-08-27 11:59:35 +02:00
Andrey Antukh 37b0c4adc0 🐛 Add support fror ::db/for-update on sql ns 2025-08-27 11:58:42 +02:00
Florian Schroedl 9106617436 🎉 Add composite typography token 2025-08-27 11:35:56 +02:00
Yamila Moreno 68a95cf0d0 📎 Fix CI inputs 2025-08-27 11:21:34 +02:00
Yamila Moreno 1438632dde 📎 Fix CI inputs 2025-08-27 11:19:42 +02:00
Florian Schroedl 112fa46896 🐛 Fix case-sensitivity and multi word italic in font weight parsing 2025-08-27 11:15:21 +02:00
Yamila Moreno 6da5bbf33a 📎 Fix CI inputs 2025-08-27 11:14:56 +02:00
Yamila Moreno 95faf340c4 📎 Fix CI inputs 2025-08-27 11:13:13 +02:00
Marina López 723ea508df 🐛 Fix missing link for enterprise trial 2025-08-27 10:56:17 +02:00
Yamila Moreno cbe538261c 📎 Fix CI inputs 2025-08-27 10:42:13 +02:00
Yamila Moreno 1925e6782f 📎 Fix CI inputs 2025-08-27 10:33:17 +02:00
Yamila Moreno bcd950c141 📎 Fix CI inputs 2025-08-27 10:11:56 +02:00
Aitor Moreno b215689566 🎉 Add multiple fonts to text editor WASM playground 2025-08-27 09:57:55 +02:00
Pablo Alba dfe0f64c7c Add variants advanced retrieve tests (#7183) 2025-08-26 16:40:27 +02:00
Andrey Antukh 9de3910526 Add missing impl for Associative on LoadableWeakValueMap (#7188) 2025-08-26 16:39:48 +02:00
Luis de Dios 59eb75d8c3 💄 Style improvements in the component list & grid (#7185) 2025-08-26 16:35:09 +02:00
Florian Schroedl 6670b76ccc Show warning when applying token with non-matching font variant 2025-08-26 16:12:07 +02:00
Florian Schroedl 09b9383a0b Choose closest font weight for token weight when no matching weight is found 2025-08-26 16:12:07 +02:00
Eva Marco 31e37f352d 🐛 Fix token option schema (#7186)
* 🐛 Fix token option schema

* 🐛 Fix numeric-input schema
2025-08-26 14:06:59 +02:00
Yamila Moreno c5958e4d61 📎 Add storybook to CI bundle 2025-08-26 10:45:08 +02:00
Luis de Dios f1e7149e88 Add shortcuts for creating variants and properties (#7181)
*  Add shortcuts for creating variants and properties

* 📎 PR changes
2025-08-26 09:32:41 +02:00
Pablo Alba d80ef17623 🐛 Fix cut pasting a variant into its own parent (#7179) 2025-08-26 09:25:52 +02:00
Andrey Antukh ffe469ce71 Merge pull request #7159 from penpot/niwinz-develop-migrations-fixes
♻️ Add a better approach for load libraries on file validation and migrations
2025-08-26 09:25:23 +02:00
Andrey Antukh c35bb6e09a 🎉 Add loadable weak map impl for libraries loading on validation and migration 2025-08-26 09:03:25 +02:00
Alejandro Alonso 8d404d97a1 🐛 Fix clipping 2025-08-26 08:52:42 +02:00
Andrey Antukh fa2b0bd67c Don't migrate libraries on accessing them on file data migrations
We don't migrate the libraries for avoid cascade migration; it is not ideal
but it reduces the total of the required memory needed for process a single
file migration that requires libraries to be loaded.
2025-08-26 08:15:45 +02:00
Andrey Antukh 9563d1b1f6 Merge pull request #6635 from penpot/eva-add-numeric-input
 Add numeric input component
2025-08-25 19:42:47 +02:00
Eva Marco 33fc578f96 🎉 Add numeric-input component to DS
A new numeric-input impl compatible with tokens
2025-08-25 18:52:39 +02:00
Andrey Antukh 79786dde16 🎉 Add helpers for work with weak references and weak data structs 2025-08-25 18:52:39 +02:00
Marina López 926b2c9cfb 🐛 Fix doble click to submit subscription 2025-08-25 15:42:47 +02:00
Andrés Moya c1b2aa7628 🐛 Add handler to correctly encode cljs dates to json 2025-08-25 13:52:38 +02:00
Andrey Antukh 991b26b73f 🐛 Fix undo transaction handling on creating a variant from group of components 2025-08-25 11:48:46 +02:00
Andrey Antukh 254a7461b2 Simplify commit-undo-transaction impl 2025-08-25 11:48:46 +02:00
Andrey Antukh 1384ed8aba Remove duplicated execution of check-open-transactions 2025-08-25 11:48:46 +02:00
Andrey Antukh c9393c0cfb Remove repeated/duplucated lookups on start-undo-transaction 2025-08-25 11:48:46 +02:00
Pablo Alba 6eeb55fb88 🐛 Fix after variant switch children layouts aren't updated (#7177) 2025-08-25 11:35:33 +02:00
Andrey Antukh c759afc10d 🔥 Remove unnecessary and broken unique-editors field
From the get-teams rpc method response
2025-08-25 11:33:34 +02:00
Marina López 090cb63568 🐛 Fix condition for members warning 2025-08-25 11:07:19 +02:00
Andrey Antukh f223831766 Refresh subscription info when member role is updated 2025-08-25 10:53:48 +02:00
Marina López 854f286364 ♻️ Fix subscriptions inconsistencies 2025-08-25 10:53:48 +02:00
Andrey Antukh 2846b80cf7 Add rpc methods for obtain editors 2025-08-25 10:53:12 +02:00
Andrey Antukh ad0ef82ffc 🎉 Add management http api 2025-08-25 10:53:12 +02:00
Elena Torró 3bb547fc45 🐛 Parse rx and ry SVG values correctly (#7176) 2025-08-25 10:44:11 +02:00
Pablo Alba c3b326d95e 🐛 Fix duplicating a page with variants should duplicate them (#7172)
* 🐛 Fix duplicating a page with variants should duplicate them

*  MR changes
2025-08-25 10:41:03 +02:00
Luis de Dios 8c1a97dac5 Make some improvements to the swap panel (#7174)
* 💄 Visual improvements in swap panel

* ♻️ Refactor search-bar component to use DS icons

* 📎 PR changes
2025-08-25 10:40:20 +02:00
Alejandro Alonso 4053e8c8db Merge pull request #7173 from penpot/elenatorro-11735-fix-text-auto-width
🐛 Fix paragraph layout width on autowidth
2025-08-22 15:16:40 +02:00
Xaviju ee86a3943d Update code to use design system icon (#7145) 2025-08-22 14:27:11 +02:00
Elena Torro 46b3e174ed 🐛 Fix paragraph layout width on autowidth 2025-08-22 13:55:24 +02:00
Alejandro Alonso c0c2c9489c Merge pull request #7170 from penpot/elenatorro-fix-text-tests
🔧 Update and fix text tests
2025-08-22 13:34:00 +02:00
Elena Torró c05c179d67 Merge pull request #7167 from penpot/azazeln28-feat-text-editor-wasm-playground
🎉 Add Text Editor WASM Playground
2025-08-22 12:47:41 +02:00
Aitor Moreno 596193d34d 🎉 Add missing styles on text leaves 2025-08-22 12:22:51 +02:00
Aitor Moreno 15eee0d8d8 🎉 Add Text Editor WASM Playground 2025-08-22 12:22:51 +02:00
Elena Torro 0b7444e8ff 🐛 Use SrcIn only when there is only one inner stroke, otherwise use erode filter 2025-08-22 09:54:15 +02:00
Elena Torro 96a91dc710 🔧 Add missing emoji ranges 2025-08-22 09:54:03 +02:00
Elena Torro 503d431d8e 🔧 Fix and update text use cases 2025-08-22 09:53:50 +02:00
Alejandro Alonso ed5875f29a Merge pull request #7154 from penpot/niwinz-staging-bug-1
🐛 Fix incorrect show request-access dialog on not-found on viewer
2025-08-22 09:19:47 +02:00
Yamila Moreno b12d44150b 📎 Fix and improve ci 2025-08-21 17:42:37 +02:00
Andrey Antukh 66f5eb57b9 🐛 Fix incorrect fills coerce to binary type when flag is disabled (#7169) 2025-08-21 15:11:14 +02:00
Pablo Alba 7023880e67 🐛 On a variants switch, keep the value of the rotation and its transformations (#7165) 2025-08-21 13:55:16 +02:00
Andrey Antukh 0e4cf23a93 🐛 Fix incorrect type coerce operations (#7168)
A regression introduced in previous commits that causes
a browser tab totally killed by memory usage.
2025-08-21 13:52:47 +02:00
Elena Torró 3f93b0d44b Merge pull request #7098 from penpot/superalex-fix-big-blur-2
🐛 Fix big blur rendering for wasm render
2025-08-21 09:23:26 +02:00
Elena Torró 4a6c3d6ad3 Merge pull request #7152 from penpot/superalex-update-skia-binaries
🎉 Update skia binares to 0.87.0
2025-08-21 09:10:37 +02:00
Alejandro Alonso 478439f055 🐛 Fix big blur rendering for wasm render 2025-08-21 08:47:25 +02:00
Alejandro Alonso 9ef2454210 🎉 Update skia binares to 0.87.0 2025-08-21 07:38:46 +02:00
Andrey Antukh a702fee3cd Merge pull request #7143 from penpot/luis-11856-adjust-component-title
 Adjust the appearance of the variant-related elements in the design tab
2025-08-20 17:41:50 +02:00
Pablo Alba cac639d267 🐛 Fix deleting a variant from assets panel (#7147) 2025-08-20 17:38:11 +02:00
Elena Torró 0daf548773 Merge pull request #7146 from penpot/superlaex-fix-open-path-calculation
🐛 Fix open path calculation
2025-08-20 16:38:17 +02:00
Alejandro Alonso 9249a5d4ea Merge pull request #7155 from penpot/elenatorro-use-erode-image-filter-for-inner-strokes-with-opacity
🐛 Fix inner strokes with opacity using erode instead of multiple blending modes
2025-08-20 15:55:50 +02:00
Andrey Antukh 4c83feaa31 Merge remote-tracking branch 'origin/staging' into develop 2025-08-20 13:56:58 +02:00
Luis de Dios 4d8ad19eea 💄 Improve the alignment, spaces and sizes of the variant elements 2025-08-20 13:51:04 +02:00
Luis de Dios e7e7d576b2 ♻️ Adapt title-bar component 2025-08-20 13:36:10 +02:00
Luis de Dios 7f2af1c355 Adjust component title 2025-08-20 13:36:10 +02:00
Andrey Antukh ad38a21053 🐛 Fix incorrect show request-access dialog on not-found on viewer
When a user is not-authenticated
2025-08-20 13:35:20 +02:00
Elena Torro 9bb92277e4 🐛 Fix inner strokes with opacity using erode instead of multiple blending modes 2025-08-20 13:32:20 +02:00
Alejandro Alonso 643621a389 Merge pull request #7119 from penpot/niwinz-develop-type-hints
 Add several performance enhancements
2025-08-20 12:58:43 +02:00
Alejandro Alonso ef8d2e7418 Merge pull request #7082 from penpot/niwinz-staging-changes-fix
♻️ Refactor schema references and openapi.json output
2025-08-20 12:55:16 +02:00
Andrey Antukh adffac4eec Merge remote-tracking branch 'origin/main' into staging 2025-08-20 12:49:31 +02:00
Andrey Antukh 575342b3bb ♻️ Use direct schemas instead of references
Only a very common use, basic types schemas should be used as
reference (with namespaced keywords)
2025-08-20 12:33:07 +02:00
Alejandro Alonso b6ecb4368e Merge pull request #7044 from penpot/niwinz-develop-refactor-versions-sidebar
♻️ Refactor versions sidebar
2025-08-20 12:00:28 +02:00
Pablo Alba 081df7fc03 🐛 Fix combine variants move items 2025-08-20 11:07:51 +02:00
Yamila Moreno 358343b4df 📎 Add build tag workflow and minor changes 2025-08-20 10:51:05 +02:00
Andrey Antukh d7c19325cc Add better type references naming on openapi output 2025-08-20 10:47:56 +02:00
Andrey Antukh b472a8ab19 Add general improvement to openapi and js-like doc output 2025-08-20 09:41:01 +02:00
Andrey Antukh 2b83d0d0e9 Add generative test case for openapi json serialization
This will prevent possible regression on introducing schemas without
generators or schema with generators that can't be serialized to json.
2025-08-20 09:38:57 +02:00
Andrey Antukh 911ac263fa 🐛 Use ::sm/any instead of any for get-file-fragment rpc method schema
The usage of `any?` predicate as-is uses the standard any generator that
causes to generate java.lang.Character instances created that are not
properly serialiable to JSON. The `::sm/any` schema delimits the
generator to a commonly known serializable types on json.
2025-08-20 09:38:55 +02:00
Andrey Antukh 545c78eb74 🐛 Add missing generators 2025-08-20 09:38:13 +02:00
Andrey Antukh 124b098c92 🔥 Remove already deprecated change spec 2025-08-20 09:38:13 +02:00
Andrey Antukh 43ed430475 📎 Update .gitignore file 2025-08-20 09:38:13 +02:00
Andrey Antukh edd3b1512e 🐛 Add missing attrs to add-component change schema 2025-08-20 09:38:13 +02:00
Andrey Antukh d9623c3c88 🐛 Add proper schema for decoding :obj on add-obj change 2025-08-20 09:38:13 +02:00
Andrey Antukh f052e31ff0 🐛 Fix incorrect handling of assign operation on changes protocol 2025-08-20 09:38:13 +02:00
Yamila Moreno 73dfe12ec9 📚 Update k8s documentation 2025-08-20 09:04:25 +02:00
Alejandro Alonso 0c3d73745e 🐛 Fix open path calculation 2025-08-20 09:00:04 +02:00
Aitor Moreno a6ecc4fb3c Merge pull request #7106 from penpot/niwinz-develop-modifiers-enhacements
 Several enhancements on how shape data is written on memory
2025-08-19 17:05:25 +02:00
Denys Kisil 625d9ab188 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 97.9% (1863 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-08-19 11:03:09 +00:00
Jun Fang e6cc15e19b 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 93.1% (1771 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-19 11:03:07 +00:00
王世阳 0af2cd6413 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 93.1% (1771 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-19 11:03:05 +00:00
IsCycleBai 443e0b0206 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 93.1% (1771 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-19 11:03:04 +00:00
Jun Fang a4fa5e9304 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 93.1% (1771 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-19 11:03:02 +00:00
IsCycleBai d94c311a1e 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 93.1% (1771 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-08-19 11:03:01 +00:00
Pablo Alba c0c8390a7d 🐛 Fix restoring component inside flex 2025-08-19 12:15:30 +02:00
Andrey Antukh f4be117219 🔥 Remove app.common.time/duration usage on frontend (#7139)
Is broken and not necessary; duration class is no longer
available on frontend code.
2025-08-19 10:36:34 +02:00
Andrey Antukh 7eb590e9fd ♻️ Refactor versions sidebar 2025-08-18 21:57:47 +02:00
Andrey Antukh 6588913141 ♻️ Remove several level of unnecesary allocation on writing text 2025-08-18 21:54:36 +02:00
Andrey Antukh 5c4a60aee7 Make mem write helpers receive offset as first arg 2025-08-18 21:54:36 +02:00
Andrey Antukh af02e12685 🎉 Add missing write-u32 helper to mem ns 2025-08-18 21:54:36 +02:00
Andrey Antukh 675864ce0b Remove incorrect usage of dm/get-prop 2025-08-18 21:54:36 +02:00
Andrey Antukh c55f3182d8 💄 Rename text-dimensions to get-text-dimensions 2025-08-18 21:49:00 +02:00
Andrey Antukh 0d6eac7656 💄 Add mainly cosmetic changes to set-shape-shadows
Mainly replace a loop with run! that used reduce as impl.
After measuring there are no real difference between using
the more complex loop and more simplier run!; in parity of
performance we prefer simplier approach.
2025-08-18 21:49:00 +02:00
Andrey Antukh 7acfd199aa 💄 Add mainly cosmetic changes to set-layout-child 2025-08-18 21:49:00 +02:00
Andrey Antukh 33d6f543a1 Remove several not necessary allocations from set-grid-layout-cells 2025-08-18 21:49:00 +02:00
Luis de Dios 4237ef572e 🐛 Fix use ellipsis when property names are too long (#7135) 2025-08-18 21:36:56 +02:00
Pablo Alba 6babea8b12 🐛 Fix alert for bad formula not showing in copies of variants (#7126)
* 🐛 Fix alert for bad formula not showing in copies of variants

*  MR changes
2025-08-18 21:36:29 +02:00
Pablo Alba 6b7f91c671 🐛 Fix weird resizing on combine variants with constraints scale (#7134) 2025-08-18 21:35:04 +02:00
Pablo Alba b3b183c151 🐛 Fix duplicate a variant when all have bad formulae crashes 2025-08-18 17:09:20 +02:00
Yamila Moreno 59f2ee87e6 📎 Improve github actions 2025-08-18 15:28:15 +02:00
Pablo Alba 6af8940a46 🐛 On variant create do not set sizing 2025-08-18 15:14:36 +02:00
Xavier Julian e1a1110f06 📎 Update changelog with new typography tokens 2025-08-18 13:50:14 +02:00
Pablo Alba 1dcf1e0b0f 🐛 Fix :show-content wasn't on components sync-attrs 2025-08-18 13:17:09 +02:00
Yamila Moreno 4c3e345c9c 📎 Fix github actions 2025-08-18 13:05:45 +02:00
Andrey Antukh a3b9a9f07b Remove reflection calls from buffer macros 2025-08-18 13:03:10 +02:00
Andrey Antukh 17ec360720 Add several missing type hints for avoid reflection and autoboxing 2025-08-18 13:03:10 +02:00
Andrey Antukh 89b67d59d5 Fix autoboxing on path type impl 2025-08-18 13:03:10 +02:00
Andrey Antukh 77be00014e Remove reflection on geom rect code 2025-08-18 13:03:10 +02:00
Andrey Antukh e336f287b6 Remove reflection on geom matrix code 2025-08-18 13:03:10 +02:00
Andrey Antukh 50aa6ff306 Remove reflection calls on binfile v3 code 2025-08-18 13:03:10 +02:00
Alejandro Alonso 56f162f219 Merge pull request #7133 from penpot/superalex-fix-create-variant-doesnt-work-2
🐛 Create variant doesn't work
2025-08-18 13:02:33 +02:00
Pablo Alba f7a0c4139a 🐛 Fix you can add a rect into a variant container (#7137) 2025-08-18 13:02:27 +02:00
Alejandro Alonso 7c39e321c4 Merge pull request #7136 from penpot/niwinz-develop-fix-inconsistencies-on-text-shortcuts
🐛 Fix several inconsistencies and duplicated shortcuts
2025-08-18 12:57:18 +02:00
Andrey Antukh ce6a863599 🐛 Fix several inconsistencies and duplicated shortcuts 2025-08-18 12:31:09 +02:00
Alejandro Alonso 832690e71e 🐛 Create variant doesn't work 2025-08-18 11:58:24 +02:00
Pablo Alba 7526cb0d71 🐛 Fix on variants rotation override is not preserving properly (#7120) 2025-08-18 11:57:58 +02:00
andrés gonzález 3292109ab0 🐛 Fix typos in modal about variant connections (#7122) 2025-08-18 11:47:23 +02:00
Elena Torró be376d2030 Merge pull request #7124 from penpot/ladybenko-11799-fix-remove-layout
🐛 Fix removing layout (wasm)
2025-08-18 09:52:22 +02:00
Alejandro Alonso 4d455b5e9f Merge pull request #7125 from penpot/elenatorro-11842-fix-groups-rendering-on-drag
🐛 Fix Group extrect calculation
2025-08-18 09:36:58 +02:00
Edgars Andersons 50ce28e378 🌐 Add translations for: Latvian
Currently translated at 98.4% (1872 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-08-15 21:02:02 +02:00
Yaron Shahrabani 1eee8e2ce8 🌐 Add translations for: Hebrew
Currently translated at 99.7% (1897 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-08-15 21:02:00 +02:00
Elena Torro 5e6ce9172f 🔧 Return always a shader on merge_fills for consistency 2025-08-14 16:08:03 +02:00
Elena Torró 03adbc2ae8 🔧 Update multiple emoji test and increase resulting image (#7113) 2025-08-14 15:47:55 +02:00
Belén Albeza e63a3f76f7 🐛 Fix open/close path detection in wasm (#7110)
* 🐛 Fix open/close path detection in wasm

* 💄 Remove leftover code
2025-08-14 15:45:09 +02:00
Elena Torro 6d42d456fb 🐛 Fix Group extrect calculation 2025-08-14 15:39:45 +02:00
Belén Albeza c818b6f88f 🐛 Fix layout and constraints not being cleared 2025-08-14 15:38:23 +02:00
Belén Albeza 3f3c7905b4 ♻️ Refactor wasm layout functions to their own submodule 2025-08-14 14:17:49 +02:00
Elena Torró e8dd13beb2 Merge pull request #7118 from penpot/superalex-fix-extrect-invalidation-for-texts
🐛 Fix extrect invalidation for texts
2025-08-14 13:43:42 +02:00
Elena Torró fc6b64aa68 Merge pull request #7121 from penpot/superalex-fix-set-corners
🐛 Fix set corners
2025-08-14 13:34:43 +02:00
Alejandro Alonso 5e0a2f66e3 🐛 Fix set corners 2025-08-14 11:50:40 +02:00
Pablo Alba 108d4fabba 🐛 Fix menu entry not showing on assets tab (#7115)
* 🐛 Fix menu entry not showing on assets tab

*  MR changes
2025-08-14 10:39:47 +02:00
Pablo Alba 2e277a39ca 🐛 Fix flick on design tab after variant switch (#7116) 2025-08-14 08:14:06 +02:00
Pablo Alba 814ec43714 🐛 Fix variants nesting loops (#7112)
* 🐛 Fix variants nesting loops

*  MR changes
2025-08-14 08:08:31 +02:00
Andrey Antukh 54bb9ea755 Merge remote-tracking branch 'origin/staging' into develop 2025-08-14 08:06:15 +02:00
Pablo Alba 374e921672 🐛 Fix variants change property name multiple selection 2025-08-13 17:29:03 +02:00
Alejandro Alonso 64e5ea93a0 🐛 Fix extrect invalidation for texts 2025-08-13 14:32:23 +02:00
Pablo Alba 2562d70880 🐛 Fix crash dragging external component into a variant without props (#7111) 2025-08-13 14:29:56 +02:00
Alejandro Alonso d99ef29152 Merge pull request #7029 from penpot/elenatorro-11691-fix-default-text-fill
🔧 Fix text default color and inner stroke opacity
2025-08-13 12:52:14 +02:00
Edgars Andersons d5a2cd9cd2 🌐 Add translations for: Latvian
Currently translated at 97.8% (1862 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-08-13 12:02:14 +02:00
Elena Torro 82d2889e96 🔧 Improve text strokes blending 2025-08-13 11:50:09 +02:00
Eva Marco ff2e845f2c 🐛 Fix double click on set name input (#7096) 2025-08-13 09:23:53 +02:00
Florian Schrödl ccd6ae5ade 🐛 Don't allow letter-spacing value with % (#7100) 2025-08-13 08:31:43 +02:00
Yaron Shahrabani 36bafc0d40 📎 Fix typo on translations 2025-08-12 17:58:10 +02:00
Pablo Alba f7746b8f94 Add create variants in bulk interactions from assets tab (#7102)
*  Add create variants in bulk interactions from assets tab

*  MR changes
2025-08-12 17:56:47 +02:00
Pablo Alba 537c5ca7b8 🐛 Fix missing selection after swap (#7104) 2025-08-12 17:56:03 +02:00
Pablo Alba 4901a80684 🐛 Fix flex layout everrides are not mantained on variant switch (#7105) 2025-08-12 17:55:29 +02:00
Pablo Alba 03b5d44a7c Merge pull request #7101 from penpot/palba-variants-bulk-root
🐛 Fix bad name on variants bulk when the parent is root
2025-08-12 17:04:13 +02:00
Andrey Antukh 8e51aa8df4 🐛 Fix regression on set-shape-children introduced in prev merge 2025-08-12 16:03:34 +02:00
Andrey Antukh 029a9674ca Merge pull request #7103 from penpot/niwinz-develop-modifiers-enhacements
♻️ Sanitize heap write and read operations
2025-08-12 13:11:02 +02:00
Alejandro Alonso 68cee1b1f1 Merge pull request #7076 from penpot/ladybenko-11755-fix-color-picker
🐛 Fix color picker not working with the new renderer
2025-08-12 11:57:21 +02:00
Aitor Moreno 3f74e230b2 Merge pull request #7092 from penpot/superalex-fix-artifacts-while-panning
🐛 Fix artifacts while panning in wasm render
2025-08-12 11:52:18 +02:00
Elena Torró 6bf1919f8d Merge pull request #7094 from penpot/superalex-fix-ctrl-b-for-editor-v2
🐛 Fix ctrl+b for editor v2
2025-08-12 11:36:24 +02:00
Andrey Antukh e69d61eaf4 Add facilities for work with dataview with common alases 2025-08-12 11:27:13 +02:00
Alejandro Alonso 2f83f22753 🐛 Fix artifacts while panning in wasm render 2025-08-12 11:23:13 +02:00
Andrey Antukh f9d757bb85 Move several mem write helpers to mem.heap32 ns
For simplify usage and make it clear the required addressing
is used for that functions
2025-08-12 10:53:02 +02:00
Andrey Antukh 6b6e80f4b8 🐛 Fix regression introduced on the set-grid-layout-cells fn
Incorrect data is used for calcultate the size
2025-08-12 10:33:50 +02:00
Andrey Antukh f32b92a5b0 Assign defaults on serializers instead on api
For make the operations more efficient
2025-08-12 10:33:08 +02:00
Andrey Antukh 761a0a7009 Improve memory write operations on set-grid-layout-rows 2025-08-12 10:32:35 +02:00
Andrey Antukh 129d3e61fa 🎉 Add missing wrap method on buffer abstraction 2025-08-12 10:30:02 +02:00
Stephan Paternotte aa94671002 🌐 Add translations for: Dutch
Currently translated at 100.0% (1902 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-08-12 10:02:02 +02:00
Nicola Bortoletto 52cf136f84 🌐 Add translations for: Italian
Currently translated at 96.7% (1841 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-08-12 10:02:01 +02:00
Yaron Shahrabani 808427795c 🌐 Add translations for: Hebrew
Currently translated at 99.6% (1895 of 1902 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-08-12 10:01:59 +02:00
Andrey Antukh 3f71734cb4 Remove unnecessary anon fn allocation on set-grid-layout-data
And remove incorrect use of dm/get prop for non statically known
attributes of shape
2025-08-12 09:59:18 +02:00
Andrey Antukh 9f14edb0d7 Remove unnecessary anonymouns fn allocation from set-flex-layout
And also removes usage of dm/get-prop for props that are known to be
not static
2025-08-12 09:59:18 +02:00
Andrey Antukh 7fa7a806a8 Remove unnecesary allocation of corners on wasm api set-shape 2025-08-12 09:59:18 +02:00
Andrey Antukh d364f4db62 ♻️ Sanitize heap write and read operations
Mainly improves the offset management making it less
error prone, encapsulating the write operation and offeset
management into write-* operations with proper asserts
for the expected heap type.
2025-08-12 09:59:18 +02:00
Andrey Antukh f2c431d029 Merge pull request #7041 from penpot/alotor-wasm-bools
 Add wasm boolean calculations
2025-08-12 08:07:18 +02:00
Belén Albeza 6a667c30d6 🐛 Fix color picking sometimes not picking color and/or getting stuck in a react infinite update loop 2025-08-11 17:02:12 +02:00
Alejandro Alonso de637fcf4e 🐛 Fix ctrl+b for editor v2 2025-08-11 14:56:04 +02:00
Aitor Moreno 132069472c Merge pull request #7067 from penpot/superalex-fix-frames-extrect-calculation
🐛 Fix frames extrect calculation
2025-08-11 13:57:29 +02:00
Elena Torro 172c6ad4b8 🔧 Set fill paint as transparent when there are no fills 2025-08-11 13:52:49 +02:00
Andrey Antukh 73a72ec1c7 💄 Add naming and docstring consistency fixes to wasm api 2025-08-11 12:49:01 +02:00
Andrey Antukh c39a8d84ac 💄 Abstract call to mem/free on wasm api ns 2025-08-11 10:30:14 +02:00
Andrey Antukh 027e5c64cc Reduce compexity on set-shape-children wasm api method 2025-08-11 10:30:14 +02:00
Andrey Antukh ba42c9b85e Add improved interop between wasm bool and common code 2025-08-11 10:30:14 +02:00
alonso.torres cd1be43384 Add support for boolean shapes 2025-08-11 10:30:14 +02:00
Andrey Antukh 6176027263 Import translatiosn from weblate
commit 17905edb9d24c9ae60921d94d1367a6e91df2b51
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Mon Aug 11 09:17:44 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 96.1% (1829 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b57270851a843c64af8698ea7f8300cab1be75cf
Author: Henrik Allberg <henrik@thexorb.com>
Date:   Mon Aug 11 09:19:56 2025 +0200

    🌐 Add translations for: Swedish

    Currently translated at 84.4% (1607 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/

commit 3aa31a7a52ba54126d3d14f6f24ea493f17ef99e
Author: Црнобог <68vuletic@gmail.com>
Date:   Mon Aug 11 09:19:49 2025 +0200

    🌐 Add translations for: Serbian

    Currently translated at 73.0% (1389 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sr/

commit c451f75888be5d27aac35c716375cf722ccb805a
Author: Alejandro Alonso <alejandro.alonso@kaleidos.net>
Date:   Mon Aug 11 09:20:32 2025 +0200

    🌐 Add translations for: Yoruba

    Currently translated at 62.7% (1193 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/

commit 64d084cfef057cdd635874aad961ad1f42cc16ab
Author: Alejandro Alonso <alejandro.alonso@kaleidos.net>
Date:   Mon Aug 11 09:17:58 2025 +0200

    🌐 Add translations for: Igbo

    Currently translated at 27.2% (518 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/

commit afee2e44bb22dfd28d55704cb1c387bf33b271ec
Author: Revenant <mohdmuizz22@yahoo.com>
Date:   Mon Aug 11 09:18:44 2025 +0200

    🌐 Add translations for: Malay

    Currently translated at 35.7% (680 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ms/

commit 27a43f53a486f9794e3d739793ca03cf11888240
Author: Alejandro Alonso <alejandro.alonso@kaleidos.net>
Date:   Mon Aug 11 09:17:27 2025 +0200

    🌐 Add translations for: Hausa

    Currently translated at 66.1% (1259 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/

commit 76d634a8da529ca27ff4f50d044ef8077b995b42
Author: Stephan Paternotte <stephan@paternottes.net>
Date:   Mon Aug 11 09:19:06 2025 +0200

    🌐 Add translations for: Dutch

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/

commit eacdded92d1c8be56117e8d5ca0cf99db0d6b506
Author: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv>
Date:   Mon Aug 11 09:18:35 2025 +0200

    🌐 Add translations for: Latvian

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/

commit 162163d566ef2ff89c3f96e0dbddfc24ea89bbe0
Author: Ņikita K <nikita.kozlovs@gmail.com>
Date:   Mon Aug 11 09:18:31 2025 +0200

    🌐 Add translations for: Latvian

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/

commit 01275a3458f485aeef190bf6588e0e45e8fad334
Author: Suhwan Kim <jgk9282@gmail.com>
Date:   Mon Aug 11 09:18:19 2025 +0200

    🌐 Add translations for: Korean

    Currently translated at 11.4% (218 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ko/

commit 03ba18cda687c3738bdcb6f49fd179eb449b50a3
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Aug 11 09:20:29 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit ec77d8ada6f02abd03676febc1b2974c91bc907c
Author: al0cam <benjaminsikac@gmail.com>
Date:   Mon Aug 11 09:17:46 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 85.2% (1621 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit fce74589887b1439e73324fea8e145273a1a9236
Author: Zvonimir Juranko <zjuranko@gmail.com>
Date:   Mon Aug 11 09:17:46 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 85.2% (1621 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit e16c2c3118755810d07738671cbd2f7c6452e328
Author: TheScientistPT <joao.ed.reis.gomes@gmail.com>
Date:   Mon Aug 11 09:19:29 2025 +0200

    🌐 Add translations for: Portuguese (Portugal)

    Currently translated at 83.5% (1589 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/

commit 7a394c7d4e3d2d43c93218f2e191919a1c23c864
Author: Dário <dariogomes@gmail.com>
Date:   Mon Aug 11 09:19:29 2025 +0200

    🌐 Add translations for: Portuguese (Portugal)

    Currently translated at 83.5% (1589 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/

commit 32c157b0c377fbcefeb83c1f1559d32ed935367f
Author: Amerey.eu <info@amerey.eu>
Date:   Mon Aug 11 09:15:59 2025 +0200

    🌐 Add translations for: Czech

    Currently translated at 84.9% (1615 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/

commit 3abf6a572727a8a93d6e0fac5f037c9383f213b0
Author: Mikel Larreategi <mlarreategi@codesyntax.com>
Date:   Mon Aug 11 09:16:45 2025 +0200

    🌐 Add translations for: Basque

    Currently translated at 61.4% (1169 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/eu/

commit b9165b23d30672f41324dc0135e6d519c0b704d1
Author: Radek Sawicki <radek@sqrc.pl>
Date:   Mon Aug 11 09:19:09 2025 +0200

    🌐 Add translations for: Polish

    Currently translated at 59.9% (1141 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pl/

commit b02c4cc7df984212eee183f813c7f5f16cd4c9eb
Author: Nicola Bortoletto <nicola.bortoletto@live.com>
Date:   Mon Aug 11 09:18:08 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit 4eae1c68c2c17d438282a3d5f246609c97bf0064
Author: Valentina Chapellu <valentina.chapellu@gmail.com>
Date:   Mon Aug 11 09:18:04 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit 225484e99e58fdf32dd888f70bb95f3070b16dc2
Author: Ahmad HosseinBor <123hozeifeh@gmail.com>
Date:   Mon Aug 11 09:16:52 2025 +0200

    🌐 Add translations for: Persian

    Currently translated at 41.0% (780 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/

commit 56ad686a1bf5634d52929790a315263fa77f2999
Author: william chen <william.fromtw@gmail.com>
Date:   Mon Aug 11 09:20:46 2025 +0200

    🌐 Add translations for: Chinese (Traditional Han script)

    Currently translated at 85.1% (1620 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/

commit 8d7c4c97556f7e5f001183535df09a1c796cdec1
Author: im424 <424@live.hk>
Date:   Mon Aug 11 09:20:46 2025 +0200

    🌐 Add translations for: Chinese (Traditional Han script)

    Currently translated at 85.1% (1620 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/

commit 00eea4f7f6fb6a02cd4256a5269d76d5ee678c3f
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Mon Aug 11 09:17:37 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 96.2% (1830 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit 23bed6b374ceee65a2c257ea3c32b9a8726a619d
Author: Linerly <linerly@proton.me>
Date:   Mon Aug 11 09:17:52 2025 +0200

    🌐 Add translations for: Indonesian

    Currently translated at 90.3% (1719 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/

commit ab3fb9f1b6b0596f564a1e4ba580a55b2ee5a556
Author: Mahmoud A. Rabo <Mahmoud@s3geeks.com>
Date:   Mon Aug 11 09:15:34 2025 +0200

    🌐 Add translations for: Arabic

    Currently translated at 58.8% (1120 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/

commit e1dd8905c6290e7f634de0e0e5228ac38cdbce4d
Author: AlexTECPlayz <alextec70@outlook.com>
Date:   Mon Aug 11 09:19:36 2025 +0200

    🌐 Add translations for: Romanian

    Currently translated at 68.1% (1296 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/

commit fb3401c258f195d23a0f33461cd596eaf10b8751
Author: George Lemon <george@getvasco.com>
Date:   Mon Aug 11 09:19:35 2025 +0200

    🌐 Add translations for: Romanian

    Currently translated at 68.1% (1296 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/

commit b8e351851472436c70ecf51a59f87dfbc038c2cf
Author: Allan Nordhøy <epost@anotheragency.no>
Date:   Mon Aug 11 09:18:56 2025 +0200

    🌐 Add translations for: Norwegian Bokmål

    Currently translated at 8.7% (166 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nb_NO/

commit 322b67dabc5332d11e004d1ddbd1109c612a8460
Author: Stas Haas <stas@girafic.de>
Date:   Mon Aug 11 09:16:14 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.1% (1714 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit c46e2d73039f3dcb6c7840060a7086829287ff80
Author: Pablo Alba <pablo.alba@kaleidos.net>
Date:   Mon Aug 11 09:16:12 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.1% (1714 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit fbc774fe28c2e051c5f7926135955ad39146eff4
Author: Eranot <renato.konflanz@unochapeco.edu.br>
Date:   Mon Aug 11 09:19:23 2025 +0200

    🌐 Add translations for: Portuguese (Brazil)

    Currently translated at 67.0% (1276 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/

commit a8f110374dc910462a7e83010b93f01c5ef5c514
Author: 王世阳 <wangshiyangchina@gmail.com>
Date:   Mon Aug 11 09:20:38 2025 +0200

    🌐 Add translations for: Chinese (Simplified Han script)

    Currently translated at 72.0% (1370 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/

commit 9a31a6239be28d43157132bfa9d6d7d2543b26ec
Author: Anonymous <noreply@weblate.org>
Date:   Mon Aug 11 09:20:38 2025 +0200

    🌐 Add translations for: Chinese (Simplified Han script)

    Currently translated at 72.0% (1370 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/

commit 024ff5c9ed52bca1112828512c60ee3049d956c0
Author: Merih Güz <iletisim@merihguz.com>
Date:   Mon Aug 11 09:20:17 2025 +0200

    🌐 Add translations for: Turkish

    Currently translated at 75.6% (1438 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/

commit 5de31af3af03876f5af4bdba91c17b4889386fe9
Author: Çağlar Yeşilyurt <grch@mm.st>
Date:   Mon Aug 11 09:20:17 2025 +0200

    🌐 Add translations for: Turkish

    Currently translated at 75.6% (1438 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/

commit 92ad28a35d98930a895bf9c070aeecce3056a643
Author: The_BadUser <vanjavs41@gmail.com>
Date:   Mon Aug 11 09:19:43 2025 +0200

    🌐 Add translations for: Russian

    Currently translated at 75.8% (1442 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/

commit ed9522d50281d56df8ccf7257f028e1ff317b4bc
Author: Vin <k3kelm4vw@mozmail.com>
Date:   Mon Aug 11 09:19:43 2025 +0200

    🌐 Add translations for: Russian

    Currently translated at 75.8% (1442 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/

commit 52b2837ef77449020f18d5fb4a3c8af539fe477e
Author: Anonymous <noreply@weblate.org>
Date:   Mon Aug 11 09:16:19 2025 +0200

    🌐 Add translations for: Greek

    Currently translated at 27.0% (515 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/el/

commit 15656760a1546bc877149797fd322f489cd9030a
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Mon Aug 11 09:17:17 2025 +0200

    🌐 Add translations for: French

    Currently translated at 96.1% (1829 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit d6952275de5e9a83bfe4d2238147c80bcfa1e35b
Author: Unreal Vision <unrealvisionyt@gmail.com>
Date:   Mon Aug 11 09:17:14 2025 +0200

    🌐 Add translations for: French

    Currently translated at 96.1% (1829 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 828f535facc0a9d3c586fed027b23443b0698ee2
Author: Louis Chance <contact@louischance.com>
Date:   Mon Aug 11 09:17:13 2025 +0200

    🌐 Add translations for: French

    Currently translated at 96.1% (1829 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 0656f7836a630f409d9fc575e132f250eec7f5a3
Author: Pablo Alba <pablo.alba@kaleidos.net>
Date:   Mon Aug 11 09:17:13 2025 +0200

    🌐 Add translations for: French

    Currently translated at 96.1% (1829 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 0497701d1ca4134549c5b8bd4419466f87111fcd
Author: Anonymous <noreply@weblate.org>
Date:   Mon Aug 11 09:16:31 2025 +0200

    🌐 Add translations for: Spanish

    Currently translated at 97.2% (1850 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/

commit 22c77ac2bf5fa9f5945bd680b97fafe5779b7324
Author: Andrey Antukh <niwi@niwi.nz>
Date:   Mon Aug 11 09:16:25 2025 +0200

    🌐 Add translations for: Spanish

    Currently translated at 97.2% (1850 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/

commit cdd3b23d7ccb1cf00fa3b66788df69449f012de7
Author: Aryiu <aryiu@users.noreply.hosted.weblate.org>
Date:   Mon Aug 11 09:15:52 2025 +0200

    🌐 Add translations for: Catalan

    Currently translated at 56.6% (1078 of 1902 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ca/

commit 41ab7871188c93469c9e94ade3c495a7380450e1
Author: Hosted Weblate <hosted@weblate.org>
Date:   Mon Aug 11 09:15:14 2025 +0200

    🌐 Update translation files

    Updated by "Cleanup translation files" hook in Weblate.

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/

commit 4f46b6b42a9660cdceaf231e9e20615b4e64c8fe
Merge: 58bd7c6bd4 2239711f15
Author: Hosted Weblate <hosted@weblate.org>
Date:   Mon Aug 11 09:15:10 2025 +0200

    🌐 Merge branch 'origin/develop' into Weblate.

commit 2239711f15f62c29cb7eb1981874ae81019d4b3e
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jul 29 07:59:24 2025 +0200

    🌐 Add translations for: German

    Currently translated at 92.6% (1721 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 836068ca8cbc3e72a96bfa4be1d239ad2d516d32
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 08:41:57 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1c2958825198a6194bf99db5cc50a3a386df98f6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:58:53 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit c0f884b12350225c897e0f0843e09a02ea1c6639
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:56:18 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4692c0019c9d09a1019b6605d748d8f3144edf68
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:51:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6f74a30b90e39d1cb998dd7a37931d1a55a1bbfb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:44:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e657464742dc9f151313d4450025fd3ac57a6732
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:39:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a41e8ed7865083123703710fbdacaa5ee9e506fa
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:34:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4d27f8cb303b27f9ddba30d2a2fbc2163c920694
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:27:34 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b5369fa2380beaa190265df4847c81af713bf348
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:03:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4d639b62f29e469abce6f463208079e73f70b146
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:02:50 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ddb5fe44ee8e12f6592b4cbf3f0c9f5a1be3f695
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:49:11 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e702b556e2ae798e22cc966f6eff421cbc6fda81
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:42:32 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f74f8f96e5af79900df3508ee5551d8f85e63558
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:40:51 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f7db30ac4d1855b646e0ece454a2153aaaaeb309
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:33:44 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 07fd2353f670b981601f5d51aca3f013483af9a6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:29:04 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a8b578cbaa69c6bd1c8319b43a96799267eae98d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:25:49 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 12a9927f7b5b257d2909be6c73af72a58ddfb8b6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:24:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2314eab73fa7fcb7b479c16dfb0551a188a7c46e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:23:25 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 88b721c600ff72a2e8bdc4c301f0745a3174fbd5
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:17:17 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 14b4124e4cdf4ced296d234b4dbd76afa5a6166c
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:12:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ccaa8da28c65d0256027f2fbf3e556797ef901fb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:16:45 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e517e2f0152383549a46ac72b4ebc94752dbdbf3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:12:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit cb359962bc2aff200c501d8466db1097e93d074e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:03:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 71205252a4f4aadeed2cf0b6da41722a6d2e38a8
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:51:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit fe56a9fdd177c4ca624b9dbb88bfa30dc31a92f4
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:48:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 3f2d9bf68f466c21557e045fcce7aab76441d9da
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:22:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e41af1831d5f749e341ce163f520ce2dff5fc7de
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:21:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit bb8c9129c9c3cff69ce59b7bfe86560bba977e09
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:20:40 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit efd5849fc1e6e76fdf91b21b0a69be2b7e89cbcf
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:18:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 0b2403b8bd2682e4a2cfdfe879b34c83ed5a6913
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:13:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 8412b71915047cf0d436279b89f93bc0c77f1c20
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:03:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit fe49d3ccd5697ef830bb71086557f8a249c3ed6e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:02:20 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 48f39509b7a93e501115c3d4cc5d8bb76361b197
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:00:51 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 634df69814a209dc830ffebe471be4e821b849fa
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 06:57:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f2ddf8266af3f4d56351883f5ad845327834fe8b
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 06:50:16 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 27d24e6438d34ecfea68b87d0322d51b05cfdb68
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:49:01 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit bc0afe1b8053bb0258b7b18905714d3cc070c55f
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:40:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4f6e0bd6778e8105d248f298319b9771c99347a1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:36:54 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit faae4f0c18ee77bd2330936448823f3caa7df881
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:35:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5e43671edccc5786f46afba4b1e5c32de39d05a6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:29:10 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6aec58217b608f125e327d9b7048fa163984b8b3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:24:10 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a20b28bfe96618e507259b6294f6791bc30e1ed3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:22:34 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 59ee55aa262c7248b9f85f2fdd5b217132b71975
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:21:42 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 3825a6a464c6015f383e90be9155c81da8a1f2c1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:17:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7fe83a8f347a65e101ec1e01ef30d8791057718f
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:39:05 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7eab18e15ec35f4e7a4eb33fbec17c8fd61c5a65
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:28:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 66a42acd37df0eecfa409fd460b279565b7c7292
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:23:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 9eb17f7172141e366eb338b128bf2229deeb1246
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:23:08 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2626d45eb96da5e47a5b90c5ba4aa13362b8eea2
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Mon Jul 7 11:56:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f3719edaa121a9c880cf5f670ed18bb6f1806378
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Mon Jul 7 11:43:06 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 67001aafa71aa9dba8c5e1e927c9ce305e14070e
Author: Stephan Paternotte <stephan@paternottes.net>
Date:   Mon Jul 7 07:02:49 2025 +0200

    🌐 Add translations for: Dutch

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/

commit 4d4f5265c00c384a9ba7bb4bf49461279ca536fd
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:37:46 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f3cc8ae33cbb6941434cf71d7d4b4e037aa0a594
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:19:25 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2d3c0e4a342e1a051d0bf07a580b99a53c12078a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:09:45 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4e394b39599e4854ff53374f2ea900d3ea0d49f7
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:27:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ebef981a652cba41d988e4f93f685c640ecd5efe
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:24:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6ca42826654fec0fec38631252547323a2226b93
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:23:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5bd8c711b63e967ef2657106e8f2e1498a2596e0
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:18:58 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6356069174f83939690751f965b641709a7a708e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:22:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a3107d059ec8cf8762cca2e258e09595651c7e4c
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:09:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit c0f2f43b0669a38c05c1bae783359d70bb53957d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:01:49 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit d725f53e9ea0c22302485fccc3289e8546124570
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 07:54:46 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6a50a0db429792a41861281f41b1bafd2bebd64d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 07:53:55 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ee382c0d777c65240fdea46222e8249ae131c538
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:12:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 181d3083e1e03b7b640545479c666a629c3cdacb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:10:31 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e898179de59525f259d5ccfae59f4f2fca309f3d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:05:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b16596c7b9c3814478c0149e172329a3aa074dd1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 10:47:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ae70f146dc1b187d53bcba575e4c365b9077d42a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:40:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4be87babf5605533eed83cee114cf9171c7985cb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:39:23 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1f4261a6e5686224cfedfebf385572d565b573c7
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:27:26 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 50db4f8bc8fbac02ef880add7e5e2330ab06dab0
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:17:03 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit d2897327a2aeef26cfc4da4ca63291af7504921e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:13:15 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5c7f7e4179ab2e510a6fcabb86f66bd1b2827bd6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:09:13 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b1d5bbcd0db8a96967cc067bb29f147b2174eb8a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:06:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2cea4705c972032fc7be9b4fbc047a5422846b13
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:03:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 44d01ba7054306027f006fcfca9afad18e1f08ac
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:59:32 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 50a2f98ac6573d47237578dd1bce6d848cc83c78
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:58:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit cef20ab80a59ca5da6a14be2c6b3b6fc57ea4aab
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:58:15 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 214f9c2bb1e75fe99fe7407e77eb273d437782cd
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:54:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4f9f7a38f811b1948fb9a34fc57f4e9d0ddf1c1b
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:46:36 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit be7e929ae70344d6f4349eae569a3012bdfe2e2a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:12:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4db59803d86f78a35538b089f122291a2577716d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:06:20 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 14aad9500e7c901573effbb5fdff2a8a0bcca036
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:05:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 85014b458caa951d798e45000b02f25b9fdad271
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:02:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 002a623606025367f33870c8074e2ea486315b5d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:01:16 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b72efd33dfeadefcb9f63e68e59cb16fb6366483
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:57:48 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ab75e51c330561eb132eef8354908fcbd8b3ec09
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:57:18 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2bae66951b930e872c06443b78c36c225c564438
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:45:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1151123aeabfcad65410bd44d6e04685d21ba5ed
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:40:17 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 97a4f34e599d4f1ac6da3029cab3d993ee3ab4da
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:35:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a2e1ec123bce716ea0bcc2542809398ec3f65cb1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:30:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 9e72ab6771857ec0814cd0fa22dac6bf1470901a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:28:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 87849347286683544dba68e9a1cbdc9fc06b7ff3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:22:13 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1e359566b91bf23270a34ed398b058a635a7de7b
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:35:56 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 312ee3f7036615fce7f88b880accce71605470dc
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:31:42 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 7ca4e07d12e419e11b2d0079acb8133beac1315a
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:29:26 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 3e1db432d2fef95542ea9d0d5e4c164ab2190e11
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:22:29 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit f6121a315de283a8a0b2e163aeaff48851a42a25
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:19:07 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit e1720aae76015fba1754575dc743a3021040e04a
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:15:08 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit a399deae487af3db9127adc89aa39bd813296ecf
Author: Corentin Noël <tintou@noel.tf>
Date:   Fri Jun 27 14:26:07 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1855 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 1c81b4c9b3bac3bc210b16ba9691cc5e2917c896
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Thu Jun 26 11:31:04 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1854 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 0c756f50747fd9a0031e398d6bba4903d402519a
Author: Stas Haas <stas@girafic.de>
Date:   Sun Jun 22 11:35:49 2025 +0200

    🌐 Add translations for: German

    Currently translated at 92.6% (1720 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit eaa9d3e2bccf4f2fb9aa9f23fee530c7efe5e720
Author: al0cam <benjaminsikac@gmail.com>
Date:   Thu Jun 19 14:48:06 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 87.4% (1624 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit 8fc4f74bf8e8e754f780a155c2eb8f6d9c51f3b4
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 17 09:06:30 2025 +0200

    🌐 Add translations for: German

    Currently translated at 91.9% (1708 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 4d396ef7f7b1be2ec5f57d671717ae1dbb9419ab
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 16 21:34:47 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 28dcfb52785fecca69a4bf22ac7222b330d29f9f
Author: al0cam <benjaminsikac@gmail.com>
Date:   Mon Jun 16 07:54:05 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 87.2% (1620 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit 680c9a1a0ebe3b323cf1bc7d3f92d7e0bda53c7a
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Mon Jun 16 18:51:01 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit 6e3f6ed276b74df94a64af294ea4ca80ac1c43f6
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Sat Jun 14 12:53:48 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1854 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit feabe2414490e29db3fbabf20a00d890df9907d8
Author: Nicola Bortoletto <nicola.bortoletto@live.com>
Date:   Fri Jun 13 08:26:35 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit a3b49ee9510b53ae5b010bfbcfb5022a79f6fe1b
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Thu Jun 12 08:17:05 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 97.0% (1803 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit c775173ec6f5d22964e9b79cc2a80e3cbd08fccb
Author: Stas Haas <stas@girafic.de>
Date:   Thu Jun 12 10:56:20 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.7% (1686 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 249051c087fd679f7d5a5c604a65c1eb6fb377d0
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Wed Jun 11 11:24:21 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 96.9% (1800 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit d33693bf9f8aa419b7688deed03a26926d7b5338
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 10 15:05:19 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.4% (1680 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit c3d4852c7f61ac398aa505eb0f1b3e0a5e6a6f49
Author: Unreal Vision <unrealvisionyt@gmail.com>
Date:   Tue Jun 10 14:59:59 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.7% (1853 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 16e6fadb8dd1c2285c64fde7079d6b33c8a7f6a6
Author: Rudra Harsh <harshrudra020@gmail.com>
Date:   Mon Jun 9 15:46:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 1.2% (23 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7653bc6060bc0f6ad0664549cc46a15d7e72ccd7
Author: Stephan Paternotte <stephan@paternottes.net>
Date:   Tue Jun 10 05:46:17 2025 +0200

    🌐 Add translations for: Dutch

    Currently translated at 99.7% (1853 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/

commit 0f65e960b1de543b9133e6742a732e46967d1f83
Author: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv>
Date:   Tue Jun 10 11:51:03 2025 +0200

    🌐 Add translations for: Latvian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/

commit 05f12ae1bf59986cdc8f5e98ac14c33d6c0e79e2
Author: Nicola Bortoletto <nicola.bortoletto@live.com>
Date:   Mon Jun 9 23:14:58 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit 2e16f175f57b757a5de4f387caf58331ec5dc822
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Mon Jun 9 19:01:14 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 96.6% (1794 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit a202b8b663c3a32af7e21f662907d8365fb587c5
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 10 14:57:32 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.0% (1672 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit ffe9682df9a5a65dc1f582844e566cf3eff32a08
Author: Unreal Vision <unrealvisionyt@gmail.com>
Date:   Tue Jun 10 14:56:54 2025 +0200

    🌐 Add translations for: French

    Currently translated at 98.7% (1833 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit f8f4abe8007491f5392bfb1ab5cbfba618e22700
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Mon Jun 9 19:44:09 2025 +0200

    🌐 Add translations for: French

    Currently translated at 98.7% (1833 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit bb6fee5a9ba86eb823c22fae22a49afdcc36c659
Author: Rudra Harsh <harshrudra020@gmail.com>
Date:   Mon Jun 9 15:21:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 0.5% (11 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 55b4c5c07827d4b6eda2ffcc71caca81e6f36534
Author: Madalena Melo <madalena.melo@kaleidos.net>
Date:   Mon Jun 9 11:52:50 2025 +0200

    🌐  Added translation for: Hindi
2025-08-11 09:21:55 +02:00
Andrey Antukh 58bd7c6bd4 Import translations from weblate
commit 2239711f15f62c29cb7eb1981874ae81019d4b3e
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jul 29 07:59:24 2025 +0200

    🌐 Add translations for: German

    Currently translated at 92.6% (1721 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 836068ca8cbc3e72a96bfa4be1d239ad2d516d32
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 08:41:57 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1c2958825198a6194bf99db5cc50a3a386df98f6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:58:53 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit c0f884b12350225c897e0f0843e09a02ea1c6639
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:56:18 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4692c0019c9d09a1019b6605d748d8f3144edf68
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:51:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6f74a30b90e39d1cb998dd7a37931d1a55a1bbfb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:44:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e657464742dc9f151313d4450025fd3ac57a6732
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:39:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a41e8ed7865083123703710fbdacaa5ee9e506fa
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:34:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4d27f8cb303b27f9ddba30d2a2fbc2163c920694
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:27:34 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b5369fa2380beaa190265df4847c81af713bf348
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:03:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4d639b62f29e469abce6f463208079e73f70b146
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 07:02:50 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ddb5fe44ee8e12f6592b4cbf3f0c9f5a1be3f695
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:49:11 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e702b556e2ae798e22cc966f6eff421cbc6fda81
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:42:32 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f74f8f96e5af79900df3508ee5551d8f85e63558
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:40:51 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f7db30ac4d1855b646e0ece454a2153aaaaeb309
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 10 06:33:44 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 100.0% (1857 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 07fd2353f670b981601f5d51aca3f013483af9a6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:29:04 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a8b578cbaa69c6bd1c8319b43a96799267eae98d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:25:49 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 12a9927f7b5b257d2909be6c73af72a58ddfb8b6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:24:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2314eab73fa7fcb7b479c16dfb0551a188a7c46e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:23:25 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 88b721c600ff72a2e8bdc4c301f0745a3174fbd5
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:17:17 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 14b4124e4cdf4ced296d234b4dbd76afa5a6166c
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 09:12:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ccaa8da28c65d0256027f2fbf3e556797ef901fb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:16:45 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e517e2f0152383549a46ac72b4ebc94752dbdbf3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:12:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit cb359962bc2aff200c501d8466db1097e93d074e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 08:03:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 71205252a4f4aadeed2cf0b6da41722a6d2e38a8
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:51:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit fe56a9fdd177c4ca624b9dbb88bfa30dc31a92f4
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:48:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 3f2d9bf68f466c21557e045fcce7aab76441d9da
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:22:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e41af1831d5f749e341ce163f520ce2dff5fc7de
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:21:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit bb8c9129c9c3cff69ce59b7bfe86560bba977e09
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:20:40 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit efd5849fc1e6e76fdf91b21b0a69be2b7e89cbcf
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:18:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 0b2403b8bd2682e4a2cfdfe879b34c83ed5a6913
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:13:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 8412b71915047cf0d436279b89f93bc0c77f1c20
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:03:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit fe49d3ccd5697ef830bb71086557f8a249c3ed6e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:02:20 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 48f39509b7a93e501115c3d4cc5d8bb76361b197
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 07:00:51 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 634df69814a209dc830ffebe471be4e821b849fa
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 06:57:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f2ddf8266af3f4d56351883f5ad845327834fe8b
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Wed Jul 9 06:50:16 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 27d24e6438d34ecfea68b87d0322d51b05cfdb68
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:49:01 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit bc0afe1b8053bb0258b7b18905714d3cc070c55f
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:40:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4f6e0bd6778e8105d248f298319b9771c99347a1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:36:54 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit faae4f0c18ee77bd2330936448823f3caa7df881
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:35:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5e43671edccc5786f46afba4b1e5c32de39d05a6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:29:10 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6aec58217b608f125e327d9b7048fa163984b8b3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:24:10 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a20b28bfe96618e507259b6294f6791bc30e1ed3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:22:34 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 59ee55aa262c7248b9f85f2fdd5b217132b71975
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:21:42 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 3825a6a464c6015f383e90be9155c81da8a1f2c1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 09:17:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 82.7% (1536 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7fe83a8f347a65e101ec1e01ef30d8791057718f
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:39:05 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7eab18e15ec35f4e7a4eb33fbec17c8fd61c5a65
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:28:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 66a42acd37df0eecfa409fd460b279565b7c7292
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:23:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 9eb17f7172141e366eb338b128bf2229deeb1246
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Tue Jul 8 07:23:08 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2626d45eb96da5e47a5b90c5ba4aa13362b8eea2
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Mon Jul 7 11:56:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f3719edaa121a9c880cf5f670ed18bb6f1806378
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Mon Jul 7 11:43:06 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 52.2% (970 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 67001aafa71aa9dba8c5e1e927c9ce305e14070e
Author: Stephan Paternotte <stephan@paternottes.net>
Date:   Mon Jul 7 07:02:49 2025 +0200

    🌐 Add translations for: Dutch

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/

commit 4d4f5265c00c384a9ba7bb4bf49461279ca536fd
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:37:46 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit f3cc8ae33cbb6941434cf71d7d4b4e037aa0a594
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:19:25 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2d3c0e4a342e1a051d0bf07a580b99a53c12078a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 12:09:45 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4e394b39599e4854ff53374f2ea900d3ea0d49f7
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:27:07 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ebef981a652cba41d988e4f93f685c640ecd5efe
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:24:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6ca42826654fec0fec38631252547323a2226b93
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:23:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5bd8c711b63e967ef2657106e8f2e1498a2596e0
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 09:18:58 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6356069174f83939690751f965b641709a7a708e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:22:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a3107d059ec8cf8762cca2e258e09595651c7e4c
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:09:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 40.3% (750 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit c0f2f43b0669a38c05c1bae783359d70bb53957d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 08:01:49 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit d725f53e9ea0c22302485fccc3289e8546124570
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 07:54:46 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 6a50a0db429792a41861281f41b1bafd2bebd64d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Fri Jul 4 07:53:55 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ee382c0d777c65240fdea46222e8249ae131c538
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:12:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 181d3083e1e03b7b640545479c666a629c3cdacb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:10:31 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit e898179de59525f259d5ccfae59f4f2fca309f3d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 11:05:22 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b16596c7b9c3814478c0149e172329a3aa074dd1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 10:47:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ae70f146dc1b187d53bcba575e4c365b9077d42a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:40:00 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4be87babf5605533eed83cee114cf9171c7985cb
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:39:23 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1f4261a6e5686224cfedfebf385572d565b573c7
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:27:26 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 50db4f8bc8fbac02ef880add7e5e2330ab06dab0
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:17:03 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit d2897327a2aeef26cfc4da4ca63291af7504921e
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:13:15 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 5c7f7e4179ab2e510a6fcabb86f66bd1b2827bd6
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:09:13 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b1d5bbcd0db8a96967cc067bb29f147b2174eb8a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:06:52 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2cea4705c972032fc7be9b4fbc047a5422846b13
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 09:03:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 44d01ba7054306027f006fcfca9afad18e1f08ac
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:59:32 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 50a2f98ac6573d47237578dd1bce6d848cc83c78
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:58:33 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit cef20ab80a59ca5da6a14be2c6b3b6fc57ea4aab
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:58:15 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 214f9c2bb1e75fe99fe7407e77eb273d437782cd
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:54:59 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4f9f7a38f811b1948fb9a34fc57f4e9d0ddf1c1b
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:46:36 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit be7e929ae70344d6f4349eae569a3012bdfe2e2a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:12:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 4db59803d86f78a35538b089f122291a2577716d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:06:20 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 14aad9500e7c901573effbb5fdff2a8a0bcca036
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:05:29 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 85014b458caa951d798e45000b02f25b9fdad271
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:02:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 002a623606025367f33870c8074e2ea486315b5d
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 08:01:16 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit b72efd33dfeadefcb9f63e68e59cb16fb6366483
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:57:48 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit ab75e51c330561eb132eef8354908fcbd8b3ec09
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:57:18 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 2bae66951b930e872c06443b78c36c225c564438
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:45:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1151123aeabfcad65410bd44d6e04685d21ba5ed
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:40:17 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 97a4f34e599d4f1ac6da3029cab3d993ee3ab4da
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:35:28 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit a2e1ec123bce716ea0bcc2542809398ec3f65cb1
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:30:19 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 9e72ab6771857ec0814cd0fa22dac6bf1470901a
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:28:38 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 87849347286683544dba68e9a1cbdc9fc06b7ff3
Author: VKing9 <vaibhavrathod2282@gmail.com>
Date:   Thu Jul 3 07:22:13 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 27.3% (508 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 1e359566b91bf23270a34ed398b058a635a7de7b
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:35:56 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 312ee3f7036615fce7f88b880accce71605470dc
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:31:42 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 7ca4e07d12e419e11b2d0079acb8133beac1315a
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:29:26 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 3e1db432d2fef95542ea9d0d5e4c164ab2190e11
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:22:29 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit f6121a315de283a8a0b2e163aeaff48851a42a25
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:19:07 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit e1720aae76015fba1754575dc743a3021040e04a
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 30 14:15:08 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit a399deae487af3db9127adc89aa39bd813296ecf
Author: Corentin Noël <tintou@noel.tf>
Date:   Fri Jun 27 14:26:07 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1855 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 1c81b4c9b3bac3bc210b16ba9691cc5e2917c896
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Thu Jun 26 11:31:04 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1854 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 0c756f50747fd9a0031e398d6bba4903d402519a
Author: Stas Haas <stas@girafic.de>
Date:   Sun Jun 22 11:35:49 2025 +0200

    🌐 Add translations for: German

    Currently translated at 92.6% (1720 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit eaa9d3e2bccf4f2fb9aa9f23fee530c7efe5e720
Author: al0cam <benjaminsikac@gmail.com>
Date:   Thu Jun 19 14:48:06 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 87.4% (1624 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit 8fc4f74bf8e8e754f780a155c2eb8f6d9c51f3b4
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 17 09:06:30 2025 +0200

    🌐 Add translations for: German

    Currently translated at 91.9% (1708 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 4d396ef7f7b1be2ec5f57d671717ae1dbb9419ab
Author: Denys Kisil <ossenjoyer@proton.me>
Date:   Mon Jun 16 21:34:47 2025 +0200

    🌐 Add translations for: Ukrainian (ukr_UA)

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/

commit 28dcfb52785fecca69a4bf22ac7222b330d29f9f
Author: al0cam <benjaminsikac@gmail.com>
Date:   Mon Jun 16 07:54:05 2025 +0200

    🌐 Add translations for: Croatian

    Currently translated at 87.2% (1620 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/

commit 680c9a1a0ebe3b323cf1bc7d3f92d7e0bda53c7a
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Mon Jun 16 18:51:01 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit 6e3f6ed276b74df94a64af294ea4ca80ac1c43f6
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Sat Jun 14 12:53:48 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.8% (1854 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit feabe2414490e29db3fbabf20a00d890df9907d8
Author: Nicola Bortoletto <nicola.bortoletto@live.com>
Date:   Fri Jun 13 08:26:35 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit a3b49ee9510b53ae5b010bfbcfb5022a79f6fe1b
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Thu Jun 12 08:17:05 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 97.0% (1803 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit c775173ec6f5d22964e9b79cc2a80e3cbd08fccb
Author: Stas Haas <stas@girafic.de>
Date:   Thu Jun 12 10:56:20 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.7% (1686 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit 249051c087fd679f7d5a5c604a65c1eb6fb377d0
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Wed Jun 11 11:24:21 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 96.9% (1800 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit d33693bf9f8aa419b7688deed03a26926d7b5338
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 10 15:05:19 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.4% (1680 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit c3d4852c7f61ac398aa505eb0f1b3e0a5e6a6f49
Author: Unreal Vision <unrealvisionyt@gmail.com>
Date:   Tue Jun 10 14:59:59 2025 +0200

    🌐 Add translations for: French

    Currently translated at 99.7% (1853 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit 16e6fadb8dd1c2285c64fde7079d6b33c8a7f6a6
Author: Rudra Harsh <harshrudra020@gmail.com>
Date:   Mon Jun 9 15:46:43 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 1.2% (23 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 7653bc6060bc0f6ad0664549cc46a15d7e72ccd7
Author: Stephan Paternotte <stephan@paternottes.net>
Date:   Tue Jun 10 05:46:17 2025 +0200

    🌐 Add translations for: Dutch

    Currently translated at 99.7% (1853 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/

commit 0f65e960b1de543b9133e6742a732e46967d1f83
Author: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv>
Date:   Tue Jun 10 11:51:03 2025 +0200

    🌐 Add translations for: Latvian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/

commit 05f12ae1bf59986cdc8f5e98ac14c33d6c0e79e2
Author: Nicola Bortoletto <nicola.bortoletto@live.com>
Date:   Mon Jun 9 23:14:58 2025 +0200

    🌐 Add translations for: Italian

    Currently translated at 99.9% (1856 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/

commit 2e16f175f57b757a5de4f387caf58331ec5dc822
Author: Yaron Shahrabani <sh.yaron@gmail.com>
Date:   Mon Jun 9 19:01:14 2025 +0200

    🌐 Add translations for: Hebrew

    Currently translated at 96.6% (1794 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/

commit a202b8b663c3a32af7e21f662907d8365fb587c5
Author: Stas Haas <stas@girafic.de>
Date:   Tue Jun 10 14:57:32 2025 +0200

    🌐 Add translations for: German

    Currently translated at 90.0% (1672 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/

commit ffe9682df9a5a65dc1f582844e566cf3eff32a08
Author: Unreal Vision <unrealvisionyt@gmail.com>
Date:   Tue Jun 10 14:56:54 2025 +0200

    🌐 Add translations for: French

    Currently translated at 98.7% (1833 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit f8f4abe8007491f5392bfb1ab5cbfba618e22700
Author: Ingrid Pigueron <ingridp.uxr@gmail.com>
Date:   Mon Jun 9 19:44:09 2025 +0200

    🌐 Add translations for: French

    Currently translated at 98.7% (1833 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/

commit bb6fee5a9ba86eb823c22fae22a49afdcc36c659
Author: Rudra Harsh <harshrudra020@gmail.com>
Date:   Mon Jun 9 15:21:41 2025 +0200

    🌐 Add translations for: Hindi

    Currently translated at 0.5% (11 of 1857 strings)

    Translation: Penpot/frontend
    Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/

commit 55b4c5c07827d4b6eda2ffcc71caca81e6f36534
Author: Madalena Melo <madalena.melo@kaleidos.net>
Date:   Mon Jun 9 11:52:50 2025 +0200

    🌐  Added translation for: Hindi
2025-08-11 09:14:05 +02:00
Andrey Antukh f02667e031 Merge remote-tracking branch 'origin/staging' into develop 2025-08-11 09:12:03 +02:00
Alejandro Alonso 8e0a6e4123 🐛 Fix auto height is fixed in the HTML inspect tab for text elements (#7078) 2025-08-11 09:07:43 +02:00
Marina López 0131cd6f8b Display the total price of the subscription and the cap amount (#7088) 2025-08-11 09:07:24 +02:00
Alejandro Alonso 99b40cecf2 Revert "🐛 Fix big blur rendering for wasm render"
This reverts commit c7a4c67d83.
2025-08-09 08:44:52 +02:00
Alejandro Alonso c7a4c67d83 🐛 Fix big blur rendering for wasm render 2025-08-09 08:42:55 +02:00
Florian Schrödl c29a8cb0c4 Implement font-weight token (#7089) 2025-08-08 11:11:18 +02:00
Andrey Antukh 288a7b21d6 Merge tag '2.9.0-RC8' 2025-08-08 09:47:42 +02:00
andrés gonzález 32bd08533d 💄 Remove slide about overrides in the release notes (#7086) 2025-08-08 09:46:40 +02:00
Yamila Moreno c1aae12327 📎 Improve gh actions 2025-08-07 18:08:25 +02:00
Yamila Moreno 23a6f4b7c1 📎 Improve gh actions 2025-08-07 18:07:47 +02:00
Yamila Moreno a9f4fe84fa 📎 Improve gh actions 2025-08-07 17:51:20 +02:00
Andrey Antukh 133e6e1e68 Merge tag '2.9.0-RC7' 2025-08-07 16:30:30 +02:00
Andrey Antukh f7832585dc Add tests for snapshot locking (#7085) 2025-08-07 16:27:43 +02:00
Eva Marco e34bfb50a8 🐛 Fix font variant names for source sans pro font (#7087) 2025-08-07 16:25:15 +02:00
Andrey Antukh 6abd045273 🐛 Add missing generator for token-set file change operation (#7080)
* 🐛 Add missing generator for token-set file change operation

* 🐛 Use ::sm/any instead of :any for on get-file-data-for-thumbnail rpc method

Mainly because :any will use a very generic generator that can generate
instances of Character that are not directly serializable to JSON
2025-08-07 12:36:14 +02:00
Florian Schrödl 0a106c2604 🐛 Fix import of borderWidth (#7084) 2025-08-07 12:16:18 +02:00
Andrey Antukh 8f5f88743b Normalize font variant naming for google fonts (#7083) 2025-08-07 11:14:40 +02:00
Florian Schrödl 9562d2f1f0 Allow font-families with surrounding quotation marks (#7081) 2025-08-07 11:13:04 +02:00
Andrey Antukh ea482f16c8 💄 Add minor cosmetic changes to dashboard sidebar components (#7052)
* 💄 Change component decl style of sidebar-team-switch

* 💄 Change component decl style of sidebar-search

* 💄 Add general cosmetic changes to sidebar components
2025-08-07 10:57:46 +02:00
Alejandro Alonso 50634e1a4c 🐛 Fix selection lost when using keyboard 2025-08-07 09:28:17 +02:00
Andrey Antukh 56de96d25b Merge remote-tracking branch 'origin/staging' into develop 2025-08-07 08:04:40 +02:00
Luis de Dios 5d1c20c47c 🐛 Fix focus new added property (#7065) 2025-08-07 07:47:17 +02:00
Marina López 778a608854 🐛 Fix tooltip for icon plans from team dropdown (#7075) 2025-08-07 07:43:49 +02:00
andrés gonzález 7de8e10721 🐛 Fix changelog link (#7070) 2025-08-07 07:43:11 +02:00
andrés gonzález 80f41c4a69 🐛 Fix issue where Alt + arrow keys shortcut interferes with letter-spacing (#7071) 2025-08-07 07:42:33 +02:00
Luis de Dios a3557a81e4 🐛 Fix add space between the name and the index of new properties (#7068) 2025-08-07 07:41:33 +02:00
Luis de Dios 0a02e526ee Treat empty names as a malformed formula (#7073) 2025-08-07 07:41:07 +02:00
Luis de Dios db9349e764 💄 Style improvements in the swap panel (#7077) 2025-08-07 07:40:38 +02:00
Belén Albeza 60903f349f 🐛 Fix color picker not working with the new renderer 2025-08-06 18:00:48 +02:00
Florian Schrödl b91e955486 Text decoration fixes (#7066)
*  Show text more options when apply text decoration token

* 🐛 Fix placeholder
2025-08-06 16:23:38 +02:00
Marina López a76a9fae41 🐛 Fix an unused translation (#7074) 2025-08-06 13:28:02 +02:00
Andrey Antukh f7cfbdd229 🐛 Comment the problematic migration 2025-08-05 22:05:52 +02:00
Andrey Antukh e28d2842f6 🐛 Revert the revert of orientation detection on media
This reverts commit 515cbf7bef.
2025-08-05 22:03:09 +02:00
Andrey Antukh ccc3ca0948 Disable virtual threads on http server 2025-08-05 20:34:47 +02:00
Andrey Antukh 515cbf7bef 🐛 Revert orientation detection on media 2025-08-05 19:30:01 +02:00
Andrey Antukh c320cbc47b 🐛 Revert to semaphore based climit impl 2025-08-05 19:17:35 +02:00
Yamila Moreno 6166f45a7f Merge pull request #7069 from penpot/yms-update-k8s-documentation
📚 Update k8s documentation
2025-08-05 15:44:59 +02:00
Yamila Moreno c103eb86db 📚 Update k8s documentation 2025-08-05 13:55:39 +02:00
Alejandro Alonso 61d93d69b1 Merge pull request #7048 from penpot/elenatorro-11704-fix-symbols-font
 Include symbols support
2025-08-05 13:40:16 +02:00
Belén Albeza d5abf34538 🐛 Fix text style change not being applied (#7036)
* 🐛 Fix text styles not being applied to current cursor

* 🔧 Add text file for bug 11552

* 📚 Update changelog
2025-08-05 13:35:54 +02:00
Alejandro Alonso 7efc297cd9 Merge pull request #7053 from penpot/ladybenko-11678-compact-keep-ratio-flag
 Compact fill serialization (opacity + flags)
2025-08-05 13:29:49 +02:00
Alejandro Alonso 98522a390e 🐛 Fix frames extrect calculation 2025-08-05 13:25:25 +02:00
Andrey Antukh 46969585ed Disable native buffers usage on xnio
A temporal change for investigate native memory leak
2025-08-04 22:13:08 +02:00
Andrey Antukh 47882c5419 Add missing parameter on climit instance creation 2025-08-04 19:53:56 +02:00
andrés gonzález 019d5e083a 💄 Change copys at the 2.9 release slides (#7063) 2025-08-04 19:53:50 +02:00
Belén Albeza 6fc949844d Use 1 byte to store opacity in gradient fills 2025-08-04 14:13:40 +02:00
Andrey Antukh 97e8c9250a Merge remote-tracking branch 'origin/staging' into develop 2025-08-04 14:10:57 +02:00
Andrey Antukh 85f6cf32ae 🐛 Several bugfixes (#7062)
* 🐛 Fix incorrect status validation on subscription internal api

* 🐛 Make the shortcuts overwritting optional
2025-08-04 13:54:29 +02:00
Marina López ded8e39e73 🐛 Fix hidden button in subscribe modal when there is a large number of teams (#7061) 2025-08-04 13:16:58 +02:00
Florian Schrödl 551313d3de Text case fixes (#7058)
*  Add placeholder

*  Remove status icon
2025-08-04 12:13:57 +02:00
Andrey Antukh e730200873 🐛 Fix pinned project ordering on dashboard sidebar (#7060) 2025-08-04 12:07:19 +02:00
Andrey Antukh 433e61bc4e Merge remote-tracking branch 'origin/staging' into develop 2025-08-04 11:52:24 +02:00
Andrei Fëdorov 818b03d8f2 Add text decoration token (#7049) 2025-08-04 10:47:09 +02:00
Belén Albeza ae3aef8dcc Use existing space for storing image fill flags 2025-08-04 10:42:56 +02:00
Luis de Dios 1b30325640 🐛 Fix adjust focus in select component (#7024) 2025-08-04 10:21:17 +02:00
Yamila Moreno 44d626d578 📎 Fix typo in documentation 2025-08-01 16:32:42 +02:00
Francis Santiago 4501d13961 📚 Clarify OpenShift requirements (#6937)
* 📚 Clarify OpenShift requirements

* 📚 Remove the click for expanding
2025-08-01 16:26:04 +02:00
Elena TorróandAndrey Antukh c8f5ec4698 ♻️ Refactor dropdown-menu and make dropdown visibility exclusive (#6956)
* 🐛 Fix having multiple dropdown menus opened on dashboard page

* ♻️ Refactor dropdown-menu

Make it follow new standards and make it external api more usable,
not depending on manually provided list of ids.

This also implements the autoclosing of "other" active/open
dropdown-menu (or other similar components).

* 📎 Add PR feedback changes

* 🐛 Fix incorrect event handling on project-menu

* 🐛 Fix unexpected exception

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-08-01 16:14:15 +02:00
Pablo AlbaandAndrey Antukh 07b15819d4 🎉 Add the ability to create variants from a selection (#7045)
* 🎉 Add the ability to create variants from a selection

* 📎 Add PR feedback changes

* 💄 Add minor cosmetic changes

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-08-01 15:39:46 +02:00
Elena Torró f519c6ef46 Center team settings properly (#7021) 2025-08-01 15:17:32 +02:00
Juan de la Cruz baa1cfb2f8 🎉 Add 2.9 release slides (#7019) 2025-08-01 14:59:11 +02:00
Eva Marco 905699d15a Add info to apply-token events (#7050) 2025-08-01 14:00:30 +02:00
Eva Marco fe53869308 🐛 Fix small details on number token application (#7051) 2025-08-01 13:52:09 +02:00
Elena Torro c69ee35e18 Include symbols support 2025-08-01 13:41:12 +02:00
Luis de Dios 8d5ee92f16 🐛 Fix show 'add new property' from menu when a variant is selected (#7042) 2025-08-01 13:21:19 +02:00
Andrey Antukh e55d184d2b Merge remote-tracking branch 'origin/staging' into develop 2025-08-01 13:13:56 +02:00
Andrey Antukh 50076bac83 Merge remote-tracking branch 'origin/main' into staging 2025-08-01 13:10:52 +02:00
Eva Marco e976714964 🐛 Fix error on inspect tab with texts (#7032) 2025-08-01 13:03:43 +02:00
Brandon Currell 44bc4b7fa4 🐳 Add missing package in the exporter Docker image (#7026)
penpot-exporter requires poppler-utils for exporting to a PDF, but it is missing.
Added the package to the Dockerfile in the RUN section where dependencies are
being installed.

Signed-off-by: Brandon Currell <brandon+git@currell.pw>
2025-08-01 13:00:51 +02:00
Brandon Currell ce0d6ffda2 🐳 Add missing package in the exporter Docker image (#7026)
penpot-exporter requires poppler-utils for exporting to a PDF, but it is missing.
Added the package to the Dockerfile in the RUN section where dependencies are
being installed.

Signed-off-by: Brandon Currell <brandon+git@currell.pw>
2025-08-01 12:59:39 +02:00
Yamila Moreno 5c14f486d7 🐳 Update Imagemagick version 2025-08-01 12:49:11 +02:00
Andrey Antukh 0cbd980b68 🎉 Add imagemagick docker image build scripts (#6925)
* 🎉 Add imagemagick docker image build scripts

* 📎 Add PR feedback changes
2025-08-01 12:48:47 +02:00
Andrey Antukh bc2308f2ce Merge pull request #7011 from penpot/lmcrean-lmcrean-milestones-version-lock
🎉 Add the ability to lock/unlock of file versions
2025-08-01 12:40:07 +02:00
Laurie Crean 0b47a366ab Implement version locking functionality for file snapshots
Signed-off-by: Laurie Crean <lmcrean@gmail.com>
2025-08-01 11:41:30 +02:00
Andrey Antukh 1892fa6782 Merge pull request #7043 from penpot/niwinz-develop-refactor-time-helpers
♻️ Refactor time related namespaces
2025-08-01 11:35:07 +02:00
Andrey Antukh 6f35b7db24 Add reader tag support for tokens related types 2025-08-01 11:20:01 +02:00
Andrey Antukh 4d9e070bcd Add reader tag support for types path data 2025-08-01 11:20:01 +02:00
Andrey Antukh 61fe8e8d8e Add reader tag support for geom matrix 2025-08-01 11:20:01 +02:00
Andrey Antukh 0934095e96 Add reader tag support for geom point 2025-08-01 11:20:01 +02:00
Andrey Antukh eba2ff7d8d Add impl for Inst protocol for FileTime class 2025-08-01 11:20:01 +02:00
Andrey Antukh 283eb0419c ♻️ Refactor time related namespaces
Mainly removes the custom app.util.time namespace
from frontend and backend and normalize all to use
the app.common.time namespace
2025-08-01 11:20:01 +02:00
Elena Torro 9a0c36c442 🐛 Fix default color when neither fill nor background color is set 2025-07-31 16:17:13 +02:00
luisδμ ff1d26294a 🐛 Fix create properties with a default value instead of an empty one (#7033) 2025-07-31 15:01:51 +02:00
Eva Marco 63bfbbb3c6 🐛 Fix typography token context menu (#7038) 2025-07-31 15:00:14 +02:00
Eva Marco 95dda2b1af 🐛 Fix stroke width token application (#7039) 2025-07-31 14:59:48 +02:00
Elena Torró 76d725559e Set default new text fill color depending on background color (#6998) 2025-07-31 12:31:54 +02:00
luisδμ d7ec8ccbc0 🐛 Fix property name cannot be empty (#7030) 2025-07-31 12:27:10 +02:00
Juanfran 6def5e285b 🐛 Apply design review fixes for variant connection help (#11186) (#7016) 2025-07-31 12:26:04 +02:00
Andrey Antukh 5170872961 Merge pull request #7031 from penpot/eva-fix-export-button-width
🐛 Fix export button width on inspect tab
2025-07-31 12:25:03 +02:00
Andrey Antukh b46e9ee065 Merge remote-tracking branch 'origin/staging' into develop 2025-07-31 12:22:14 +02:00
Andrey Antukh 871ca68e1e 📎 Allow revert commits on github commit checker 2025-07-31 12:14:29 +02:00
Andrey Antukh 0ab896fc76 Revert " Highlight first font in font selector search, apply on Enter/click"
This reverts commit e62567d09e.
2025-07-31 12:14:29 +02:00
Andrey Antukh 6a4b548457 Revert "🐛 Fix font selector highlight inconsistency (#6990)"
This reverts commit 708a40bff1.
2025-07-31 12:14:29 +02:00
Elena Torró 0457ca4fe5 Use 'desvincular' instead of 'desacoplar' (#7020) 2025-07-31 11:50:46 +02:00
Elena Torro 083be7df88 🐛 Fix focus editor check 2025-07-31 10:05:37 +02:00
Eva Marco 695a399941 🐛 Fix export button width on inspect tab 2025-07-31 09:30:46 +02:00
luisδμ 200b69fae2 📚 Improve documentation for combobox and select in the storybook (#7006) 2025-07-31 09:05:54 +02:00
Eva Marco a32463fada 🐛 Fix tooltip position after several shows and hides (#7022) 2025-07-31 09:00:05 +02:00
luisδμ 3b04cd37ff 🐛 Fix empty values should not have dimmed text (#7015) 2025-07-30 18:06:39 +02:00
Eva Marco 5d44c88988 🐛 Fix token pill not showing position application on dimension token type (#7018) 2025-07-30 14:24:10 +02:00
luisδμ 4d688b1d55 🐛 Fix title for button when trying to remove last variant property (#7017) 2025-07-30 13:28:42 +02:00
Andrey Antukh e43b6fb0b7 Merge pull request #6992 from penpot/niwinz-artboard-defaults
 Add defaults for artboard drawing
2025-07-30 13:27:54 +02:00
Andrey Antukh 7895f03447 💄 Add minor cosmetic changes 2025-07-30 13:11:28 +02:00
Marina López 1f42b2f72d Show preset name when an option is selected 2025-07-30 13:11:28 +02:00
Andrey Antukh f4adfe56be Add defaults for artboard drawing 2025-07-30 13:11:28 +02:00
Alejandro Alonso 33a679fbc0 Merge pull request #6940 from penpot/niwinz-develop-inplace-import
🎉 Add support for in-place binfile import
2025-07-30 12:42:37 +02:00
Pablo Alba 9db67cc5e8 🐛 Fix bad swap slot after two swaps (#6962)
* 🐛 Fix bad swap slot after two swaps

*  MR changes
2025-07-30 12:35:27 +02:00
luisδμ 9834f0596b 🐛 Fix move empty variant values to the end when component is selected (#7009)
* 🐛 Move empty variant values to the end when component is selected

* 📎 PR changes
2025-07-30 12:29:51 +02:00
Andrey Antukh ce87d797d1 Merge pull request #7014 from penpot/niwinz-staging-regression-3
🐛 Fix several issues related to font/text related tokens
2025-07-30 12:25:28 +02:00
Andrey Antukh 37cec8891f 🎉 Add inplace binfile import support 2025-07-30 12:23:40 +02:00
Andrey Antukh fd62141c04 Disable pointer-map feature (temporary)
Because the upcoming refactor changes several aspects
of that feature and it not make sense to continue have
this active for now, until refactor is merged.
2025-07-30 12:06:41 +02:00
Andrey Antukh 4bdba6894d Add get-with-sql helper to db module 2025-07-30 12:06:41 +02:00
Andrey Antukh 6c7fef29a8 Improve file data type constructor 2025-07-30 12:06:41 +02:00
Andrey Antukh a77edc5aa2 Add better uri constructor function 2025-07-30 12:06:41 +02:00
Andrey Antukh 7fde1436e1 🐛 Add missing styles to the empty node on editor-v1 2025-07-30 11:45:39 +02:00
Andrey Antukh e1c5a32fcb 💄 Fix indentation style on generate-unapply-tokens 2025-07-30 11:45:19 +02:00
Andrey Antukh b262e6a46f 🐛 Fix incorrect condition on checking text shape attrs 2025-07-30 11:44:07 +02:00
Yamila Moreno 31f37a20e3 Merge pull request #7013 from penpot/yms-simplify-gh-actions
 Simplify gh-actions workflows
2025-07-30 11:42:39 +02:00
alonso.torres 06b4ae5c96 🐛 Fix problem with layout update touching geometry 2025-07-30 11:27:15 +02:00
Alejandro Alonso a3e24785d3 Merge pull request #7003 from penpot/alotor-fix-transform
🐛 Fix wasm transform issues
2025-07-30 11:10:54 +02:00
Yamila Moreno 78102210a5 Simplify gh-actions workflows 2025-07-30 10:45:01 +02:00
Pablo Alba 7553d68100 🐛 Fix corner case of chained switch and libraries (#7008) 2025-07-30 08:44:27 +02:00
Andrey Antukh 2e726b62c3 📎 Update changelog 2025-07-29 20:07:05 +02:00
Andrey Antukh 02acd81c2c 🐛 Add missing profile prop to access style component (#7007)
* 💄 Fix request-access component style

* 🐛 Add missing profile prop to access style component
2025-07-29 16:04:15 +02:00
Andrey Antukh 44daa1cf65 Merge remote-tracking branch 'origin/staging' into develop 2025-07-29 15:22:14 +02:00
Andrey Antukh bae2de75ff Merge branch 'main' into staging 2025-07-29 15:21:58 +02:00
Andrey Antukh b68c426cd1 🐛 Fix exception on fills menu when binary-fills flag is active
And multiple shapes are selected.
2025-07-29 15:10:32 +02:00
Andrey Antukh 5161ef15bf 🐛 Fix regression on show access request dialog (#7005) 2025-07-29 14:58:02 +02:00
Andrey Antukh bdbaa6d597 Merge remote-tracking branch 'origin/staging' into develop 2025-07-29 14:34:35 +02:00
Eva Marco 36d3d94ec9 🐛 Fix X & Y position do not sincronize with tokens (#7004) 2025-07-29 14:32:06 +02:00
Andrey Antukh 0e675a725d 📎 Fix linter issues on frontend
Caused by the merge from staging to develop
2025-07-29 14:15:01 +02:00
Andrey Antukh 17447d7610 Remove restriction of duplicate bindings on mousetrap 2025-07-29 14:14:19 +02:00
Andrey Antukh 2a3046ba2e 📎 Fix linter issue on common 2025-07-29 14:10:49 +02:00
Andrey Antukh 54d76123d0 Merge remote-tracking branch 'origin/staging' into develop 2025-07-29 14:06:53 +02:00
Andrey Antukh 6ffbf08826 Merge pull request #6969 from penpot/andy-show-keyboard-distance
 Show distance between layers while moving them with the keyboard
2025-07-29 13:32:53 +02:00
andrés gonzálezandAndrey Antukh 708a40bff1 🐛 Fix font selector highlight inconsistency (#6990)
* 🐛 Fix font selector highlight inconsistency

*  Add minor performance enhancements

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-07-29 13:12:54 +02:00
Andrey Antukh d84ee8bb65 Optimize mousetrap binding setup 2025-07-29 13:12:28 +02:00
Elena Torró a16f40cb73 Set page objects once on wasm render(#6994) 2025-07-29 13:00:40 +02:00
Andrey Antukh 02cff2740f Remove restriction of duplicate bindings on mousetrap 2025-07-29 12:51:39 +02:00
Andres Gonzalez 6049d97ed9 Display continously the distances between layers
When a user moves a layer with the keyboard.
2025-07-29 12:51:04 +02:00
Andrey Antukh 3f657a0c04 Merge pull request #6997 from penpot/alotor-fix-wasm-bugs
🐛 Fix wasm problems
2025-07-29 12:42:26 +02:00
Marina López efaf6573bd 📎 Update monetization texts (#7002) 2025-07-29 12:42:11 +02:00
alonso.torres 4b020dcc1a 🐛 Fix problem when changing size with user input 2025-07-29 12:16:08 +02:00
alonso.torres 223a468bbf 🐛 Fix problem when moving layout with measure input 2025-07-29 12:15:53 +02:00
alonso.torres ddd0e447f6 🐛 Fix problem when creating shapes after new page 2025-07-29 12:15:27 +02:00
alonso.torres 0c0c81e9a5 🐛 Fix problem with shape to path not working 2025-07-29 12:15:27 +02:00
Yamila Moreno 001bcbce59 Merge pull request #6995 from penpot/yms-update-imagemagick-version
🐳 Update Imagemagick version
2025-07-29 10:58:32 +02:00
Yamila Moreno c195c07a3f 🐳 Update Imagemagick version 2025-07-29 10:37:11 +02:00
Alejandro Alonso f5298f51e7 🐛 Fix the context menu always closes after any action (#6944) 2025-07-29 09:50:55 +02:00
Alejandro Alonso 46c440fef2 🐛 Fix remove color button in the gradient editor (#6993) 2025-07-28 17:48:05 +02:00
Aitor Moreno e6ac2c1159 Merge pull request #6880 from penpot/elenatorro-fix-editor-crash-on-deleting-entire-selection-firefox
🐛 Handle empty paragraph on entire selected text deletion
2025-07-28 17:39:25 +02:00
Florian Schrödl 4c605b8151 Implement text case token (#6978) 2025-07-28 17:36:06 +02:00
Elena Torro 2913899aa5 🐛 Fix auto-format on font 2025-07-28 17:31:36 +02:00
Elena Torro ecd3245612 🐛 Fix request render after pending calls have finished on set-objects 2025-07-28 17:31:36 +02:00
Xaviju dadeda4476 🐛 Display stroke properties in inspect tab (#6955) 2025-07-28 16:17:54 +02:00
Elena Torró d129557f77 Merge pull request #6988 from penpot/superalex-fix-render-wasm-visible-0-width-strokes
🐛 Fix visible 0 width strokes in wasm render
2025-07-28 14:36:07 +02:00
Alejandro Alonso e77f8b572a Merge pull request #6953 from penpot/superalex-fix-component-changes-not-propagated
🐛 Fix component changes not propagated
2025-07-28 12:53:37 +02:00
Alejandro Alonso ade5eecf80 🐛 Fix component changes not propagated 2025-07-28 12:38:09 +02:00
Elena Torró ff7e34e308 Merge pull request #6984 from penpot/superalex-fix-switching-theme-form-wasm-render
🐛 Fix switching theme for wasm render
2025-07-28 11:30:41 +02:00
Yamila Moreno 88055294a2 Reuse github workflows (#6989) 2025-07-28 09:03:47 +02:00
Alejandro Alonso e473f45048 🐛 Fix visible 0 width strokes in wasm render 2025-07-28 08:46:47 +02:00
Alejandro Alonso bcee670ac6 🐛 Fix switching theme for wasm render 2025-07-28 07:44:56 +02:00
andrés gonzález 97fc7702b8 📚 Improve and clarify 'Hide and lock layers' section (#6975) 2025-07-25 14:53:32 +02:00
andrés gonzálezandMadalena Melo 54fcd58531 📚 Add doc for resizing text (#6974)
* 📚 Add doc for resizing text

* 📚 Update docs for text resizing

Co-authored-by: Madalena Melo <madalena.melo@kaleidos.net>
Signed-off-by: andrés gonzález <andres.gonzalez79@gmail.com>

---------

Signed-off-by: andrés gonzález <andres.gonzalez79@gmail.com>
Co-authored-by: Madalena Melo <madalena.melo@kaleidos.net>
2025-07-25 13:20:52 +02:00
andrés gonzález b7a8747f00 📚 Add doc for tokens zip file import option (#6973) 2025-07-25 13:20:39 +02:00
Elena Torró b93e96a18d Merge pull request #6958 from penpot/superalex-fix-texts-bigger-than-selrects-in-multiple-tiles
🐛 Fix rendering texts bigger than their selrects in mutiple tiles
2025-07-25 13:14:29 +02:00
Alejandro Alonso b70f6af2df 🐛 Fix rendering texts bigger than their selrects in mutiple tiles 2025-07-25 12:56:57 +02:00
Andrey Antukh d00de7d5a4 Merge pull request #6960 from penpot/xaviju-remove-image-shape-type-fix
🐛 Remove image type from inspect tab panels
2025-07-25 12:49:57 +02:00
andrés gonzález 5ae4dde222 📚 Add font size token doc (#6972) 2025-07-25 12:30:56 +02:00
Elena Torro 0e20bb6271 🐛 Fix text width calculation 2025-07-25 12:27:26 +02:00
Xaviju 2fbd4b07e0 🐛 Remove image type from inspect tab panels 2025-07-25 12:08:01 +02:00
Elena Torró bd15ef4618 Merge pull request #6854 from penpot/ladybenko-11522-fix-missing-font
🐛 Fix missing font when pasting text
2025-07-25 11:55:56 +02:00
Belén Albeza af5b942e05 🐛 Fix copy/paste not working on follow up pastes 2025-07-25 09:53:48 +02:00
Belén Albeza 098fd9fb0f 🐛 Fix not picking up font style / variant in new renderer 2025-07-25 09:48:20 +02:00
Belén Albeza a242962113 🐛 Fix missing font when pasting text (editor v1) 2025-07-25 09:48:20 +02:00
Elena Torro 2b95e6b7a9 🐛 Fix update canvas background color 2025-07-25 09:19:59 +02:00
Florian Schroedl 58a843ea23 Remove token when applying tyopgraphic asset style 2025-07-24 17:51:53 +02:00
Florian Schroedl 4189d01844 Remove token when applying tyopgraphic asset style 2025-07-24 17:14:04 +02:00
Andrés Moya 57330f53e2 🔧 Use id instead of name for tokens crud 2025-07-24 15:21:18 +02:00
Florian Schroedl f6b97af148 🐛 Fix spacing menu not available in dimensions token 2025-07-24 15:20:10 +02:00
Florian Schroedl 1c79e726af 🐛 Fix spacing menu not available in dimensions token 2025-07-24 15:16:01 +02:00
Alejandro Alonso 76b7287bf1 Merge pull request #6864 from penpot/niwinz-staging-snapshot-migrations
 Add migrations handling on file snapshots
2025-07-24 11:41:17 +02:00
Andrey Antukh 019bc2f183 Add migrations handling on file snapshots 2025-07-24 11:40:54 +02:00
Florian Schroedl 8c96a617be Add test for spacing token application rules 2025-07-24 11:01:49 +02:00
Florian Schroedl 1f15e9b81e Fix spacing token for frame children 2025-07-24 11:01:49 +02:00
Alejandro Alonso f7627e515a Merge pull request #6876 from penpot/niwinz-develop-minor-changes-logical-deletion
 Change default status filtering for logical deletion
2025-07-24 10:58:49 +02:00
Andrey Antukh d08c94d5a6 Change default status filtering for logical deletion 2025-07-24 10:43:45 +02:00
Florian Schroedl cccea3dc71 Add test for spacing token application rules 2025-07-24 10:42:08 +02:00
Florian Schroedl c82c39caf3 Fix spacing token for frame children 2025-07-24 10:42:08 +02:00
Xaviju 01896501c1 🐛 Remove image type from inspect tab panels (#6959) 2025-07-24 09:37:38 +02:00
Andrey Antukh 33cf75e933 Merge remote-tracking branch 'origin/staging' into develop 2025-07-24 09:00:29 +02:00
Andrey Antukh 3f9a1525ca Merge pull request #6954 from penpot/alotor-fix-gradient-stroke
🐛 Fix opacity on stroke gradients
2025-07-24 08:59:02 +02:00
alonso.torres 52c1e227d5 🐛 Fix change from gradient to solid color 2025-07-24 08:58:48 +02:00
alonso.torres 955538b12a 🐛 Fix opacity on stroke gradients 2025-07-24 08:58:46 +02:00
Alonso Torres dfc8a1da4a Fix problem with booleans selection (#6950) 2025-07-24 08:57:02 +02:00
Alonso Torres 8254af27cb 🐛 Fix problem when changing between flex/grid layout (#6949) 2025-07-24 08:54:07 +02:00
Pablo Alba b477ca0508 🐛 Fix design review bugs on variants advanced retrieve (#6948) 2025-07-24 08:53:26 +02:00
Elena Torró f76391ecbb 🐛 Enable switch to system theme on options menu (#6946) 2025-07-24 08:43:03 +02:00
Andrés Moya c49e9fbf18 🐛 Fix last migration of token sets (#6957) 2025-07-24 08:42:16 +02:00
Marina López 122701ee7b 🐛 Fix modal submit button for unpaid or canceled subscriptions (#6947) 2025-07-24 08:41:39 +02:00
Andrés Moya 351362bb50 🐛 Fix migration from tokens lib version 1.2 2025-07-23 15:28:53 +02:00
Andrey Antukh 9a6989d2ca 📎 Fix linter issues introduced on merging staging into develop 2025-07-23 12:27:04 +02:00
Andrey Antukh 8aebe1a41e Merge remote-tracking branch 'origin/staging' into develop 2025-07-23 12:26:09 +02:00
Andrey Antukh 1acf78d57c Merge branch 'main' into staging 2025-07-23 12:09:37 +02:00
Andrey Antukh 523373dfa2 📎 Update .gitignore file 2025-07-23 12:09:15 +02:00
Andrés Moya f55e7d8165 🐛 Keep shape level groups for token sync later 2025-07-23 12:04:31 +02:00
Andrés Moya 9fdc6be465 🐛 Fix bad touched attributes when applying tokens to text shapes 2025-07-23 12:04:31 +02:00
Alejandro Alonso 9390c1e7be 🐛 Fix "Copy as SVG" generates different code from the Inspect panel (#6945) 2025-07-23 11:46:58 +02:00
Florian Schroedl (aider) d788a4d252 Implement new token-type :font-families 2025-07-23 11:26:28 +02:00
Eva Marco b20b272eae 📚 Update changelog 2025-07-23 09:53:49 +02:00
Aitor Moreno 2cddc6fb5b Merge pull request #6583 from penpot/niwinz-fills-binary-type
🎉 Fills as binary type
2025-07-23 09:26:26 +02:00
Alejandro Alonso d46b519524 🐛 Fix remove color button in the gradient editor (#6942) 2025-07-23 09:04:54 +02:00
Andrey Antukh 4effd375a9 Add several improvements to admin pannel 2025-07-23 08:33:33 +02:00
Andrey Antukh 4e753dc474 💄 Use resolved schemas instead of references
For several schemas on common types
2025-07-23 08:33:28 +02:00
Andrey Antukh fbf63b98c3 Reuse file data checkers on file validate ns 2025-07-23 08:33:23 +02:00
Marina López 3df557b370 ♻️ Remove the workaround for updating the subscription after subscribing (#6938) 2025-07-23 08:10:20 +02:00
Aitor Moreno cdb600b081 Remove unused code 2025-07-23 08:03:23 +02:00
Aitor Moreno ffb688696b 🎉 Add keep-aspect-ratio integration 2025-07-23 08:03:23 +02:00
Andrey Antukh 8bb210e7b6 🎉 Add binary fills integration 2025-07-23 08:03:23 +02:00
Andrey Antukh 9ee488009f ♻️ Add substantial refactor on how types are organized
This mainly affects types related to colors, fills and texts, moving library
based operations from color namespace.
2025-07-23 08:03:23 +02:00
Andrey Antukh 96d9b102b6 Add type hints on config ns 2025-07-23 07:32:11 +02:00
Andrey Antukh 16fba49937 Expose flags for common submodule 2025-07-23 07:32:11 +02:00
Andrey Antukh af99bd620c Use binary fills to write data to wasm memory 2025-07-23 07:32:11 +02:00
Andrey Antukh 8a58b9d459 Use new write-bool helper on fills metadata 2025-07-23 07:32:11 +02:00
Andrey Antukh e3c62075b8 Write keep-aspect-ration on fill binary format 2025-07-23 07:32:11 +02:00
Andrey Antukh 22a70eb5b2 🎉 Add write-bool helper to buffer ns helpers 2025-07-23 07:32:11 +02:00
Andrey Antukh 4e2998a366 ♻️ Rename fill to fills namespace 2025-07-23 07:32:11 +02:00
Andrey Antukh 158f759cde Add binary fills initialization on workspace fetch 2025-07-23 07:32:11 +02:00
Aitor Moreno 3e3be95420 Merge pull request #6927 from penpot/elenatorro-test-fix-text-shadows
🐛 Fix text shadows apply text opacity
2025-07-23 06:59:28 +02:00
Elena Torró b5808701ec Merge pull request #6873 from penpot/niwinz-develop-enhancements-1
 Add improvements for backend admin/debug page
2025-07-22 15:14:08 +02:00
XavijuandXavier Julian 35f3125fff 🐛 Fix null when copying shadow color on inspect tab (#6923)
Co-authored-by: Xavier Julian <xaviju@proton.me>
2025-07-22 14:49:36 +02:00
Elena Torro 5427d207cd 🐛 Fix text shadows apply text opacity 2025-07-22 14:34:10 +02:00
XavijuandXavier Julian ee23d72d13 🐛 Fix null when copying shadow color on inspect tab (#6923)
Co-authored-by: Xavier Julian <xaviju@proton.me>
2025-07-22 14:06:06 +02:00
Francis Santiago f22aa606ce 📚 Clarify OpenShift requirements (#6937)
* 📚 Clarify OpenShift requirements

* 📚 Remove the click for expanding
2025-07-22 14:05:02 +02:00
Andrey Antukh d914314c1c Merge remote-tracking branch 'origin/staging' into develop 2025-07-22 13:04:57 +02:00
Pablo Alba 4aa9f1f62b 🐛 On component swap do not show secondary variants (#6928) 2025-07-22 12:33:37 +02:00
David Barragán Merino 9d288486d7 🐛 Subscription current period dates could be null (#6931)
`current-period-start` and `current-period-end` can be null if the invoice has not yet been created in stripe. This happens after the subscription is created, before the webhook is sent.
2025-07-22 12:32:42 +02:00
Pablo Alba ea5521485a ♻️ Remove redundant flag on text overrides (#6933) 2025-07-22 12:32:24 +02:00
Marina López f768ffbdad 🐛 Fix wrong behaviour for unpaid or canceled subscriptions (#6932) 2025-07-22 12:31:45 +02:00
Andrey Antukh 4f0d3660de 🎉 Add imagemagick docker image build scripts (#6925)
* 🎉 Add imagemagick docker image build scripts

* 📎 Add PR feedback changes
2025-07-22 11:51:13 +02:00
Andrey Antukh fa72bb4adf Add several improvements to admin pannel 2025-07-22 10:06:29 +02:00
Andrey Antukh ea0044f69a 💄 Use resolved schemas instead of references
For several schemas on common types
2025-07-22 10:06:29 +02:00
Andrey Antukh 7e493376a4 Reuse file data checkers on file validate ns 2025-07-22 10:06:29 +02:00
Andrey Antukh 8c5afe5ab3 📎 Add next release entries to the changelog 2025-07-21 21:20:46 +02:00
Andrey Antukh 7ccb742ef3 Merge remote-tracking branch 'origin/develop' into staging 2025-07-21 21:15:54 +02:00
Andrey Antukh 7bc29c22ed Merge remote-tracking branch 'origin/develop' into staging 2025-07-21 21:07:24 +02:00
Andrey Antukh 1d550eaa18 Merge remote-tracking branch 'origin/staging' into develop 2025-07-21 21:03:19 +02:00
Andrey Antukh b71ec4bfe0 Simplify docker build script (#6924) 2025-07-21 20:45:03 +02:00
Andrey Antukh 827bbf6a7f Merge pull request #6926 from penpot/juanfran-close-libraries-modal-on-esc
🐛 Fix ESC key not closing Add/Manage Libraries modal
2025-07-21 15:48:40 +02:00
Juanfran 2db0cc0cbf 🐛 Fix ESC key not closing Add/Manage Libraries modal 2025-07-21 15:23:54 +02:00
Andrey Antukh 42ef01b339 Merge pull request #6871 from penpot/niwinz-develop-login-enhancements
 Allow login dialog on settings
2025-07-21 15:19:06 +02:00
Aitor Moreno fdaef2be69 Merge pull request #6891 from penpot/elenatorro-test-style-decoration-blending
🔧 Add text decoration styles
2025-07-21 15:18:18 +02:00
Pablo Alba ae3213f5d4 🐛 Fix text override corner case 2025-07-21 12:40:03 +02:00
Andrey Antukh 6dfd05fdd1 Merge remote-tracking branch 'origin/staging' into develop 2025-07-21 12:05:24 +02:00
Andrey Antukh 51107c3fc9 🐛 Fix incorrect event name on event constructor 2025-07-21 11:58:48 +02:00
Andrey Antukh b6863efb3a Merge pull request #6874 from penpot/xaviju-11355-tokens-import-details-layout
 Improve legibility on import token notification details
2025-07-21 11:54:08 +02:00
Andrey Antukh 799bceb8b7 🐛 Check if profile is logged-in on subscriptions internal redirects 2025-07-21 11:40:31 +02:00
Andrey Antukh 9e573128c1 🐛 Fix incorrect event name on event constructor 2025-07-21 11:40:31 +02:00
Andrey Antukh 1f05511add Allow login dialog on settings 2025-07-21 11:40:30 +02:00
Elena Torro eeee52a738 🐛 Ensure line height is properly handled on line breaks 2025-07-21 11:37:56 +02:00
Xavier Julian 7f53860296 📎 Add warning on feature flag temporary fix for font-size tokens 2025-07-21 11:23:27 +02:00
Andrey Antukh 16d0077393 Merge pull request #6920 from penpot/mdbenito-feature/wheel-scrolling-for-templates
 Enable wheel scrolling over templates-section in the dashboard
2025-07-21 11:22:47 +02:00
Andrey Antukh 622fed2f0d 💄 Add minor formating enhancements to dashboard templates ui code 2025-07-21 10:39:50 +02:00
Andrey Antukh d22ade3289 Remove duplicated code 2025-07-21 10:38:18 +02:00
Miguel de Benito Delgado 7febf330ac Enable wheel scrolling over templates-section in the dashboard 2025-07-21 10:34:50 +02:00
Andrey Antukh 75a50ac1ac Merge pull request #6912 from penpot/andy-highlight-font-selector
 Highlight first font in font selector search, apply on Enter/click
2025-07-21 10:33:53 +02:00
Andres Gonzalez e62567d09e Highlight first font in font selector search, apply on Enter/click
[Taiga #11579](https://tree.taiga.io/project/penpot/issue/11579)

 Highlight first font in font selector search, apply on Enter/click
2025-07-21 10:13:36 +02:00
Andrey Antukh 8d80eebeb1 Merge pull request #6906 from penpot/andy-enhance-text-auto-resize
 Switch auto-width to auto-height on horizontal resize on text shapes
2025-07-21 10:11:35 +02:00
Andres Gonzalez ee9a42238d Switch auto-width to auto-height on horizontal resize on text shapes 2025-07-21 09:56:45 +02:00
Andrey Antukh 758c76d661 Merge pull request #6905 from penpot/andy-enhance-text-resize-behavior
 Allow double-click on text bounding box to set auto-width/auto-height
2025-07-21 09:55:18 +02:00
Andrey Antukh 1dec46cbfa Merge pull request #6903 from penpot/superalex-fix-page-duplication
🐛 Fix error on validating file referential integrity when duplicating a page
2025-07-21 09:46:12 +02:00
Andrey Antukh ae25d704c1 📎 Add missing use-fn hook 2025-07-21 09:32:44 +02:00
Andres Gonzalez e05f8c0329 Improve text layer resize behavior
Text layers now only switch to fixed grow-type on vertical resize, not on horizontal resize, for a more intuitive UX. See #4602.
2025-07-21 09:27:42 +02:00
Alejandro Alonso ce62e11626 🐛 Fix error on validating file referential integrity when duplicating a page 2025-07-21 09:26:23 +02:00
Andrey Antukh 9f04c2fc1d Merge pull request #6901 from penpot/superalex-hide-bb-when-editing-effects
 Hide bounding box while editing visual effects
2025-07-21 09:23:18 +02:00
Andrey Antukh 05a405a82d Merge pull request #6893 from penpot/xaviju-11144-copy-color-attr
 Keep color data when copying from info tab into CSS
2025-07-21 09:22:57 +02:00
Andrey Antukh 3c8c21c378 Merge pull request #6899 from abedef/patch-1
📚 Fix broken link in self-hosting docs
2025-07-21 09:19:39 +02:00
Xavier Julian 2dbeb884a5 Keep color data when copying from info tab into CSS 2025-07-21 09:07:20 +02:00
Andrey Antukh 931d72b41f Merge pull request #6887 from dfelinto/fix-trackpad-swipe
🐛 Fix touchpad swipe back/forward #4246
2025-07-21 08:58:32 +02:00
Alejandro Alonso 2e3cdd872c Revert " Highlight first found font in font list when searching [Taiga #3204]"
This reverts commit 55a13c3139.
2025-07-17 13:01:24 +02:00
Andres Gonzalez 55a13c3139 Highlight first found font in font list when searching [Taiga #3204]
This enhancement highlights the first found font in the font list when searching, and allows pressing Enter to select it, for a more intuitive font selection experience.

See [Taiga #3204](https://tree.taiga.io/project/penpot/issue/3204).
2025-07-17 12:09:50 +02:00
Andrey Antukh f63d1c87e3 Merge pull request #6904 from penpot/andy-fix-email-change-message
 Update email change confirmation message for clarity
2025-07-17 11:31:21 +02:00
Alejandro Alonso abbfd44534 Hide bounding box while editing visual effects 2025-07-17 09:33:10 +02:00
Andres Gonzalez f772724f9a Update email change confirmation message for clarity 2025-07-16 10:22:53 +02:00
Andrey Antukh f3abd0f190 Merge pull request #6902 from penpot/andy-clarify-invite-member-message
 Clarify invite member message for existing team members
2025-07-15 15:48:45 +02:00
Andres Gonzalez 5d4042c861 Clarify invite member message for existing team members
Update the English message shown when inviting team members whose emails are already part of the team, as suggested in issue #6785.
2025-07-15 14:05:20 +02:00
Dalai Felinto 1fbcec98fb 🐛 Fix touchpad swipe back/forward #4246
This prevents the browser to take over the trackbad swipe gesture both
for the dashboard and the workspace.

At an early attempt I did get the code to work only for the workspace,
but it is too unreliable and I could every now and then get it to misbehave.

I believe it is better to be safe and always prevent the browser from
going back/forth, regardless of workspace/dashboard.

Signed-off-by: Dalai Felinto <dalai@blender.org>
2025-07-15 00:24:20 +02:00
Alejandro Alonso abef9f3cf7 Merge pull request #6889 from penpot/niwinz-staging-bugfix
🐛 Fix unexpected exception on processing old texts
2025-07-14 07:08:31 +02:00
Abed Fayyad 6f1958f9f2 📚 Fix broken link in self-hosting docs
Replaced broken Markdown link to the unofficial self-hosting section.

Signed-off-by: Abed Fayyad <yo@abedef.ca>
2025-07-13 09:16:13 -04:00
Andrey Antukh 6b2ce86d5f Merge pull request #6896 from penpot/juanfran-issue-show-main-component-focus
🐛 Fix initialize-page namespace when showing main component
2025-07-11 18:31:37 +02:00
Pablo Alba 0cfd70da2e 🐛 Fix corner cases on variants text overrides 2025-07-11 15:28:55 +02:00
Xavier Julian 4167faf39d 📎 Add blend-mode in code editor feature to CHANGELOG 2025-07-11 15:14:29 +02:00
Pablo Alba 90e6e8c5eb 🐛 Fix double undo on text partial overrides 2025-07-11 15:05:30 +02:00
Andrey Antukh e2c5a1378e Merge pull request #6724 from penpot/elenatorro-improve-create-profile-command
🔧 Add option to skip tutorial/walkthrough when creating a profile from the script
2025-07-11 14:15:49 +02:00
Xavier Julian 81f99458e5 📎 Add new font-size token type to CHANGELOG 2025-07-11 14:09:39 +02:00
Elena Torro b40b1fa2e4 🔧 Refactor ParagraphBuilder and fix auto height 2025-07-11 13:29:22 +02:00
Juanfran bb1ec109d8 🐛 Fix initialize-page namespace when showing main component 2025-07-11 13:09:20 +02:00
Florian Schroedl 9c5a13c4ac Enable font-size token 2025-07-11 10:37:17 +02:00
Elena Torro 4c21468850 🔧 Add text decoration styles 2025-07-10 14:26:41 +02:00
Xavier Julian 02ae934e25 📎 Add import tokens from zip file to CHANGELOG 2025-07-10 13:40:42 +02:00
Andrey Antukh 95cfb26b38 Merge pull request #6882 from penpot/xaviju-11283-info-tab-visibility-attrs-review
♻️ Fix tab info not updating and suggested code refactor
2025-07-10 11:56:38 +02:00
Andrey Antukh 935c22d124 Merge pull request #6885 from penpot/marina-change-text-capitalize
🐛 Fix title button from Title case to Capitalize
2025-07-10 11:55:50 +02:00
Marina López ba6a02d1d9 🐛 Add fixes from subscription design review (#6870)
* 🐛 Fixes from subscription design review

* 🐛 Fix to consider professional plan the unpaid and canceled status

* 📎 Fixes PR feedback
2025-07-10 11:55:16 +02:00
Xavier Julian 0b681effe7 ♻️ Fix tab info not updating and suggested code refactor 2025-07-10 11:38:53 +02:00
Marina López 6826db8498 🐛 Fix title button from Title case to Capitalize 2025-07-10 11:29:48 +02:00
Andrey Antukh 66c5841d48 Merge pull request #6886 from penpot/alotor-fix-create-layout
🐛 Fix problem when creating a layout from an existing layout
2025-07-10 11:28:19 +02:00
Xavier Julian af10705b4c ♻️ Review import message text 2025-07-10 10:18:59 +02:00
Andrey Antukh 40c300fa1a 🐛 Fix unexpected exception on processing old texts 2025-07-10 09:22:00 +02:00
Pablo Alba 41146ef71d 🐛 Fix text overrides when there are structure changes 2025-07-09 21:58:01 +02:00
Pablo Alba abb6aee57d 🐛 On texts overrides, keep also vertical-align property 2025-07-09 21:58:01 +02:00
alonso.torres aa01d3b707 🐛 Fix problem when creating a layout from an existing layout 2025-07-09 15:44:15 +02:00
alonso.torres a003687256 🐛 Fix problem with grid assignments 2025-07-09 14:55:14 +02:00
Elena Torro e2b55d814b 🐛 Fix select all deletion error on Firefox 2025-07-09 14:50:35 +02:00
Andrey Antukh 51a6d61be6 Merge pull request #6865 from penpot/xaviju-11283-info-tab-visibility-attrs
 Add visibility group and attributes to info tab
2025-07-09 12:18:10 +02:00
Xavier Julian 0daa8be0b5 Add visibility group and attributes to info tab 2025-07-09 11:19:30 +02:00
Andrey Antukh 00599f76d0 Merge pull request #6875 from penpot/ladybenko-fix-devenv-mac-ubuntu
🔧 Fix building and running devenv (Mac / Linux)
2025-07-09 08:28:49 +02:00
Belén Albeza cb8aae4d5f 🔧 Drop the -R in chown (dockerfile mac) 2025-07-08 15:45:34 +02:00
Belén Albeza 927228fc8f 🔧 Remove COPY of apt.sources (linux issue) 2025-07-08 15:44:42 +02:00
Xavier Julian 77a47e4b2b Improve legibility on import token notification details 2025-07-08 15:09:50 +02:00
Andrés Moya 88bb9bfe52 🐛 Detach styles from assets when applying tokens 2025-07-08 13:15:45 +02:00
Andrey Antukh e554b9fcb7 Merge remote-tracking branch 'origin/staging' into develop 2025-07-08 11:04:29 +02:00
Aitor Moreno 4548310235 Merge pull request #6867 from penpot/azazeln28-fix-missing-solid-color
🐛 Fix missing required SolidColor
2025-07-08 09:11:12 +02:00
Aitor Moreno ea9261b0b2 🐛 Fix missing required SolidColor 2025-07-08 08:45:03 +02:00
Aitor Moreno 6ffcd58368 Merge pull request #6846 from penpot/alotor-wasm-refactor-mut-2
♻️ Refactor wasm shapes state management
2025-07-08 08:31:15 +02:00
alonso.torres 69135ef8c7 ♻️ Refactor wasm shapes state management 2025-07-08 08:30:40 +02:00
Aitor Moreno 747427daa4 Merge pull request #6841 from penpot/superalex-fix-frame-clipping
🐛 Fix frame clipping
2025-07-08 08:26:48 +02:00
Alejandro Alonso 5b704faf79 Merge pull request #6862 from penpot/niwinz-develop-minor-fixes
 Add several improvements
2025-07-07 15:40:20 +02:00
Andrey Antukh d24eab7241 📎 Update changelog 2025-07-07 15:07:07 +02:00
Andrey Antukh c8fef97598 Add missing field on get-teams rpc method 2025-07-07 13:36:00 +02:00
Andrey Antukh 44e3e4a641 🔥 Remove image shape inspect pannel
Image shapes are long ago deprecated not used
on penpot
2025-07-07 13:36:00 +02:00
Andrey Antukh f3616c68a0 Improve decode empty string for path content 2025-07-07 13:36:00 +02:00
Andrey Antukh 9ea3f81bc4 🐛 Clean invalid library colors 2025-07-07 13:35:59 +02:00
Pablo Alba cfec023585 ♻️ Rename flag :component-swap to :allow-altering-copies 2025-07-07 12:07:36 +02:00
Pablo Alba 469d47eaf3 🐛 Fix variants combobox and select to new options format 2025-07-07 11:46:50 +02:00
Alejandro Alonso 51bb6583d2 🐛 Fix frame clipping 2025-07-07 11:09:29 +02:00
Pablo Alba a44c70ef69 Keep the swapped childs if the copies when doing a variant switch 2025-07-07 10:50:49 +02:00
Andrés Moya 4fddf34a73 🐛 Fix error when there exists a tokens lib with no sets 2025-07-07 10:02:49 +02:00
Xavier Julian 8f840daa91 Improve token import error copy 2025-07-07 09:59:57 +02:00
Juanfran 0a7d6d98e1 Integrate plugin runtime as npm library (#6852) 2025-07-07 09:46:07 +02:00
Álvaro Tejero-Cantero bcb69b6227 🐛 Restore viewport and selection when exiting focus mode (#6827)
* 📚 Provide guidance on how to exit focus mode

* 🐛 Restore viewport & selection post focus mode

* 📎 Update changelog
2025-07-07 09:44:06 +02:00
Andrey Antukh 92d708d52c Merge remote-tracking branch 'origin/staging' into develop 2025-07-07 09:37:55 +02:00
Alonso Torres accd5226d7 🐛 Fix sidebar width in localhost (#6732) 2025-07-07 09:28:52 +02:00
Andrey Antukh 16a1fd14e5 🐛 Fix media translation on text nodes on paste (#6845)
Fix incorrect media translation on paste text with fill images
2025-07-07 09:03:35 +02:00
Aitor Moreno 824bb19c7e Merge pull request #6848 from penpot/niwinz-staging-library-referer
 Add referer field to binfile v3
2025-07-07 09:02:13 +02:00
Aitor Moreno d0f3e0f0b0 Merge pull request #6853 from penpot/niwinz-staging-path-bool-fixes
🐛 Fix exception on a corner case of creating bool shape
2025-07-07 09:00:57 +02:00
Andrey Antukh 43ba2b05e8 📎 Change current config values for error report explain 2025-07-04 14:51:08 +02:00
Andrey Antukh d5ccb704b2 🐛 Fix unexpected exception on creating bool shapes 2025-07-04 14:42:09 +02:00
Andrey Antukh 0374e4f3eb Merge remote-tracking branch 'origin/staging' into develop 2025-07-04 12:02:12 +02:00
David Barragán Merino 6d21fcc9de 🔧 Fix condition for automatic events (#6849) 2025-07-04 11:57:57 +02:00
Andrey Antukh 77741b49a7 Add tracking for referer on the import-binfile 2025-07-04 11:02:36 +02:00
Andrey Antukh a7e0cfc609 🎉 Bump 1.0.7 release of the penpot library
Includes the ability to pass referer
2025-07-04 11:02:36 +02:00
Andrey Antukh 50a6355537 🎉 Add options for creating library build context
With the ability to pass referer.
2025-07-04 11:02:36 +02:00
Alejandro Alonso 264aef277d Merge pull request #6847 from penpot/niwinz-staging-error-reports-2
 Add minor improvements to error report on calc bool content
2025-07-04 10:53:02 +02:00
Andrey Antukh 78d0e6d059 Add minor improvements to error report on calc bool content 2025-07-04 10:13:24 +02:00
Alonso Torres 6d41d36b3a 🐛 Fix problem when double click on hidden shapes (#6833) 2025-07-04 09:01:20 +02:00
David Barragán MerinoandFrancis Santiago bb97df373e 🔧 Add Github Action to build and upload artifact (#6840)
Co-authored-by: Francis Santiago <francis.santiago@kaleidos.net>
2025-07-04 08:41:46 +02:00
David Barragán MerinoandFrancis Santiago 528c819323 🔧 Add Github Action to build and upload artifact (#6840)
Co-authored-by: Francis Santiago <francis.santiago@kaleidos.net>
2025-07-04 08:25:23 +02:00
Florian Schrödl 21746144b7 Add letter spacing token (#6814)
* 🐛 Fix merge schema not working with key generation

*  Add letter-spacing token

* ♻️ Remove comments

* ♻️ Inline line-height for now
2025-07-03 16:00:58 +02:00
Andrey Antukh 3165761bac Merge remote-tracking branch 'origin/staging' into develop 2025-07-03 15:32:30 +02:00
Andrés Moya c09f72c3d5 🐛 Sanitize wrong ids in token themes (#6843) 2025-07-03 15:31:45 +02:00
Alejandro Alonso a41af032cd Merge pull request #6844 from penpot/niwinz-staging-enhancements
 Improve error reporting
2025-07-03 15:31:20 +02:00
Andrey Antukh 86ee4f55c5 📚 Update docstring 2025-07-03 14:54:00 +02:00
Andrey Antukh 63cd3ae025 Add better error handling for bool creation 2025-07-03 14:54:00 +02:00
Andrey Antukh cafb7abb53 🎉 Add better syntax facility for ex/try! macro 2025-07-03 14:54:00 +02:00
Andrey Antukh e5b6c4a9e0 Add minor improvement to error reporter logger 2025-07-03 14:54:00 +02:00
Andrey Antukh 1d5bad5523 💄 Report file-id on file changes exception 2025-07-03 14:54:00 +02:00
Andrey Antukh 96d6868b45 🐛 Add missing fields on get-team-shared-files query 2025-07-03 14:54:00 +02:00
Andrey Antukh b739d8bd0c 💄 Change default depth on params for error reports 2025-07-03 14:54:00 +02:00
Alejandro Alonso dd803dc1de Merge pull request #6839 from penpot/niwinz-staging-fix-broken-path
🐛 Add migration for fix undecoded path content
2025-07-03 13:43:59 +02:00
Florian Schrödl 7dd61968b5 Implement object type specific tokens (#6816)
*  Allow token applying for supported shape types only

* 🐛 Remove x/y attribute keys from spacing token

*  Shape specific context-menu

*  Only apply tokens to supported shapes when doing multi selection apply

*  Handle groups not supported by tokens yet

* 🐛 Fix outdated tests

* ♻️ Commentary

*  Add helper functions for attribute applicability checks

* ♻️ Groups don't have own attributes

* ♻️ Remove unused function

* ♻️ Move attribute logic to common.types.token
2025-07-03 12:22:04 +02:00
Juanfran 669d6d9ae2 Merge pull request #6837 from penpot/juanfran-us-11186-rules-help
 Add in-app help to guide users about variant rules
2025-07-03 11:30:16 +02:00
Andrey Antukh b627c10737 🔥 Remove duplicated check-fn 2025-07-03 10:50:09 +02:00
María Valderrama 95f4a9bd29 Add missing start-plugin event (#6809)
*  Add missing start-plugin event

* 📎 Correct event origin
2025-07-03 10:29:43 +02:00
Miguel de Benito Delgado b931547300 🐳 Add "postgres" network alias to default docker network in devenv (#6823) 2025-07-03 10:28:53 +02:00
Andrey Antukh a2b8f19ff3 🐛 Add migration for fix undecoded path content 2025-07-03 08:40:23 +02:00
Belén Albeza 30274c4f5c 🔧 Restore arm64 build of devenv (#6826) 2025-07-03 08:28:07 +02:00
Andrés Moya 0a71134652 🔧 Sanitize and check tokens when deserializing from db (#6838) 2025-07-02 17:01:10 +02:00
Juanfran 72b1919e29 Add in-app help to guide users about variant rules 2025-07-02 14:46:36 +02:00
Andrey Antukh 898182e3d5 Add minor events props normalization (#6836) 2025-07-02 14:41:48 +02:00
Xavier Julian be43365909 🐛 Fix broken import file type drodown options 2025-07-02 14:35:01 +02:00
Andrey Antukh 41994703a9 ♻️ Refactor tab-switcher* component (#6815)
* 💄 Add minor style adjustments to workspace sidebar

* 💄 Add style enhacement to sitemap component

* ♻️ Refactor tab-switcher* component
2025-07-02 14:08:47 +02:00
Marina López 3d45080e3c 🐛 Fixes from subscription design review (#6812) 2025-07-02 10:49:16 +02:00
Miguel de Benito Delgado 28c055e3f9 📚 Fix and extend backend repl doc (#6819) 2025-07-02 10:38:35 +02:00
Prithvi Tharun 4f993bf4ae 💄 Replace 'Verify new email' label with 'Confirm new email' (#6831)
Improves clarity by using more accurate and familiar terminology.

Signed-off-by: Prithvi Tharun <ptrithu8@gmail.com>
2025-07-02 10:32:09 +02:00
Alejandro Alonso 3cb0e1b6ee 🐛 Fix exif rotation detection when auto-rotation isn't supported (#6818) 2025-07-02 10:31:05 +02:00
Miguel de Benito Delgado e03c822b51 🐛 Fix internal error on missing theme setting in profile (#6822) 2025-07-02 10:17:22 +02:00
Andrey Antukh 1432b211a6 Merge remote-tracking branch 'origin/staging' into develop 2025-07-02 10:13:30 +02:00
Miguel de Benito Delgado 3e45e4fb25 🐛 Fix internal error on missing theme setting in profile (#6822) 2025-07-02 09:57:56 +02:00
Eva Marco a3aabf3b7d 🐛 Fix tooltip position after click (#6830) 2025-07-02 09:56:14 +02:00
Andrés Moya 953287ea33 🐛 Avoid crash in combobox with empty options 2025-07-02 08:57:25 +02:00
Elena Torró 493831f110 Merge pull request #6821 from penpot/alotor-refactor-mutability
♻️ Refactor mutability modifiers in wasm
2025-07-01 13:52:39 +02:00
alonso.torres 3d374e8e97 ♻️ Refactor mutability modifiers in wasm 2025-07-01 12:47:31 +02:00
Andrés Moya f0f01af55c 🔧 Make TokenSet an abstract data type 2025-06-30 16:59:00 +02:00
Xavier Julian 6de9de9e38 Add new metric for token update and provide token type 2025-06-30 13:21:49 +02:00
Kelp b893a62e40 Add new typography icon to the DS (#6808)
Signed-off-by: Kelp <5446186+NatachaMenjibar@users.noreply.github.com>
2025-06-30 11:06:54 +02:00
alonso.torres 8dcb376b18 Add drop grid cells in wasm 2025-06-30 10:28:59 +02:00
alonso.torres 52a4fc6030 🐛 Fix drop index on flex layout wasm 2025-06-30 10:28:59 +02:00
Andrey AntukhandEva Marco 403d92838a ♻️ Add minor refactor to options dropdown options handling and validation (#6739)
* ♻️ Refactor options-dropdown* and related components

* 🐛 Fix props error

* 🐛 Fix test

* 📎 Update rumext

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
2025-06-29 11:52:29 +02:00
Xavier Julian 6bd3253e5e ♻️ Restructure UI files for tokens editor 2025-06-27 13:23:42 +02:00
Pablo Alba 20b5b7f6e4 🐛 Fix variant switch in another page (#6802) 2025-06-27 12:23:54 +02:00
Andrés Moya 5c4fd97541 🐛 Allow importing file without any token but with themes or sets (#6796) 2025-06-27 11:32:14 +02:00
Pablo Alba 804146ae9a 🐛 Fix text partial change doesn't show up on another page (#6799) 2025-06-27 10:21:21 +02:00
Juanfran 24e78e6a10 🐛 Display error message on register form (#6797) 2025-06-27 10:01:54 +02:00
Pablo Alba daca26e54f 🐛 On variants override use the component name instead of the copy name 2025-06-26 17:37:21 +02:00
Aitor Moreno 29016cef49 Merge pull request #6794 from penpot/alotor-wasm-fix-grid-fr
🐛 Fix problem with fr allocation
2025-06-26 14:39:42 +02:00
alonso.torres fb07788e8f 🐛 Fix problem with fr allocation 2025-06-26 13:17:26 +02:00
Alejandro Alonso 3010abbf64 Merge pull request #6793 from penpot/alotor-fix-plugins-system-theme
🐛 Fix problem with plugins on system theme
2025-06-26 12:30:51 +02:00
alonso.torres e6a7eed7a9 🐛 Fix problem with plugins on system theme 2025-06-26 12:15:06 +02:00
Andrey Antukh c75a617d26 Merge remote-tracking branch 'origin/staging' into develop 2025-06-26 11:19:29 +02:00
Andrey Antukh f2c4a1eb1f Merge pull request #6674 from penpot/niwinz-develop-enhacements-3
 Refactor fills-menu and related components
2025-06-26 11:09:30 +02:00
Marina López 62371fded0 🐛 Fix libraries position in dashboard sidebar (#6791) 2025-06-26 11:08:18 +02:00
Alejandro Alonso daf3b5caa8 🐛 Fix slow color picker (#6780) 2025-06-26 11:07:35 +02:00
Andrey Antukh e72d31a082 🔥 Remove unused and commented code 2025-06-26 10:50:38 +02:00
Andrey Antukh 6b4a85cd15 🐛 Fix issue on changing from gradient to solid color on colorpicker 2025-06-26 10:50:38 +02:00
Andrey Antukh 027a7a457d Add minor style improvements for reorder-handler component 2025-06-26 10:50:38 +02:00
Andrey Antukh 20d2d22f39 Add performance oriented refactor to fill-menu component 2025-06-26 10:50:36 +02:00
Andrey Antukh 34d65ed1c8 Merge pull request #6775 from penpot/superalex-fix-entering-long-project-name
🐛 Fix entering long project name
2025-06-26 10:48:54 +02:00
Andrey Antukh a191fe63a1 Merge remote-tracking branch 'origin/staging' into develop 2025-06-26 09:18:23 +02:00
luisδμandPablo Alba 2de0c90fc7 🐛 Remove empty properties starting with the last one (#6757)
* 🐛 Remove empty properties starting with the last one

*  MR changes

---------

Co-authored-by: Pablo Alba <pablo.alba@kaleidos.net>
2025-06-26 09:16:59 +02:00
Luis de Dios 7cd0e28c3b Allow variants with no properties 2025-06-26 08:42:34 +02:00
Andrey Antukh 25ef1800d0 Merge remote-tracking branch 'origin/staging' into develop 2025-06-25 19:30:57 +02:00
Andrey Antukh 9760911fce Merge remote-tracking branch 'origin/staging' into develop 2025-06-25 14:24:26 +02:00
Alejandro Alonso 82583f5079 🐛 Fix entering long project name 2025-06-25 14:21:52 +02:00
Marina López f81a973a4d 🐛 Fix text decoration line through value in inspect tab (#6778) 2025-06-25 14:11:58 +02:00
Alejandro Alonso ca99671d3c 📚 Update CHANGES with support for exif rotated images (#6782) 2025-06-25 14:10:13 +02:00
Marina López 1f42f032fc 🐛 Add fixes for subscription design review (#6751)
* 🐛 Fix from subscription design review

* 📎 Fixes PR feedback
2025-06-25 13:41:45 +02:00
Marina López 67ca8ccb22 🐛 Fix copy font-size doesn't copy the unit (#6776) 2025-06-25 12:14:33 +02:00
Xavier Julian ce59070fd1 ♻️ Restructure UI files for token sets 2025-06-25 11:27:13 +02:00
Marina López e258030bc0 💄 Change 'save color' button (#6774) 2025-06-25 10:21:22 +02:00
Alejandro Alonso 8f00292f8f 🎉 Support for exim rotated images (#6767) 2025-06-25 10:20:37 +02:00
Florian Schroedl fe91201431 Keep warning for unsupported token types when FF is disabled 2025-06-24 15:41:24 +02:00
Florian Schroedl 00c7411f92 🐛 Fix dtcg token type name 2025-06-24 15:41:24 +02:00
Xavier Julian e585cbd673 ♻️ Restructure UI files for import/export and common files 2025-06-24 13:58:54 +02:00
Alejandro Alonso bdc10ac173 Merge pull request #6754 from penpot/azazeln28-issue-11401-fix-wrong-aspect-ratio
🐛 Fix image aspect ratio rendering on oriented images
2025-06-24 13:23:35 +02:00
Elena Torró 9f5cb61a19 Merge pull request #6766 from penpot/elenatorro-fix-text-auto-height
🐛 Fix text auto height
2025-06-24 13:18:28 +02:00
Alejandro Alonso e442d8adad Add tests for exif rotated images 2025-06-24 13:08:18 +02:00
Elena Torro 925af4e1e9 🐛 Fix text auto height 2025-06-24 12:36:12 +02:00
alonso.torres a45886c86c Small cosmetic change 2025-06-24 10:26:37 +02:00
alonso.torres 36b6f6323a ♻️ Refactor modifiers methods 2025-06-24 10:26:37 +02:00
alonso.torres afec3b9bc1 🐛 Fix problem with margin in flex layout 2025-06-24 10:26:37 +02:00
alonso.torres ac6a814026 🐛 Fix problem with flex layout in wasm 2025-06-24 10:26:37 +02:00
Alejandro Alonso 89fb802362 Merge pull request #6764 from penpot/alotor-fix-problem-lines
🐛 Fix wasm problem with horizontal/vertical lines
2025-06-24 09:43:11 +02:00
alonso.torres b0d858df2b 🐛 Fix wasm problem with horizontal/vertical lines 2025-06-24 09:24:00 +02:00
Aitor Moreno f54497194a Merge pull request #6762 from penpot/elenatorro-10901-add-text-vertical-alignment
🔧 Add vertical alignment for text shapes
2025-06-23 17:05:47 +02:00
Elena Torro 134fb1ab4c 🔧 Add vertical alignment for text shapes 2025-06-23 16:45:25 +02:00
Aitor Moreno 833546d754 🐛 Fix wrong aspect ratio on oriented image 2025-06-23 15:30:01 +02:00
Elena Torró 0010d61ae2 Merge pull request #6758 from penpot/elenatorro-text-rendering-fixes-and-tests
🔧 Add tests to cover text styles
2025-06-23 14:06:19 +02:00
Elena Torro 747f72be47 🔧 Add tests to cover text styles 2025-06-23 13:43:09 +02:00
Alejandro Alonso 1882efe3f7 🐛 Fix paths rendered initially ony in tile 0 0 2025-06-23 12:23:49 +02:00
Florian Schrödl 580bb46a05 Implement font-size token type (#6708)
*  Implement font-size token type

*  Hide typography tokens behind FF

* 💄 Update icon

* 🔧 Add font-size to unapply check

* ♻️ Generalize status-icon logic and remove icon for font-size
2025-06-23 12:12:40 +02:00
Alejandro Alonso 9ea0875e65 Merge pull request #6742 from penpot/ladybenko-fix-wasm-debug-text-hi-dpr
 Fix size of 'wasm renderer' debug text on dpr > 1
2025-06-23 11:47:20 +02:00
Alejandro Alonso 43b19ba33e Merge pull request #6738 from penpot/ladybenko-11247-enable-dpr-when-render-wasm
🔧 Enable render-wasm-dpr by default
2025-06-23 11:46:24 +02:00
Andrey Fedorov 130cd52f79 Notify user if imported file or directory doesn't contain token files 2025-06-23 11:44:00 +02:00
Aitor Moreno 21fd56076c Merge pull request #6756 from penpot/superalex-fix-empty-fills
🐛 Fix empty fills
2025-06-23 11:31:34 +02:00
Alejandro Alonso c97314ddb5 🐛 Fix empty fills 2025-06-23 11:14:58 +02:00
Andrey Antukh 34bbce5089 Merge remote-tracking branch 'origin/staging' into develop 2025-06-23 10:06:05 +02:00
ºelhombreteclaandAndrey Antukh 9a0538e5e3 Add visual indicator for new comments in the workspace (#6728)
* 🎉 Add comment notification to workspace

* 💄 Add info to changelog

*  Add minor efficiency improvements

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-06-23 09:51:41 +02:00
Belén Albeza 56d96aaf07 🔧 Enable render-wasm-dpr by default 2025-06-20 12:48:49 +02:00
luisδμ 70f3988046 Show warning when selecting a copy of conflicted variant (#6743) 2025-06-20 11:52:07 +02:00
Andrey Antukh ec021d944d Merge remote-tracking branch 'origin/staging' into develop 2025-06-20 11:37:50 +02:00
Elena Torró f1a6b46165 Merge pull request #6745 from penpot/superalex-bug-fix-text-image-strokes
🐛 Fix image fill strokes are not rendered correctly for texts
2025-06-20 11:14:24 +02:00
Belén Albeza 2412402a23 Fix size of 'wasm renderer' debug text on dpr > 1 2025-06-20 10:55:33 +02:00
Alejandro Alonso 5375029497 🐛 Fix image fill strokes are not rendered correctly for texts 2025-06-20 10:52:26 +02:00
Aitor Moreno 8bfc314b17 Merge pull request #6700 from penpot/superalex-fix-async-content-rendering
🐛 Fix asynchronous content rendering
2025-06-20 10:11:28 +02:00
Elena Torró 38112e287a Merge pull request #6734 from penpot/ladybenko-11392-no-ui-in-tests
🔧 Make visual regression tests to hide the UI when taking a screenshot (render-wasm tests only)
2025-06-20 09:05:30 +02:00
Alejandro Alonso 5c7a4ce5b7 🐛 Fix fill images for text 2025-06-20 07:45:28 +02:00
Andrey Antukh 7e909dfbe8 Merge remote-tracking branch 'origin/staging' into develop 2025-06-19 15:35:19 +02:00
Alonso Torres 7f7f0893d0 🐛 Fix sidebar width in localhost (#6732) 2025-06-19 15:31:27 +02:00
María Valderrama 22fbc3fa5f 💄 Improve dashboard's sidebar (#6736) 2025-06-19 15:28:32 +02:00
Alejandro Alonso d71fa659d5 🐛 Fix asynchronous content rendering 2025-06-19 14:03:40 +02:00
Alejandro Alonso d0425cabda Merge pull request #6721 from penpot/ladybenko-11276-fix-modifiers-dpr
🐛 Fix panning and scroll when dpr > 1 (render wasm)
2025-06-19 14:01:47 +02:00
Belén Albeza 9852d24b83 🔧 Make visual regression tests to hide the UI when taking a screenshot (render-wasm tests only) 2025-06-19 13:37:52 +02:00
Alejandro Alonso 2239482049 Merge pull request #6717 from penpot/alotor-grid-editor
 Support grid editor with wasm modifiers
2025-06-19 13:32:27 +02:00
Xavier Julian 4ea4a1e130 ♻️ Restructure UI files for token settings 2025-06-19 13:10:09 +02:00
alonso.torres 11467e26a2 🐛 Fix problem with flex wrap in wasm 2025-06-19 13:03:25 +02:00
alonso.torres b997d5a320 🐛 Fix problem with grid layout wasm 2025-06-19 13:03:25 +02:00
alonso.torres 5b4cd9f4f1 🐛 Fix problem when moving masks, bools, groups with wasm 2025-06-19 13:03:25 +02:00
alonso.torres 58e5748b4f 🐛 Fix wasm layout problems 2025-06-19 13:03:25 +02:00
alonso.torres b2647f30c2 Support grid editor with wasm modifiers 2025-06-19 13:03:25 +02:00
luisδμ 72f2a409f9 Show warning when duplicated variant prop name and value (#6639)
*  Show warning when duplicated variant prop name and value

* 📎 PR changes
2025-06-19 12:34:28 +02:00
Xavier Julian 62a6f2c2f1 ♻️ Restructure UI files for theme creation modal 2025-06-19 11:59:25 +02:00
Xavier Julian 105e0ba75f ♻️ Create themes folder and themes root file 2025-06-19 10:53:31 +02:00
Belén Albeza 4a9f6ea04e 🐛 Fix panning and scroll when dpr > 1 (render wasm) 2025-06-19 10:42:19 +02:00
luisδμ e7e39a5521 Avoid duplicated property names adding a number (#6681)
*  Avoid repeated property names appending a number

* 📎 PR changes

* 🐛 Adjust rules for incrementing numbers in prop names
2025-06-19 09:11:41 +02:00
Belén Albeza 70a29c43ec 🔧 Fix race condition in colorpicket gradient tests (#6722) 2025-06-19 08:57:38 +02:00
Andrey Antukh 386c729507 Merge remote-tracking branch 'origin/staging' into develop 2025-06-19 08:49:11 +02:00
Alejandro Alonso 219dca3ab8 Merge pull request #6723 from penpot/elenatorro-11385-fix-text-gradients
🐛 Fix text fill gradients and add visual regression test for text…
2025-06-19 07:03:19 +02:00
Elena Torro 5c120b601c 🐛 Fix text fill gradients and add visual regression test for text styles 2025-06-18 18:02:28 +02:00
Elena Torro cf8006ce9c 🔧 Add option to skip tutorial/walkthrough when creating profiles for dev purposes 2025-06-18 17:00:46 +02:00
Kelp 71afccbeb5 Adds new font-size icon to the DS
Signed-off-by: Kelp <5446186+NatachaMenjibar@users.noreply.github.com>
2025-06-18 15:55:13 +02:00
Andrey Antukh bbb9713f97 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 14:09:49 +02:00
Andrey Antukh 063c6e7771 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 13:34:47 +02:00
Andrey Antukh b8b56d5aa4 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:54:17 +02:00
Andrey Antukh 402508a710 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:41:30 +02:00
Andrey Antukh 88ed08916e Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:39:23 +02:00
Andrey Antukh a9a0970001 Merge pull request #6679 from penpot/niwinz-develop-enhacements-4
 Add editors count to get-owned-teams rpc method response
2025-06-18 10:38:48 +02:00
Andrey Antukh 5ea515cc9f Merge pull request #6713 from mdbenito/doc/undo-ns
📚 Document app.main.data.workspace.undo
2025-06-18 10:37:42 +02:00
Miguel de Benito Delgado c0df527b3d 📚 Document app.main.data.workspace.undo 2025-06-18 09:52:15 +02:00
Alejandro Alonso 6a46110f80 Merge pull request #6672 from penpot/superalex-fix-focus-mode-wasm
🐛 Fix focus mode for wasm render
2025-06-17 16:55:47 +02:00
Alejandro Alonso 1c7aea4b84 🐛 Fix focus mode for wasm render 2025-06-17 16:42:45 +02:00
Andrey Antukh 90116c207f Merge remote-tracking branch 'origin/staging' into develop 2025-06-17 16:23:35 +02:00
Pablo Alba 46fe3a6239 📚 Add comments on convoluted variants code (#6704) 2025-06-17 16:17:56 +02:00
Elena Torró 01311225c7 Merge pull request #6695 from penpot/superalex-fix-allocate-0-bytes-path-attrs
🐛 Fix wasm render path issues
2025-06-17 14:44:15 +02:00
Elena Torró 717f3e1b32 Merge pull request #6703 from penpot/elenatorro-update-render-wasm-docs
📚 Add schemas and links to render-wasm README
2025-06-17 14:24:47 +02:00
Elena Torro 9a44bd0967 📚 Add schemas and links to render-wasm README 2025-06-17 14:06:46 +02:00
Belén Albeza b92e108205 Are more visual regression tests for wasm (#6702) 2025-06-17 12:39:38 +02:00
Alejandro Alonso 8c6a80829f Merge pull request #6671 from penpot/azazeln28-refactor-minor-perf-issues
♻️ Refactor some minor perf issues
2025-06-17 11:30:43 +02:00
Elena Torró 039a544990 Merge pull request #6675 from penpot/alotor-grid-helpers
 Add grid helpers to wasm
2025-06-17 11:14:18 +02:00
Alejandro Alonso 60dbf02896 Merge pull request #6701 from penpot/elenatorro-fix-custom-font-load
🐛 Fix storing custom fonts
2025-06-17 10:06:02 +02:00
Elena Torro d248dd09bc 🐛 Fix storing custom fonts 2025-06-17 09:38:17 +02:00
Alejandro Alonso 81d2b9a82e 🐛 Fix group fills propagation when fill is none 2025-06-17 09:17:54 +02:00
Alejandro Alonso 1bb6f2754c 🐛 Fix allocate 0 bytes for path attrs 2025-06-17 08:43:00 +02:00
Andrey Antukh df84396fea Merge remote-tracking branch 'origin/staging' into develop 2025-06-16 16:51:14 +02:00
mirakernelandkira a56822ad99 🐛 Avoid crash on empty string in interaction prototype (#6687)
Fixes #6469

Using `uuid/parse` caused a crash when "Relative to" field was set to "auto",
producing an empty string. This change uses `uuid/parse*` instead, which safely
returns nil for invalid or empty inputs, preventing the crash.

Signed-off-by: Dmitriy Mikheev <mirakernel.disroot.org>
Co-authored-by: kira <kira@kira.kira>
2025-06-16 15:18:59 +02:00
Elena Torró 4869373a43 🔧 Add methods to render text as path (#6624)
* 🔧 Refactor text strokes drawing

* 🔧 Add text to path methods for future usage

* 📚 Add text as paths internal documentation
2025-06-16 13:37:29 +02:00
Pablo Alba 2d36a1f3e0 🐛 Fix when retrieving a variant from several with same props, it get the last one 2025-06-16 12:23:40 +02:00
Alejandro Alonso 38941d4811 Merge pull request #6676 from penpot/elenatorro-fix-load-pending-single-attr
🐛 Fix parsing pending callback on setting single shape attr
2025-06-16 11:50:57 +02:00
alonso.torres 0be8a6e0e6 Add grid helpers to wasm 2025-06-16 09:55:35 +02:00
Marina López 3624a14141 Subscription tests (#6669)
*  Subscription tests

*  Subscription tests
2025-06-16 09:31:50 +02:00
Peter Kahoun 141431bb9e Update cs.po - inflection fixes (#6677)
Signed-off-by: Peter Kahoun <kahi.cz@gmail.com>
2025-06-16 09:29:47 +02:00
Elena Torro f58ee2c89f 🔧 Add visual regression tests for font load 2025-06-11 13:22:23 +02:00
Pablo Alba 925b6c02d6 🎉 Separate the content of the text of the rest of properties on variants 2025-06-11 11:22:43 +02:00
Pablo Alba 9761cba337 ♻️ Restore separate the content of the text of the rest of properties on components updates
This reverts commit b2aaa5f0df.
2025-06-11 11:21:54 +02:00
Andrey Antukh 71f5806e23 Add editors count to get-owned-teams rpc method response 2025-06-11 08:40:58 +02:00
Elena Torro 330bee7839 🐛 Fix parsing pending callback on setting single shape attr 2025-06-10 21:34:41 +02:00
Andrey Fedorov d44e4e5275 Add zip file format import for tokens 2025-06-10 17:32:06 +02:00
Aitor Moreno 369e134bed ♻️ Refactor some minor perf issues 2025-06-10 16:00:10 +02:00
Elena Torró f02dd9f8dc Merge pull request #6651 from penpot/superalex-path-fixes
🐛 Path fixes
2025-06-10 12:11:25 +02:00
Alejandro Alonso e91550cd9d Merge pull request #6646 from penpot/ladybenko-10904-playwright-wasm
🔧 Set up visual regression tests for wasm renderer
2025-06-10 09:24:59 +02:00
Alejandro Alonso ed76b1b1ee 🎉 Support for webp images (#6665) 2025-06-10 08:40:30 +02:00
Belén Albeza afdbb5cf2f 📚 Add documentation specific for wasm visual regression tests 2025-06-09 17:46:18 +02:00
Belén Albeza 971b92a75b 🔧 Make mockAsset to accept an array of asset ids too 2025-06-09 17:46:18 +02:00
Belén Albeza 479406b884 🔧 Add initial snapshots 2025-06-09 17:46:18 +02:00
Belén Albeza 1a10b7ebfd 🔧 Wait for first render using a custom event (visual regression tests) 2025-06-09 17:46:18 +02:00
Belén Albeza 59a4b51d2c 🔧 Set up playwright project for render wasm 2025-06-09 17:01:29 +02:00
Alejandro Alonso 78d6166bac 🐛 Fix caps for rounded paths 2025-06-09 14:40:54 +02:00
Alejandro Alonso 8db910baee 🐛 Fix rendering vertical and horizontal paths 2025-06-09 13:04:18 +02:00
Alejandro Alonso a9702f104d 🐛 Fix shapes without fills contained in a group with fills 2025-06-09 13:04:18 +02:00
2127 changed files with 340224 additions and 104070 deletions

No files matched your search

-342
View File
@@ -1,342 +0,0 @@
version: 2.1
jobs:
lint:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
steps:
- checkout
- run:
name: "fmt check"
working_directory: "."
command: |
yarn install
yarn run fmt:clj:check
- run:
name: "lint clj common"
working_directory: "."
command: |
yarn run lint:clj:common
- run:
name: "lint clj frontend"
working_directory: "."
command: |
yarn run lint:clj:frontend
- run:
name: "lint clj backend"
working_directory: "."
command: |
yarn run lint:clj:backend
- run:
name: "lint clj exporter"
working_directory: "."
command: |
yarn run lint:clj:exporter
- run:
name: "lint clj library"
working_directory: "."
command: |
yarn run lint:clj:library
test-common:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
- run:
name: "JVM tests"
working_directory: "./common"
command: |
clojure -M:dev:test
- run:
name: "NODE tests"
working_directory: "./common"
command: |
yarn install
yarn run test
- save_cache:
paths:
- ~/.m2
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
test-frontend:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "install dependencies"
working_directory: "./frontend"
# We install playwright here because the dependent tasks
# uses the same cache as this task so we prepopulate it
command: |
yarn install
yarn run playwright install chromium
- run:
name: "lint scss on frontend"
working_directory: "./frontend"
command: |
yarn run lint:scss
- run:
name: "unit tests"
working_directory: "./frontend"
command: |
yarn run test
- save_cache:
paths:
- ~/.m2
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
test-library:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies and build
working_directory: "./library"
command: |
yarn install
- run:
name: Build and Test
working_directory: "./library"
command: |
./scripts/build
yarn run test
test-components:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g -Xms2g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies
working_directory: "./frontend"
command: |
yarn install
yarn run playwright install chromium
- run:
name: Build Storybook
working_directory: "./frontend"
command: yarn run build:storybook
- run:
name: Serve Storybook and run tests
working_directory: "./frontend"
command: |
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
"npx http-server storybook-static --port 6006 --silent" \
"npx wait-on tcp:6006 && yarn test:storybook"
test-integration:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: large
environment:
JAVA_OPTS: -Xmx6g -Xms2g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "integration tests"
working_directory: "./frontend"
command: |
yarn install
yarn run build:app:assets
yarn run build:app
yarn run build:app:libs
yarn run playwright install chromium
yarn run test:e2e -x --workers=4
test-backend:
docker:
- image: penpotapp/devenv:latest
- image: cimg/postgres:14.5
environment:
POSTGRES_USER: penpot_test
POSTGRES_PASSWORD: penpot_test
POSTGRES_DB: penpot_test
- image: cimg/redis:7.0.5
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "backend/deps.edn" }}
- run:
name: "tests"
working_directory: "./backend"
command: |
clojure -M:dev:test --reporter kaocha.report/documentation
environment:
PENPOT_TEST_DATABASE_URI: "postgresql://localhost/penpot_test"
PENPOT_TEST_DATABASE_USERNAME: penpot_test
PENPOT_TEST_DATABASE_PASSWORD: penpot_test
PENPOT_TEST_REDIS_URI: "redis://localhost/1"
- save_cache:
paths:
- ~/.m2
- ~/.gitlibs
key: v1-dependencies-{{ checksum "backend/deps.edn" }}
test-render-wasm:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
steps:
- checkout
- run:
name: "fmt check"
working_directory: "./render-wasm"
command: |
cargo fmt --check
- run:
name: "lint"
working_directory: "./render-wasm"
command: |
./lint
- run:
name: "cargo tests"
working_directory: "./render-wasm"
command: |
./test
workflows:
penpot:
jobs:
- lint
- test-frontend:
requires:
- lint: success
- test-library:
requires:
- test-frontend: success
- lint: success
- test-components:
requires:
- test-frontend: success
- lint: success
- test-integration:
requires:
- test-frontend: success
- lint: success
- test-backend:
requires:
- lint: success
- test-common:
requires:
- lint: success
- test-render-wasm
+18
View File
@@ -45,10 +45,25 @@
:potok/reify-type :potok/reify-type
{:level :error} {:level :error}
:redundant-primitive-coercion
{:level :off}
:unused-excluded-var
{:level :off}
:unresolved-excluded-var
{:level :off}
:missing-protocol-method
{:level :off}
:unresolved-namespace :unresolved-namespace
{:level :warning {:level :warning
:exclude [data_readers]} :exclude [data_readers]}
:unused-value
{:level :off}
:single-key-in :single-key-in
{:level :warning} {:level :warning}
@@ -64,6 +79,9 @@
:redundant-nested-call :redundant-nested-call
{:level :off} {:level :off}
:redundant-str-call
{:level :off}
:earmuffed-var-not-dynamic :earmuffed-var-not-dynamic
{:level :off} {:level :off}
@@ -0,0 +1,38 @@
---
name: New Render Bug Report
about: Create a report about the bugs you have found in the new render
title: ''
labels: new render
assignees: claragvinola
---
**Describe the bug**
A clear and concise description of what the bug is.
**Steps to Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots or screen recordings**
If applicable, add screenshots or screen recording to help illustrate your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+1
View File
@@ -13,6 +13,7 @@
- [ ] Add a detailed explanation of how to reproduce the issue and/or verify the fix, if applicable. - [ ] Add a detailed explanation of how to reproduce the issue and/or verify the fix, if applicable.
- [ ] Include screenshots or videos, if applicable. - [ ] Include screenshots or videos, if applicable.
- [ ] Add or modify existing integration tests in case of bugs or new features, if applicable. - [ ] Add or modify existing integration tests in case of bugs or new features, if applicable.
- [ ] Refactor any modified SCSS files following the refactor guide.
- [ ] Check CI passes successfully. - [ ] Check CI passes successfully.
- [ ] Update the `CHANGES.md` file, referencing the related GitHub issue, if applicable. - [ ] Update the `CHANGES.md` file, referencing the related GitHub issue, if applicable.
+93
View File
@@ -0,0 +1,93 @@
name: Bundles Builder
on:
# Create bundle from manual action
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
build_wasm:
description: 'BUILD_WASM. Valid values: yes, no'
type: string
required: false
default: 'yes'
build_storybook:
description: 'BUILD_STORYBOOK. Valid values: yes, no'
type: string
required: false
default: 'yes'
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
build_wasm:
description: 'BUILD_WASM. Valid values: yes, no'
type: string
required: false
default: 'yes'
build_storybook:
description: 'BUILD_STORYBOOK. Valid values: yes, no'
type: string
required: false
default: 'yes'
jobs:
build-bundle:
name: Build and Upload Penpot Bundle
runs-on: penpot-runner-01
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
- name: Build bundle
env:
BUILD_WASM: ${{ inputs.build_wasm }}
BUILD_STORYBOOK: ${{ inputs.build_storybook }}
run: ./manage.sh build-bundle
- name: Prepare directories for zipping
run: |
mkdir zips
mv bundles penpot
- name: Create zip bundle
run: |
echo "📦 Packaging Penpot bundle..."
zip -r zips/penpot.zip penpot
- name: Upload Penpot bundle to S3
run: |
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}.zip --metadata bundle-version=${{ steps.vars.outputs.bundle_version }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 📦 *[PENPOT] Error building penpot bundles.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
Bundle version: `${{ steps.vars.outputs.bundle_version }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+21
View File
@@ -0,0 +1,21 @@
name: _DEVELOP
on:
schedule:
- cron: '16 5-20 * * 1-5'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "develop"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "develop"
+41
View File
@@ -0,0 +1,41 @@
name: DevEnv Docker Image Builder
on:
workflow_dispatch:
jobs:
build-and-push:
name: Build and push DevEnv Docker image
environment: release-admins
runs-on: penpot-runner-02
steps:
- name: Set common environment variables
run: |
# Each job execution will use its own docker configuration.
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.PUB_DOCKER_USERNAME }}
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
- name: Build and push DevEnv Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'penpotapp/devenv'
with:
context: ./docker/devenv/
file: ./docker/devenv/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.DOCKER_IMAGE }}:latest
cache-from: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max
+183
View File
@@ -0,0 +1,183 @@
name: Docker Images Builder
on:
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch or ref'
type: string
required: true
default: 'develop'
jobs:
build-and-push:
name: Build and Push Penpot Docker Images
runs-on: penpot-runner-02
steps:
- name: Set common environment variables
run: |
# Each job execution will use its own docker configuration.
echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Download Penpot Bundles
id: bundles
env:
FILE_NAME: penpot-${{ steps.vars.outputs.gh_ref }}.zip
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
tmp=$(aws s3api head-object \
--bucket ${{ secrets.S3_BUCKET }} \
--key "$FILE_NAME" \
--query 'Metadata."bundle-version"' \
--output text)
echo "bundle_version=$tmp" >> $GITHUB_OUTPUT
pushd docker/images
aws s3 cp s3://${{ secrets.S3_BUCKET }}/$FILE_NAME .
unzip $FILE_NAME > /dev/null
mv penpot/backend bundle-backend
mv penpot/frontend bundle-frontend
mv penpot/exporter bundle-exporter
mv penpot/storybook bundle-storybook
mv penpot/mcp bundle-mcp
popd
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# To avoid the “429 Too Many Requests” error when downloading
# images from DockerHub for unregistered users.
# https://docs.docker.com/docker-hub/usage/
- name: Login to DockerHub Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.PUB_DOCKER_USERNAME }}
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images:
frontend
backend
exporter
storybook
mcp
labels: |
bundle_version=${{ steps.bundles.outputs.bundle_version }}
- name: Build and push Backend Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'backend'
BUNDLE_PATH: './bundle-backend'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.backend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Frontend Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'frontend'
BUNDLE_PATH: './bundle-frontend'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.frontend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Exporter Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'exporter'
BUNDLE_PATH: './bundle-exporter'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.exporter
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push Storybook Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'storybook'
BUNDLE_PATH: './bundle-storybook'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.storybook
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Build and push MCP Docker image
uses: docker/build-push-action@v6
env:
DOCKER_IMAGE: 'mcp'
BUNDLE_PATH: './bundle-mcp'
with:
context: ./docker/images/
file: ./docker/images/Dockerfile.mcp
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ steps.vars.outputs.gh_ref }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:buildcache,mode=max
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🐳 *[PENPOT] Error building penpot docker images.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
📦 Bundle: `${{ steps.bundles.outputs.bundle_version }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
@@ -0,0 +1,21 @@
name: _NITRATE MODULE
on:
schedule:
- cron: '36 5-20 * * 1-5'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "nitrate-module"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "nitrate-module"
@@ -0,0 +1,14 @@
name: _STAGING RENDER
on:
schedule:
- cron: '36 5-20 * * 1-5'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "staging-render"
build_wasm: "yes"
build_storybook: "yes"
+21
View File
@@ -0,0 +1,21 @@
name: _STAGING
on:
schedule:
- cron: '36 5-20 * * 1-5'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: "staging"
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: "staging"
+46
View File
@@ -0,0 +1,46 @@
name: _TAG
on:
push:
tags:
- '*'
jobs:
build-bundle:
uses: ./.github/workflows/build-bundle.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
build_wasm: "yes"
build_storybook: "yes"
build-docker:
needs: build-bundle
uses: ./.github/workflows/build-docker.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
notify:
name: Notifications
runs-on: ubuntu-24.04
needs: build-docker
steps:
- name: Notify Mattermost
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
🐳 *[PENPOT] Docker image available: ${{ github.ref_name }}*
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
publish-final-tag:
if: ${{ !contains(github.ref_name, '-RC') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && contains(github.ref_name, '.') }}
needs: build-docker
uses: ./.github/workflows/release.yml
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- name: Check Commit Type - name: Check Commit Type
uses: gsactions/commit-message-checker@v2 uses: gsactions/commit-message-checker@v2
with: with:
pattern: '^(Merge|:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle):)\s[A-Z].*[^.]$' pattern: '^(((:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle|rewind|construction_worker):)\s[A-Z].*[^.])|(Merge|Revert|Reapply).+[^.])$'
flags: 'gm' flags: 'gm'
error: 'Commit should match CONTRIBUTING.md guideline' error: 'Commit should match CONTRIBUTING.md guideline'
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request
@@ -0,0 +1,125 @@
name: Plugins/api-doc deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/libs/plugin-types/index.d.ts'
- 'plugins/libs/plugin-types/REAME.md'
- 'plugins/tools/typedoc.css'
- 'plugins/CHANGELOG.md'
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: Build docs
working-directory: plugins
shell: bash
run: pnpm run build:doc
- name: Select Worker name
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangler-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩📚 *[PENPOT PLUGINS] Error deploying API documentation.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
@@ -0,0 +1,127 @@
name: Plugins/package deployer
on:
# Deploy package from manual action
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
plugin_name:
description: 'Pluging name (like plugins/apps/<plugin_name>-plugin)'
type: string
required: true
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch'
type: string
required: true
default: 'develop'
plugin_name:
description: 'Publig name (from plugins/apps/<plugin_name>-plugin)'
type: string
required: true
permissions:
contents: read
jobs:
deploy:
runs-on: penpot-runner-01
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
working-directory: plugins
shell: bash
run: npx nx build ${{ inputs.plugin_name }}-plugin
- name: Select Worker name
run: |
REF="${{ inputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config apps/${{ inputs.plugin_name }}-plugin/wrangler.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩📦 *[PENPOT PLUGINS] Error deploying ${{ env.WORKER_NAME }}.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
Plugin name: `${{ inputs.plugin_name }}-plugin`
Cloudflare worker name: `${{ env.WORKER_NAME }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
@@ -0,0 +1,143 @@
name: Plugins/packages deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/apps/*-plugin/**'
- 'libs/plugins-styles/**'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
colors_to_tokens: ${{ steps.filter.outputs.colors_to_tokens }}
create_palette: ${{ steps.filter.outputs.create_palette }}
lorem_ipsum: ${{ steps.filter.outputs.lorem_ipsum }}
rename_layers: ${{ steps.filter.outputs.rename_layers }}
contrast: ${{ steps.filter.outputs.contrast }}
icons: ${{ steps.filter.outputs.icons }}
poc_state: ${{ steps.filter.outputs.poc_state }}
table: ${{ steps.filter.outputs.table }}
# [For new plugins]
# Add more outputs here
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
colors_to_tokens:
- 'plugins/apps/colors-to-tokens-plugin/**'
- 'libs/plugins-styles/**'
contrast:
- 'plugins/apps/contrast-plugin/**'
- 'libs/plugins-styles/**'
create_palette:
- 'plugins/apps/create-palette-plugin/**'
- 'libs/plugins-styles/**'
icons:
- 'plugins/apps/icons-plugin/**'
- 'libs/plugins-styles/**'
lorem_ipsum:
- 'plugins/apps/lorem-ipsum-plugin/**'
- 'libs/plugins-styles/**'
rename_layers:
- 'plugins/apps/rename-layers-plugin/**'
- 'libs/plugins-styles/**'
table:
- 'plugins/apps/table-plugin/**'
- 'libs/plugins-styles/**'
# [For new plugins]
# Add more plugin filters here
# another_plugin:
# - 'plugins/apps/another-plugin/**'
# - 'libs/plugins-styles/**'
colors-to-tokens-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.colors_to_tokens == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: colors-to-tokens
contrast-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.contrast == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: contrast
create-palette-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.create_palette == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: create-palette
icons-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.icons == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: icons
lorem-ipsum-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.lorem_ipsum == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: lorem-ipsum
rename-layers-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.rename_layers == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: rename-layers
table-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.table == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: table
# [For new plugins]
# Add more jobs for other plugins below, following the same pattern
# another-plugin:
# needs: detect-changes
# if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.another_plugin == 'true'
# uses: ./.github/workflows/plugins-deploy-package.yml
# secrets: inherit
# with:
# gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
# plugin_name: another
@@ -0,0 +1,123 @@
name: Plugins/styles-doc deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/apps/example-styles/**'
- 'plugins/libs/plugins-styles/**'
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: Build styles
working-directory: plugins
shell: bash
run: npx nx run example-styles:build
- name: Select Worker name
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+114
View File
@@ -0,0 +1,114 @@
name: Release Publisher
on:
workflow_dispatch:
inputs:
gh_ref:
description: 'Tag to release'
type: string
required: true
workflow_call:
inputs:
gh_ref:
description: 'Tag to release'
type: string
required: true
permissions:
contents: write
jobs:
release:
environment: release-admins
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.vars.outputs.gh_ref }}
release_notes: ${{ steps.extract_release_notes.outputs.release_notes }}
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# --- Publicly release the docker images ---
- name: Configure ECR credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.DOCKER_USERNAME }}
aws-secret-access-key: ${{ secrets.DOCKER_PASSWORD }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Install Skopeo
run: |
sudo apt-get update -y
sudo apt-get install -y skopeo
- name: Copy images from AWS ECR to Docker Hub
env:
AWS_REGION: ${{ secrets.AWS_REGION }}
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
PUB_DOCKER_USERNAME: ${{ secrets.PUB_DOCKER_USERNAME }}
PUB_DOCKER_PASSWORD: ${{ secrets.PUB_DOCKER_PASSWORD }}
TAG: ${{ steps.vars.outputs.gh_ref }}
run: |
aws ecr get-login-password --region $AWS_REGION | \
skopeo login --username AWS --password-stdin \
$DOCKER_REGISTRY
echo "$PUB_DOCKER_PASSWORD" | skopeo login --username "$PUB_DOCKER_USERNAME" --password-stdin docker.io
IMAGES=("frontend" "backend" "exporter" "storybook")
for image in "${IMAGES[@]}"; do
skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$TAG
for alias in main latest; do
skopeo copy --all \
docker://$DOCKER_REGISTRY/$image:$TAG \
docker://docker.io/penpotapp/$image:$alias
done
done
# --- Release notes extraction ---
- name: Extract release notes from CHANGES.md
id: extract_release_notes
env:
TAG: ${{ steps.vars.outputs.gh_ref }}
run: |
RELEASE_NOTES=$(awk "/^## $TAG$/{flag=1; next} /^## /{flag=0} flag" CHANGES.md | awk '{$1=$1};1')
if [ -z "$RELEASE_NOTES" ]; then
RELEASE_NOTES="No changes for $TAG according to CHANGES.md"
fi
echo "release_notes<<EOF" >> $GITHUB_OUTPUT
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# --- Create GitHub release ---
- name: Create GitHub release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.vars.outputs.gh_ref }}
name: ${{ steps.vars.outputs.gh_ref }}
body: ${{ steps.extract_release_notes.outputs.release_notes }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🚀 *[PENPOT] Error releasing penpot.*
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
+45
View File
@@ -0,0 +1,45 @@
name: "MCP CI"
on:
pull_request:
branches:
- develop
- staging
- main
types:
- opened
- synchronize
paths:
- 'mcp/**'
push:
branches:
- develop
- staging
- main
paths:
- 'mcp/**'
jobs:
test:
name: "Test"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup
working-directory: ./mcp
run: ./scripts/setup
- name: Check
working-directory: ./mcp
run: |
pnpm run fmt:check;
pnpm -r run build;
pnpm -r run types:check;
+337
View File
@@ -0,0 +1,337 @@
name: "CI"
defaults:
run:
shell: bash
on:
pull_request:
types:
- opened
- synchronize
push:
branches:
- develop
- staging
concurrency:
group: ${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint:
name: "Linter"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check clojure code format
run: |
./scripts/lint
test-common:
name: "Common Tests"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run tests on JVM
working-directory: ./common
run: |
clojure -M:dev:test
- name: Run tests on NODE
working-directory: ./common
run: |
./scripts/test
test-plugins:
name: Plugins Runtime Linter & Tests
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
id: setup-node
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
pnpm install;
- name: Run Lint
working-directory: ./plugins
run: pnpm run lint
- name: Run Format Check
working-directory: ./plugins
run: pnpm run format:check
- name: Run Test
working-directory: ./plugins
run: pnpm run test
- name: Build runtime
working-directory: ./plugins
run: pnpm run build
- name: Build plugins
working-directory: ./plugins
run: pnpm run build:plugins
- name: Build styles
working-directory: ./plugins
run: pnpm run build:styles-example
test-frontend:
name: "Frontend Tests"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Unit Tests
working-directory: ./frontend
run: |
./scripts/test
- name: Component Tests
working-directory: ./frontend
env:
VITEST_BROWSER_TIMEOUT: 120000
run: |
./scripts/test-components
test-render-wasm:
name: "Render WASM Tests"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Format
working-directory: ./render-wasm
run: |
cargo fmt --check
- name: Lint
working-directory: ./render-wasm
run: |
./lint
- name: Test
working-directory: ./render-wasm
run: |
./test
test-backend:
name: "Backend Tests"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
services:
postgres:
image: postgres:17
# Provide the password for postgres
env:
POSTGRES_USER: penpot_test
POSTGRES_PASSWORD: penpot_test
POSTGRES_DB: penpot_test
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: valkey/valkey:9
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run tests
working-directory: ./backend
env:
PENPOT_TEST_DATABASE_URI: "postgresql://postgres/penpot_test"
PENPOT_TEST_DATABASE_USERNAME: penpot_test
PENPOT_TEST_DATABASE_PASSWORD: penpot_test
PENPOT_TEST_REDIS_URI: "redis://redis/1"
run: |
clojure -M:dev:test --reporter kaocha.report/documentation
test-library:
name: "Library Tests"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run tests
working-directory: ./library
run: |
./scripts/test
build-integration:
name: "Build Integration Bundle"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build Bundle
working-directory: ./frontend
run: |
./scripts/build 0.0.0
- name: Store Bundle Cache
uses: actions/cache@v4
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
test-integration-1:
name: "Integration Tests 1/4"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
needs: build-integration
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Restore Cache
uses: actions/cache/restore@v4
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
- name: Run Tests
working-directory: ./frontend
run: |
./scripts/test-e2e --shard="1/4";
- name: Upload test result
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-tests-result-1
path: frontend/test-results/
overwrite: true
retention-days: 3
test-integration-2:
name: "Integration Tests 2/4"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
needs: build-integration
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Restore Cache
uses: actions/cache/restore@v4
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
- name: Run Tests
working-directory: ./frontend
run: |
./scripts/test-e2e --shard="2/4";
- name: Upload test result
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-tests-result-2
path: frontend/test-results/
overwrite: true
retention-days: 3
test-integration-3:
name: "Integration Tests 3/4"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
needs: build-integration
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Restore Cache
uses: actions/cache/restore@v4
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
- name: Run Tests
working-directory: ./frontend
run: |
./scripts/test-e2e --shard="3/4";
- name: Upload test result
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-tests-result-3
path: frontend/test-results/
overwrite: true
retention-days: 3
test-integration-4:
name: "Integration Tests 4/4"
runs-on: penpot-runner-02
container: penpotapp/devenv:latest
needs: build-integration
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Restore Cache
uses: actions/cache/restore@v4
with:
key: "integration-bundle-${{ github.sha }}"
path: frontend/resources/public
- name: Run Tests
working-directory: ./frontend
run: |
./scripts/test-e2e --shard="4/4";
- name: Upload test result
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-tests-result-4
path: frontend/test-results/
overwrite: true
retention-days: 3
+6 -3
View File
@@ -5,6 +5,7 @@
!.yarn/releases !.yarn/releases
!.yarn/sdks !.yarn/sdks
!.yarn/versions !.yarn/versions
.pnpm-store
*-init.clj *-init.clj
*.css.json *.css.json
*.jar *.jar
@@ -31,6 +32,7 @@
/.clj-kondo/.cache /.clj-kondo/.cache
/_dump /_dump
/notes /notes
/playground/
/backend/*.md /backend/*.md
/backend/*.sql /backend/*.sql
/backend/*.txt /backend/*.txt
@@ -41,6 +43,7 @@
/backend/resources/public/assets /backend/resources/public/assets
/backend/resources/public/media /backend/resources/public/media
/backend/target/ /backend/target/
/backend/experiments
/bundle* /bundle*
/cd.md /cd.md
/clj-profiler/ /clj-profiler/
@@ -51,9 +54,6 @@
/exporter/target /exporter/target
/frontend/.storybook/preview-body.html /frontend/.storybook/preview-body.html
/frontend/.storybook/preview-head.html /frontend/.storybook/preview-head.html
/frontend/cypress/fixtures/validuser.json
/frontend/cypress/videos/*/
/frontend/cypress/videos/*/
/frontend/dist/ /frontend/dist/
/frontend/npm-debug.log /frontend/npm-debug.log
/frontend/out/ /frontend/out/
@@ -70,6 +70,8 @@
/vendor/svgclean/bundle*.js /vendor/svgclean/bundle*.js
/web /web
/library/target/ /library/target/
/library/*.zip
/external
clj-profiler/ clj-profiler/
node_modules node_modules
@@ -79,3 +81,4 @@ node_modules
/playwright/.cache/ /playwright/.cache/
/render-wasm/target/ /render-wasm/target/
/**/.yarn/* /**/.yarn/*
/.pnpm-store
+1 -1
View File
@@ -1 +1 @@
v22.13.1 v22.22.0
-40
View File
@@ -1,40 +0,0 @@
dist: xenial
language: generic
sudo: required
cache:
directories:
- $HOME/.m2
services:
- docker
branches:
only:
- master
- develop
install:
- curl -O https://download.clojure.org/install/linux-install-1.10.1.447.sh
- chmod +x linux-install-1.10.1.447.sh
- sudo ./linux-install-1.10.1.447.sh
before_script:
- env | sort
script:
- ./manage.sh build-devenv
- ./manage.sh run-frontend-tests
- ./manage.sh run-backend-tests
- ./manage.sh build-images
- ./manage.sh run
after_script:
- docker images
notifications:
email: false
env:
- NODE_VERSION=10.16.0
+412 -36
View File
@@ -1,6 +1,382 @@
# CHANGELOG # CHANGELOG
## 2.8.0 (Next / Unreleased) ## 2.13.1
### :bug: Bugs fixed
- Fix PDF Exporter outputs empty page when board has A4 format [Taiga #13181](https://tree.taiga.io/project/penpot/issue/13181)
## 2.13.0
### :heart: Community contributions (Thank you!)
- Fix mask issues with component swap (by @dfelinto) [Github #7675](https://github.com/penpot/penpot/issues/7675)
### :sparkles: New features & Enhancements
- Add new Box Shadow Tokens [Taiga #10201](https://tree.taiga.io/project/penpot/us/10201)
- Make i18n translation files load on-demand [Taiga #11474](https://tree.taiga.io/project/penpot/us/11474)
- Add deleted files to dashboard [Taiga #8149](https://tree.taiga.io/project/penpot/us/8149)
### :bug: Bugs fixed
- Fix problem when drag+duplicate a full grid [Taiga #12565](https://tree.taiga.io/project/penpot/issue/12565)
- Fix problem when pasting elements in reverse flex layout [Taiga #12460](https://tree.taiga.io/project/penpot/issue/12460)
- Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339)
- Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797)
- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915)
- Fix color assets from shared libraries not appearing as assets in Selected colors panel [Taiga #12957](https://tree.taiga.io/project/penpot/issue/12957)
- Fix CSS generated box-shadow property [Taiga #12997](https://tree.taiga.io/project/penpot/issue/12997)
- Fix inner shadow selector on shadow token [Taiga #12951](https://tree.taiga.io/project/penpot/issue/12951)
- Fix missing text color token from selected shapes in selected colors list [Taiga #12956](https://tree.taiga.io/project/penpot/issue/12956)
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
- Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865)
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
- Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167)
- Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171)
- Remove whitespaces from asset export filename [Github #8133](https://github.com/penpot/penpot/pull/8133)
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
- Fix import a file with shadow tokens [Taiga #13229](https://tree.taiga.io/project/penpot/issue/13229)
- Fix allow spaces on token description [Taiga #13184](https://tree.taiga.io/project/penpot/issue/13184)
- Fix error when creating a token with an invalid name [Taiga #13219](https://tree.taiga.io/project/penpot/issue/13219)
## 2.12.1
### :bug: Bugs fixed
- Fix setting a portion of text as bold or underline messes things up [Github #7980](https://github.com/penpot/penpot/issues/7980)
- Fix problem with style in fonts input [Taiga #12935](https://tree.taiga.io/project/penpot/issue/12935)
- Fix problem with path editor and right click [Github #7917](https://github.com/penpot/penpot/issues/7917)
## 2.12.0
### :boom: Breaking changes & Deprecations
#### Backend RPC API changes
The backend RPC API URLS are changed from `/api/rpc/command/<name>` to
`/api/main/methods/<name>`. The previous PATH is preserved for backward
compatibility; however, if you are a user of this API, it is strongly
recommended that you adapt your code to use the new PATH.
#### Updated SSO Callback URL
The OAuth / Single Sign-On (SSO) callback endpoint has changed to
align with the new OpenID Connect (OIDC) implementation.
Old callback URL:
```
https://<your_domain>/api/auth/oauth/<oauth_provider>/callback
```
New callback URL:
```
https://<your_domain>/api/auth/oidc/callback
```
**Action required:**
If you have SSO/Social-Auth configured on your on-premise instance,
the following actions are required before update:
Update your OAuth or SSO provider configuration (e.g., Okta, Google,
Azure AD, etc.) to use the new callback URL. Failure to update may
result in authentication failures after upgrading.
**Reason for change:**
This update standardizes all authentication flows under the single URL
and makis it more modular, enabling the ability to configure SSO auth
provider dinamically.
#### Changes on default docker compose
We have updated the `docker/images/docker-compose.yaml` with a small
change related to the `PENPOT_SECRET_KEY`. Since this version, this
environment variable is also required on exporter. So if you are using
penpot on-premise you will need to apply the same changes on your own
`docker-compose.yaml` file.
We have removed the Minio server from the `docker/images/docker-compose.yml`
example. It's still usable as before, we just removed the example.
### :rocket: Epics and highlights
### :heart: Community contributions (Thank you!)
- Ensure consistent snap behavior across all zoom levels [Github #7774](https://github.com/penpot/penpot/pull/7774) by [@Tokytome](https://github.com/Tokytome)
- Fix crash in token grid view due to tooltip validation (by @dfelinto) [Github #7887](https://github.com/penpot/penpot/pull/7887)
- Enable Hindi translations on the application
### :sparkles: New features & Enhancements
- Add the ability to select boards to export as PDF [Taiga #12320](https://tree.taiga.io/project/penpot/issue/12320)
- Add toggle for switching boolean property values [Taiga #12341](https://tree.taiga.io/project/penpot/us/12341)
- Make the file export process more reliable [Taiga #12555](https://tree.taiga.io/project/penpot/us/12555)
- Add auth flow changes [Taiga #12333](https://tree.taiga.io/project/penpot/us/12333)
- Add new shape validation mechanism for shapes [Github #7696](https://github.com/penpot/penpot/pull/7696)
- Apply color tokens from sidebar [Taiga #11353](https://tree.taiga.io/project/penpot/us/11353)
- Display tokens in the inspect tab [Taiga #9313](https://tree.taiga.io/project/penpot/us/9313)
- Refactor clipboard behavior to assess some minor inconsistencies and make pasting binary data faster. [Taiga #12571](https://tree.taiga.io/project/penpot/task/12571)
### :bug: Bugs fixed
- Fix text line-height values are wrong [Taiga #12252](https://tree.taiga.io/project/penpot/issue/12252)
- Fix an error translation [Taiga #12402](https://tree.taiga.io/project/penpot/issue/12402)
- Fix pan cursor not disabling viewport guides [Github #6985](https://github.com/penpot/penpot/issues/6985)
- Fix viewport resize on locked shapes [Taiga #11974](https://tree.taiga.io/project/penpot/issue/11974)
- Fix nested variant in a component doesn't keep inherited overrides [Taiga #12299](https://tree.taiga.io/project/penpot/issue/12299)
- Fix on copy instance inside a components chain touched are missing [Taiga #12371](https://tree.taiga.io/project/penpot/issue/12371)
- Fix problem with multiple selection and shadows [Github #7437](https://github.com/penpot/penpot/issues/7437)
- Fix search shortcut [Taiga #10265](https://tree.taiga.io/project/penpot/issue/10265)
- Fix shortcut conflict in text editor (increase/decrease font size vs word selection)
- Fix problem with plugins generating code for pages different than current one [Taiga #12312](https://tree.taiga.io/project/penpot/issue/12312)
- Fix input confirmation behavior is not uniform [Taiga #12294](https://tree.taiga.io/project/penpot/issue/12294)
- Fix copy/pasting application/transit+json [Taiga #12721](https://tree.taiga.io/project/penpot/issue/12721)
- Fix problem with plugins content attribute [Plugins #209](https://github.com/penpot/penpot-plugins/issues/209)
- Fix U and E icon displayed in project list [Taiga #12806](https://tree.taiga.io/project/penpot/issue/12806)
- Fix unpublish library modal not scrolling a long file list [Taiga #12285](https://tree.taiga.io/project/penpot/issue/12285)
- Fix incorrect interaction betwen hower and scroll on assets sidebar [Taiga #12389](https://tree.taiga.io/project/penpot/issue/12389)
- Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841)
- Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492)
- Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843)
- Fix unicode handling on email template abbreviation filter [Github #7966](https://github.com/penpot/penpot/pull/7966)
## 2.11.1
- Fix WEBP shape export on docker images [Taiga #3838](https://tree.taiga.io/project/penpot/issue/3838)
## 2.11.0
### :boom: Breaking changes & Deprecations
- Deprecated configuration variables with the prefix `PENPOT_ASSETS_*`, and will be
removed in future versions:
- The `PENPOT_ASSETS_STORAGE_BACKEND` becomes `PENPOT_OBJECTS_STORAGE_BACKEND` and its
values passes from (`assets-fs` or `assets-s3`) to (`fs` or `s3`)
- The `PENPOT_STORAGE_ASSETS_FS_DIRECTORY` becomes `PENPOT_OBJECTS_STORAGE_FS_DIRECTORY`
- The `PENPOT_STORAGE_ASSETS_S3_BUCKET` becomes `PENPOT_OBJECTS_STORAGE_S3_BUCKET`
- The `PENPOT_STORAGE_ASSETS_S3_REGION` becomes `PENPOT_OBJECTS_STORAGE_S3_REGION`
- The `PENPOT_STORAGE_ASSETS_S3_ENDPOINT` becomes `PENPOT_OBJECTS_STORAGE_S3_ENDPOINT`
- The `PENPOT_STORAGE_ASSETS_S3_IO_THREADS` replaced (see below)
- Add `PENPOT_NETTY_IO_THREADS` and `PENPOT_EXECUTOR_THREADS` variables to provide the
control over concurrency of the shared resources used by netty. Penpot uses the netty IO
threads for AWS S3 SDK and Redis/Valkey communication, and the EXEC threads to perform
out of HTTP serving threads tasks such that cache invalidation, S3 response completion,
configuration reloading and many other auxiliar tasks. By default they use a half number
if available cpus with a minumum of 2 for both executors. You should not touch that
variables unless you are know what you are doing.
- Replace the `PENPOT_STORAGE_ASSETS_S3_IO_THREADS` with a more general configuration
`PENPOT_NETTY_IO_THREADS` used to configure a shared netty resources across different
services which use netty internally (redis connection, S3 SDK client). This
configuration is not very commonly used so don't expected real impact on any user.
### :sparkles: New features & Enhancements
- New composite token: Typography [Taiga #10200](https://tree.taiga.io/project/penpot/us/10200)
- Show current Penpot version [Taiga #11603](https://tree.taiga.io/project/penpot/us/11603)
- Switch several variant copies at the same time [Taiga #11411](https://tree.taiga.io/project/penpot/us/11411)
- Invitations management improvements [Taiga #3479](https://tree.taiga.io/project/penpot/us/3479)
- Alternative ways of creating variants - Button Viewport [Taiga #11931](https://tree.taiga.io/project/penpot/us/11931)
- Reorder properties for a component [Taiga #10225](https://tree.taiga.io/project/penpot/us/10225)
- File Data storage layout refactor [Github #7345](https://github.com/penpot/penpot/pull/7345)
- Make several queries optimization on comment threads [Github #7506](https://github.com/penpot/penpot/pull/7506)
### :bug: Bugs fixed
- Fix selection problems when devtools open [Taiga #11950](https://tree.taiga.io/project/penpot/issue/11950)
- Fix long font names overlap [Taiga #11844](https://tree.taiga.io/project/penpot/issue/11844)
- Fix paste behavior according to the selected element [Taiga #11979](https://tree.taiga.io/project/penpot/issue/11979)
- Fix problem with export size [Github #7160](https://github.com/penpot/penpot/issues/7160)
- Fix multi level library dependencies [Taiga #12155](https://tree.taiga.io/project/penpot/issue/12155)
- Fix component context menu options order in assets tab [Taiga #11941](https://tree.taiga.io/project/penpot/issue/11941)
- Fix error updating library [Taiga #12218](https://tree.taiga.io/project/penpot/issue/12218)
- Fix restoring a variant in another file makes it overlap the existing variant [Taiga #12049](https://tree.taiga.io/project/penpot/issue/12049)
- Fix auto-width changes to fixed when switching variants [Taiga #12172](https://tree.taiga.io/project/penpot/issue/12172)
- Fix component number has no singular translation string [Taiga #12106](https://tree.taiga.io/project/penpot/issue/12106)
- Fix adding/removing identical text fills [Taiga #12287](https://tree.taiga.io/project/penpot/issue/12287)
- Fix scroll on the inspect tab [Taiga #12293](https://tree.taiga.io/project/penpot/issue/12293)
- Fix lock proportion tooltip [Taiga #12326](https://tree.taiga.io/project/penpot/issue/12326)
- Fix internal Error when selecting a set by name in the token theme editor [Taiga #12310](https://tree.taiga.io/project/penpot/issue/12310)
- Fix drag & drop functionality is swapping instead or reordering [Taiga #12254](https://tree.taiga.io/project/penpot/issue/12254)
- Fix variants not syncronizing tokens on switch [Taiga #12290](https://tree.taiga.io/project/penpot/issue/12290)
- Fix incorrect behavior of Alt + Drag for variants [Taiga #12309](https://tree.taiga.io/project/penpot/issue/12309)
- Fix text override is lost after switch [Taiga #12269](https://tree.taiga.io/project/penpot/issue/12269)
- Fix exporting a board crashing the app [Taiga #12384](https://tree.taiga.io/project/penpot/issue/12384)
- Fix nested variant in a component doesn't keep inherited overrides [Taiga #12299](https://tree.taiga.io/project/penpot/issue/12299)
- Fix selected colors not showing colors from children shapes in multiple selection [Taiga #12384](https://tree.taiga.io/project/penpot/issue/12385)
- Fix scrollbar issue in design tab [Taiga #12367](https://tree.taiga.io/project/penpot/issue/12367)
- Fix library update notificacions showing when they should not [Taiga #12397](https://tree.taiga.io/project/penpot/issue/12397)
- Fix remove flex button doesnt work within variant [Taiga #12314](https://tree.taiga.io/project/penpot/issue/12314)
- Fix an error translation [Taiga #12402](https://tree.taiga.io/project/penpot/issue/12402)
- Fix problem with certain text input in some editable labels (pages, components, tokens...) being in conflict with the drag/drop functionality [Taiga #12316](https://tree.taiga.io/project/penpot/issue/12316)
- Fix not controlled theme renaming [Taiga #12411](https://tree.taiga.io/project/penpot/issue/12411)
- Fix paste without selection sends the new element in the back [Taiga #12382](https://tree.taiga.io/project/penpot/issue/12382)
- Fix options button does not work for comments created in the lower part of the screen [Taiga #12422](https://tree.taiga.io/project/penpot/issue/12422)
- Fix problem when checking usage with removed teams [Taiga #12442](https://tree.taiga.io/project/penpot/issue/12442)
- Fix focus mode persisting across page/file navigation [Taiga #12469](https://tree.taiga.io/project/penpot/issue/12469)
- Fix shadow color validation [Github #7705](https://github.com/penpot/penpot/pull/7705)
- Fix exception on selection blend-mode using keyboard [Github #7710](https://github.com/penpot/penpot/pull/7710)
- Fix crash when using decimal (floating-point) values for X/Y or width/height [Taiga #12543](https://tree.taiga.io/project/penpot/issue/12543)
## 2.10.1
### :sparkles: New features & Enhancements
- Improve workpace file loading [Github 7366](https://github.com/penpot/penpot/pull/7366)
### :bug: Bugs fixed
- Fix regression with text shapes creation with Plugins API [Taiga #12244](https://tree.taiga.io/project/penpot/issue/12244)
## 2.10.0
### :rocket: Epics and highlights
- Variants
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
### :sparkles: New features & Enhancements
- Add efficiency enhancements to right sidebar [Github #7182](https://github.com/penpot/penpot/pull/7182)
- Add defaults for artboard drawing [Taiga #494](https://tree.taiga.io/project/penpot/us/494?milestone=465047)
- Continuous display of distances between elements when moving a layer with the keyboard [Taiga #1780](https://tree.taiga.io/project/penpot/us/1780)
- New Number token - unitless values [Taiga #10936](https://tree.taiga.io/project/penpot/us/10936)
- New font-family token [Taiga #10937](https://tree.taiga.io/project/penpot/us/10937)
- New text case token [Taiga #10942](https://tree.taiga.io/project/penpot/us/10942)
- New text-decoration token [Taiga #10941](https://tree.taiga.io/project/penpot/us/10941)
- New letter spacing token [Taiga #10940](https://tree.taiga.io/project/penpot/us/10940)
- New font weight token [Taiga #10939](https://tree.taiga.io/project/penpot/us/10939)
- Upgrade Node to v22.18.0 [Github #7283](https://github.com/penpot/penpot/pull/7283)
- Upgrade the base docker image for penpot frontend to v1.29.1 [Github #7283](https://github.com/penpot/penpot/pull/7283)
- Create variant from an existing component [Taiga #2088](https://tree.taiga.io/project/penpot/us/2088)
- Create variant from an existing variant [Taiga #8282](https://tree.taiga.io/project/penpot/us/8282)
- Actions over a component with variants [Taiga #10503](https://tree.taiga.io/project/penpot/us/10503)
- Create a variant by dragging a component into a component with variants [Taiga #8134](https://tree.taiga.io/project/penpot/us/8134)
- Transform a variant into an individual component [Taiga #8141](https://tree.taiga.io/project/penpot/us/8141)
- Delete variant [Taiga #6890](https://tree.taiga.io/project/penpot/us/6890)
- Restore an orphaned copy of a variant [Taiga #10446](https://tree.taiga.io/project/penpot/us/10446)
- Add, Edit & Delete variant properties name and value [Taiga #6892](https://tree.taiga.io/project/penpot/us/6892)
- Retrieve variants [Taiga #6888](https://tree.taiga.io/project/penpot/us/6888)
- Retrieve variants with nested components [Taiga #10277](https://tree.taiga.io/project/penpot/us/10277)
- Create variants in bulk from existing components [Taiga #7926](https://tree.taiga.io/project/penpot/us/7926)
- Alternative ways of creating variants - Button Design Tab [Taiga #10316](https://tree.taiga.io/project/penpot/us/10316)
- Fix problem with component swapping panel [Taiga #12175](https://tree.taiga.io/project/penpot/issue/12175)
### :bug: Bugs fixed
- Display strokes information in inspect tab [Taiga #11154](https://tree.taiga.io/project/penpot/issue/11154)
- Fix problem with booleans selection [Taiga #11627](https://tree.taiga.io/project/penpot/issue/11627)
- Fix missing font when copy&paste a chunk of text [Taiga #11522](https://tree.taiga.io/project/penpot/issue/11522)
- Fix bad swap slot after two swaps [Taiga #11659](https://tree.taiga.io/project/penpot/issue/11659)
- Fix missing package for the `penpot_exporter` Docker image [GitHub #7205](https://github.com/penpot/penpot/issues/7025)
- Fix issue where multiple dropdown menus could be opened simultaneously on the dashboard page [Taiga #11500](https://tree.taiga.io/project/penpot/issue/11500)
- Fix font size/variant not updated when editing a text [Taiga #11552](https://tree.taiga.io/project/penpot/issue/11552)
- Fix issue where Alt + arrow keys shortcut interferes with letter-spacing when moving text layers [Taiga #11552](https://tree.taiga.io/project/penpot/issue/11771)
- Fix consistency issues on how font variants are visualized [Taiga #11499](https://tree.taiga.io/project/penpot/us/11499)
- Fix parsing rx and ry SVG values for rect radius [Taiga #11861](https://tree.taiga.io/project/penpot/issue/11861)
- Fix misleading affordance in saved versions [Taiga #11887](https://tree.taiga.io/project/penpot/issue/11887)
- Fix pasting RTF text crashes penpot [Taiga #11717](https://tree.taiga.io/project/penpot/issue/11717)
- Fix navigation arrows in Libraries & Templates carousel [Taiga #10609](https://tree.taiga.io/project/penpot/issue/10609)
- Fix applying tokens with zero value to size [Taiga #11618](https://tree.taiga.io/project/penpot/issue/11618)
- Fix typo [Taiga #11969](https://tree.taiga.io/project/penpot/issue/11969)
- Fix typo [Taiga #11970](https://tree.taiga.io/project/penpot/issue/11970)
- Fix typos [Taiga #11971](https://tree.taiga.io/project/penpot/issue/11971)
- Fix inconsistent naming for "Flatten" [Taiga #8371](https://tree.taiga.io/project/penpot/issue/8371)
- Layout item tokens should be unapplied when moving out of a layout [Taiga #11012](https://tree.taiga.io/project/penpot/issue/11012)
- Fix incorrect date displayed for support plan [Taiga #11986](https://tree.taiga.io/project/penpot/issue/11986)
- Fix can't import 'borderWidth' type token [#132](https://github.com/tokens-studio/penpot/issues/132)
- Fix moving elements up or down while pressing alt [Taiga Issue #11992](https://tree.taiga.io/project/penpot/issue/11992)
- Fix conflicting shortcuts (remove dec/inc line height and letter spacing) [Taiga #12102](https://tree.taiga.io/project/penpot/issue/12102)
- Fix conflicting shortcuts (remove text-align shortcuts) [Taiga #12047](https://tree.taiga.io/project/penpot/issue/12047)
- Fix export file with empty tokens library [Taiga #12137](https://tree.taiga.io/project/penpot/issue/12137)
- Fix context menu on spacing tokens [Taiga #12141](https://tree.taiga.io/project/penpot/issue/12141)
## 2.9.0
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
- Clarify message when inviting existing team members to make it more user-friendly and clear which invitations will be sent. [Taiga #11441](https://tree.taiga.io/project/penpot/issue/11441) by [@iprithvitharun](https://github.com/iprithvitharun)
- Update email change confirmation message for clarity and correct grammar. [GitHub #6786](https://github.com/penpot/penpot/issues/6786) by [@iprithvitharun](https://github.com/iprithvitharun)
### :sparkles: New features & Enhancements
- Add visual indicator for new comments in the workspace [Taiga #11328](https://tree.taiga.io/project/penpot/issue/11328)
- On components overrides, separate the content of the text from the rest of properties [Taiga #7434](https://tree.taiga.io/project/penpot/us/7434)
- Improve dashboard's sidebar [Taiga #10700](https://tree.taiga.io/project/penpot/us/10700)
- Change "Save color" button to primary button [Taiga #9410](https://tree.taiga.io/project/penpot/issue/9410)
- Support for exif rotated images [GitHub #6767](https://github.com/penpot/penpot/issues/6767)
- Display Blend Mode and Layer Opacity properties in the Inspect tab [Taiga #11283](https://tree.taiga.io/project/penpot/issue/11283)
- Provide CSS `mix-blend-mode` property in code editor when present on shape [Taiga #11282](https://tree.taiga.io/project/penpot/issue/11282)
- Add the option to import tokens in a .zip file. [Taiga #11378](https://tree.taiga.io/project/penpot/us/11378)
- New typography token type - font size token [Taiga #10938](https://tree.taiga.io/project/penpot/us/10938)
- Hide bounding box while editing visual effects [Taiga #11576](https://tree.taiga.io/project/penpot/issue/11576)
- Improved text layer resizing: Allow double-click on text bounding box to set auto-width/auto-height [Taiga #11577](https://tree.taiga.io/project/penpot/issue/11577)
- Improve text layer auto-resize: auto-width switches to auto-height on horizontal resize, and only switches to fixed on vertical resize [Taiga #11578](https://tree.taiga.io/project/penpot/issue/11578)
- Add the ability to show login dialog on profile settings [Github #6871](https://github.com/penpot/penpot/pull/6871)
- Improve the application of tokens with object specific tokens [Taiga #10209](https://tree.taiga.io/project/penpot/us/10209)
- Add info to apply-token event [Taiga #11710](https://tree.taiga.io/project/penpot/task/11710)
- Fix double click on set name input [Taiga #11747](https://tree.taiga.io/project/penpot/issue/11747)
### :bug: Bugs fixed
- Copying font size does not copy the unit [Taiga #11143](https://tree.taiga.io/project/penpot/issue/11143)
- Fix text-decoration line-through that displays a wrong property value [Taiga #11145](https://tree.taiga.io/project/penpot/issue/11145)
- Fix display error message on register form [Taiga #11444](https://tree.taiga.io/project/penpot/issue/11444)
- Fix toggle focus mode did not restore viewport and selection upon exit [GitHub #6280](https://github.com/penpot/penpot/issues/6820)
- Fix problem when creating a layout from an existing layout [Taiga #11554](https://tree.taiga.io/project/penpot/issue/11554)
- Fix title button from Title Case to Capitalize [Taiga #11476](https://tree.taiga.io/project/penpot/issue/11476)
- Fix touchpad swipe leading to navigating back/forth [GitHub #4246](https://github.com/penpot/penpot/issues/4246)
- Keep color data when copying from info tab into CSS [Taiga #11144](https://tree.taiga.io/project/penpot/issue/11144)
- Update HSL values to modern syntax as defined in W3C CSS Color Module Level 4 [Taiga #11144](https://tree.taiga.io/project/penpot/issue/11144)
- Fix main component receives focus and is selected when using 'Show Main Component' [Taiga #11402](https://tree.taiga.io/project/penpot/issue/11402)
- Fix UI theme selection from main menu [Taiga #11567](https://tree.taiga.io/project/penpot/issue/11567)
- Fix duplicating pages with mainInstance shapes nested inside groups [Taiga #10774](https://tree.taiga.io/project/penpot/issue/10774)
- Fix ESC key not closing Add/Manage Libraries modal [Taiga #11523](https://tree.taiga.io/project/penpot/issue/11523)
- Fix copying a shadow color from info tab [Taiga #11211](https://tree.taiga.io/project/penpot/issue/11211)
- Fix remove color button in the gradient editor [Taiga #11623](https://tree.taiga.io/project/penpot/issue/11623)
- Fix "Copy as SVG" generates different code from the Inspect panel [Taiga #11519](https://tree.taiga.io/project/penpot/issue/11519)
- Fix overriden tokens in text copies are not preserved [Taiga #11486](https://tree.taiga.io/project/penpot/issue/11486)
- Fix problem when changing between flex/grid layout [Taiga #11625](https://tree.taiga.io/project/penpot/issue/11625)
- Fix opacity on stroke gradients [Taiga #11646](https://tree.taiga.io/project/penpot/issue/11646)
- Fix change from gradient to solid color [Taiga #11648](https://tree.taiga.io/project/penpot/issue/11648)
- Fix the context menu always closes after any action [Taiga #11624](https://tree.taiga.io/project/penpot/issue/11624)
- Fix X & Y position do not sincronize with tokens [Taiga #11617](https://tree.taiga.io/project/penpot/issue/11617)
- Fix tooltip position after first time [Taiga #11688](https://tree.taiga.io/project/penpot/issue/11688)
- Fix inconsistent ordering of pinned projects on dashboard sidebar [Taiga #11674](https://tree.taiga.io/project/penpot/issue/11674)
- Fix export button width on inspect tab [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
- Fix stroke width token application [Taiga #11724](https://tree.taiga.io/project/penpot/issue/11724)
- Fix number token application on shape [Taiga #11331](https://tree.taiga.io/project/penpot/task/11331)
- Fix auto height is fixed in the HTML inspect tab for text elements [Taiga #11680](https://tree.taiga.io/project/penpot/task/11680)
## 2.8.1
### :bug: Bugs fixed
- Fix unexpected exception on processing old texts [Github #6889](https://github.com/penpot/penpot/pull/6889)
- Fix error on inspect tab when selecting multiple shapes [Taiga #11655](https://tree.taiga.io/project/penpot/issue/11655)
- Fix missing package for the penport_exporter Docker image [GitHub #7205](https://github.com/penpot/penpot/issues/7025)
## 2.8.0
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@@ -8,18 +384,19 @@
**Penpot Library** **Penpot Library**
The initial prototype is completly reworked for provide a more consistent API The initial prototype is completly reworked to provide a more consistent API
and to have proper validation and params decoding. All the details can be found and to have proper validation and params decoding. All the details can be found
on [its own changelog](library/CHANGES.md) on [its own changelog](library/CHANGES.md)
**Penpot migrate from Redis to Valkey** **Penpot migrate from Redis to Valkey**
As [Valkey](https://valkey.io/) is an opne-souce fork of [Redis](https://redis.io/) As [Valkey](https://valkey.io/) is an opne-souce fork of [Redis](https://redis.io/)
version 7.2.4, this version of Penpot will be compatible with Redis but may diverge version 7.2.4, this version of Penpot will be compatible with Redis but may diverge
in future versions. Therefore, **migration from Redis to ValKey is recommended for all in future versions. Therefore, **migration from Redis to ValKey is recommended for all
on-premises instances** that want to keep up to date. on-premises instances** that want to keep up to date.
### :heart: Community contributions (Thank you!) ### :heart: Community contributions (Thank you!)
- Add Serbian language [GitHub #5002](https://github.com/penpot/penpot/issues/5002) by [crnobog69](https://github.com/crnobog69) - Add Serbian language [GitHub #5002](https://github.com/penpot/penpot/issues/5002) by [crnobog69](https://github.com/crnobog69)
### :sparkles: New features & Enhancements ### :sparkles: New features & Enhancements
@@ -39,6 +416,7 @@ on-premises instances** that want to keep up to date.
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792) - Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
- Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220) - Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220)
- Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144) - Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144)
- Fix problem when double click on hidden shapes [Taiga #11314](https://tree.taiga.io/project/penpot/issue/11314)
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -57,8 +435,12 @@ on-premises instances** that want to keep up to date.
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394) - Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178) - Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054) - Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337}(https://tree.taiga.io/project/penpot/issue/11337) - Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337](https://tree.taiga.io/project/penpot/issue/11337)
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061) - Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
- Fix entering long project name [Taiga #11417](https://tree.taiga.io/project/penpot/issue/11417)
- Fix slow color picker [Taiga #11019](https://tree.taiga.io/project/penpot/issue/11019)
- Fix tooltip position after click [Taiga #11405](https://tree.taiga.io/project/penpot/issue/11405)
- Fix incorrect media translation on paste text with fill images [Github #6845](https://github.com/penpot/penpot/pull/6845)
## 2.7.2 ## 2.7.2
@@ -70,7 +452,6 @@ on-premises instances** that want to keep up to date.
- Fix copy in error message [GitHub #6615](https://github.com/penpot/penpot/pull/6615) - Fix copy in error message [GitHub #6615](https://github.com/penpot/penpot/pull/6615)
- Fix url on invitation link [Taiga #11284](https://tree.taiga.io/project/penpot/issue/11284) - Fix url on invitation link [Taiga #11284](https://tree.taiga.io/project/penpot/issue/11284)
## 2.7.1 ## 2.7.1
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -78,7 +459,6 @@ on-premises instances** that want to keep up to date.
- Fix incorrect handling of strokes with images on importing files - Fix incorrect handling of strokes with images on importing files
- Fix tokens disappearing after manual additions [Taiga #11063](https://tree.taiga.io/project/penpot/issue/11063) - Fix tokens disappearing after manual additions [Taiga #11063](https://tree.taiga.io/project/penpot/issue/11063)
## 2.7.0 ## 2.7.0
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@@ -210,7 +590,6 @@ on-premises instances** that want to keep up to date.
- Add character limitation to asset inputs [Taiga #10669](https://tree.taiga.io/project/penpot/issue/10669) - Add character limitation to asset inputs [Taiga #10669](https://tree.taiga.io/project/penpot/issue/10669)
- Fix Storybook link 'list of all available icons' wrong path [Taiga #10705](https://tree.taiga.io/project/penpot/issue/10705) - Fix Storybook link 'list of all available icons' wrong path [Taiga #10705](https://tree.taiga.io/project/penpot/issue/10705)
## 2.5.4 ## 2.5.4
### :heart: Community contributions (Thank you!) ### :heart: Community contributions (Thank you!)
@@ -255,7 +634,7 @@ on-premises instances** that want to keep up to date.
### :boom: Breaking changes & Deprecations ### :boom: Breaking changes & Deprecations
Although this is not a breaking change, we believe its important to highlight it in this Although this is not a breaking change, we believe it's important to highlight it in this
section: section:
This release includes a fix for an internal bug in Penpot that caused incorrect handling This release includes a fix for an internal bug in Penpot that caused incorrect handling
@@ -263,9 +642,9 @@ of media assets (e.g., fill images). The issue has been resolved since version 2
no new incorrect references will be generated. However, existing files may still contain no new incorrect references will be generated. However, existing files may still contain
incorrect references. incorrect references.
To address this, weve provided a script to correct these references in existing files. To address this, we've provided a script to correct these references in existing files.
While having incorrect references generally doesnt result in visible issues, there are While having incorrect references generally doesn't result in visible issues, there are
rare cases where it can cause problems. For example, if a component library (containing rare cases where it can cause problems. For example, if a component library (containing
images) is deleted, and that library is being used in other files, running the FileGC task images) is deleted, and that library is being used in other files, running the FileGC task
(responsible for freeing up space and performing logical deletions) could leave those (responsible for freeing up space and performing logical deletions) could leave those
@@ -340,7 +719,6 @@ is a number of cores)
- Fix missing methods reference on API Docs - Fix missing methods reference on API Docs
- Fix memory usage issue on file-gc asynchronous task (related to snapshots feature) - Fix memory usage issue on file-gc asynchronous task (related to snapshots feature)
## 2.4.1 ## 2.4.1
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -348,7 +726,6 @@ is a number of cores)
- Fix error when importing files with touched components [Taiga #9625](https://tree.taiga.io/project/penpot/issue/9625) - Fix error when importing files with touched components [Taiga #9625](https://tree.taiga.io/project/penpot/issue/9625)
- Fix problem when changing color libraries [Plugins #184](https://github.com/penpot/penpot-plugins/issues/184) - Fix problem when changing color libraries [Plugins #184](https://github.com/penpot/penpot-plugins/issues/184)
## 2.4.0 ## 2.4.0
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@@ -402,7 +779,6 @@ is a number of cores)
- Add initial documentation for Kubernetes - Add initial documentation for Kubernetes
## 2.3.1 ## 2.3.1
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -410,7 +786,6 @@ is a number of cores)
- Fix unexpected issue on interaction between plugins sandbox and - Fix unexpected issue on interaction between plugins sandbox and
internal impl of promise internal impl of promise
## 2.3.0 ## 2.3.0
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@@ -436,7 +811,6 @@ is a number of cores)
You can enable it with the `enable-feature-text-editor-v2` configuration flag. You can enable it with the `enable-feature-text-editor-v2` configuration flag.
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix problem with constraints buttons [Taiga #8465](https://tree.taiga.io/project/penpot/issue/8465) - Fix problem with constraints buttons [Taiga #8465](https://tree.taiga.io/project/penpot/issue/8465)
@@ -476,8 +850,8 @@ is a number of cores)
### :boom: Breaking changes & Deprecations ### :boom: Breaking changes & Deprecations
- Removed "merge assets" option when exporting ".svg + .json" files. After the components changes the option wasn't - Removed "merge assets" option when exporting ".svg + .json" files. After the components changes the option wasn't
working properly and we're planning to change the format soon. We think it's better to deprecate the option for the working properly and we're planning to change the format soon. We think it's better to deprecate the option for the
time being. time being.
### :heart: Community contributions (Thank you!) ### :heart: Community contributions (Thank you!)
@@ -493,7 +867,7 @@ time being.
freeing up space in the database. It can be enabled with the freeing up space in the database. It can be enabled with the
`enable-enable-tiered-file-data-storage` flag. `enable-enable-tiered-file-data-storage` flag.
*(On-Premise feature, EXPERIMENTAL).* _(On-Premise feature, EXPERIMENTAL)._
- **JSON Interoperability for HTTP API** [Taiga #8372](https://tree.taiga.io/project/penpot/us/8372) - **JSON Interoperability for HTTP API** [Taiga #8372](https://tree.taiga.io/project/penpot/us/8372)
@@ -536,7 +910,7 @@ time being.
- **Design System** - **Design System**
We implemented and subbed in new components from our Design System: `loader*` ([Taiga #8355](https://tree.taiga.io/project/penpot/task/8355)) and `tab-switcher*` ([Taiga #8518](https://tree.taiga.io/project/penpot/task/8518)). We implemented and subbed in new components from our Design System: `loader*` ([Taiga #8355](https://tree.taiga.io/project/penpot/task/8355)) and `tab-switcher*` ([Taiga #8518](https://tree.taiga.io/project/penpot/task/8518)).
- **Storybook** [Taiga #6329](https://tree.taiga.io/project/penpot/us/6329) - **Storybook** [Taiga #6329](https://tree.taiga.io/project/penpot/us/6329)
@@ -591,11 +965,11 @@ time being.
### :sparkles: New features ### :sparkles: New features
- Consolidate templates new order and naming [Taiga #8392](https://tree.taiga.io/project/penpot/task/8392) - Consolidate templates new order and naming [Taiga #8392](https://tree.taiga.io/project/penpot/task/8392)
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix the search label in translations [Taiga #8402](https://tree.taiga.io/project/penpot/issue/8402) - Fix the "search" label in translations [Taiga #8402](https://tree.taiga.io/project/penpot/issue/8402)
- Fix pencil loader [Taiga #8348](https://tree.taiga.io/project/penpot/issue/8348) - Fix pencil loader [Taiga #8348](https://tree.taiga.io/project/penpot/issue/8348)
- Fix several issues on the OIDC. - Fix several issues on the OIDC.
- Fix regression on the `email-verification` flag [Taiga #8398](https://tree.taiga.io/project/penpot/issue/8398) - Fix regression on the `email-verification` flag [Taiga #8398](https://tree.taiga.io/project/penpot/issue/8398)
@@ -675,22 +1049,21 @@ time being.
- Fix color palette sorting [Taiga #7458](https://tree.taiga.io/project/penpot/issue/7458) - Fix color palette sorting [Taiga #7458](https://tree.taiga.io/project/penpot/issue/7458)
- Fix style scoping problem with imported SVG [Taiga #7671](https://tree.taiga.io/project/penpot/issue/7671) - Fix style scoping problem with imported SVG [Taiga #7671](https://tree.taiga.io/project/penpot/issue/7671)
## 2.0.1 ## 2.0.1
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix different issues related to components v2 migrations including [Github #4443](https://github.com/penpot/penpot/issues/4443) - Fix different issues related to components v2 migrations including [Github #4443](https://github.com/penpot/penpot/issues/4443)
## 2.0.0 - I Just Can't Get Enough ## 2.0.0 - I Just Can't Get Enough
### :rocket: Epics and highlights ### :rocket: Epics and highlights
- Grid CSS layout [Taiga #4915](https://tree.taiga.io/project/penpot/epic/4915) - Grid CSS layout [Taiga #4915](https://tree.taiga.io/project/penpot/epic/4915)
- UI redesign [Taiga #4958](https://tree.taiga.io/project/penpot/epic/4958) - UI redesign [Taiga #4958](https://tree.taiga.io/project/penpot/epic/4958)
- New components System [Taiga #2662](https://tree.taiga.io/project/penpot/epic/2662) - New components System [Taiga #2662](https://tree.taiga.io/project/penpot/epic/2662)
- Swap components [Taiga #1331](https://tree.taiga.io/project/penpot/us/1331) - Swap components [Taiga #1331](https://tree.taiga.io/project/penpot/us/1331)
- Images as fill [Taiga #2983](https://tree.taiga.io/project/penpot/us/2983) - Images as fill [Taiga #2983](https://tree.taiga.io/project/penpot/us/2983)
- HTML code generation [Taiga #5277](https://tree.taiga.io/project/penpot/us/5277) - HTML code generation [Taiga #5277](https://tree.taiga.io/project/penpot/us/5277)
- Light and dark themes [Taiga #2287](https://tree.taiga.io/project/penpot/us/2287) - Light and dark themes [Taiga #2287](https://tree.taiga.io/project/penpot/us/2287)
@@ -699,9 +1072,9 @@ time being.
- New strokes default to inside border [Taiga #6847](https://tree.taiga.io/project/penpot/issue/6847) - New strokes default to inside border [Taiga #6847](https://tree.taiga.io/project/penpot/issue/6847)
- Change default z ordering on layers in flex layout. The previous behavior was inconsistent with how HTML works and we changed it to be more consistent. Previous layers that overlapped could be hidden, the fastest way to fix this is changing the z-index property but a better way is to change the order of your layers. - Change default z ordering on layers in flex layout. The previous behavior was inconsistent with how HTML works and we changed it to be more consistent. Previous layers that overlapped could be hidden, the fastest way to fix this is changing the z-index property but a better way is to change the order of your layers.
### :heart: Community contributions (Thank you!) ### :heart: Community contributions (Thank you!)
- New Hausa, Yoruba and Igbo translations and update translation files (by All For Tech Empowerment Foundation) [Taiga #6950](https://tree.taiga.io/project/penpot/us/6950), [Taiga #6534](https://tree.taiga.io/project/penpot/us/6534)
- New Hausa, Yoruba and Igbo translations and update translation files (by All For Tech Empowerment Foundation) [Taiga #6950](https://tree.taiga.io/project/penpot/us/6950), [Taiga #6534](https://tree.taiga.io/project/penpot/us/6534)
- Hide bounding-box when editing shape (by @VasilevsVV) [#3930](https://github.com/penpot/penpot/pull/3930) - Hide bounding-box when editing shape (by @VasilevsVV) [#3930](https://github.com/penpot/penpot/pull/3930)
- CTRL + "+" to zoom into canvas instead of browser (by @audriu) [#3848](https://github.com/penpot/penpot/pull/3848) - CTRL + "+" to zoom into canvas instead of browser (by @audriu) [#3848](https://github.com/penpot/penpot/pull/3848)
- Add dev deps.edn in the project root (by @PEZ) [#3794](https://github.com/penpot/penpot/pull/3794) - Add dev deps.edn in the project root (by @PEZ) [#3794](https://github.com/penpot/penpot/pull/3794)
@@ -710,6 +1083,7 @@ time being.
- Typo (by StephanEggermont) [#157](https://github.com/penpot/penpot-docs/pull/157) - Typo (by StephanEggermont) [#157](https://github.com/penpot/penpot-docs/pull/157)
### :sparkles: New features ### :sparkles: New features
- Send comments with Ctrl+Enter / Cmd + Enter [Taiga #6085](https://tree.taiga.io/project/penpot/issue/6085) - Send comments with Ctrl+Enter / Cmd + Enter [Taiga #6085](https://tree.taiga.io/project/penpot/issue/6085)
- Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484) - Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484)
- Stroke default position [Taiga #6847](https://tree.taiga.io/project/penpot/issue/6847) - Stroke default position [Taiga #6847](https://tree.taiga.io/project/penpot/issue/6847)
@@ -777,6 +1151,7 @@ time being.
- [REDESIGN] Onboarding slides [Taiga #6678](https://tree.taiga.io/project/penpot/us/6678) - [REDESIGN] Onboarding slides [Taiga #6678](https://tree.taiga.io/project/penpot/us/6678)
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix pixelated thumbnails [Github #3681](https://github.com/penpot/penpot/issues/3681), [Github #3661](https://github.com/penpot/penpot/issues/3661) - Fix pixelated thumbnails [Github #3681](https://github.com/penpot/penpot/issues/3681), [Github #3661](https://github.com/penpot/penpot/issues/3661)
- Fix problem with not applying colors to boards [Github #3941](https://github.com/penpot/penpot/issues/3941) - Fix problem with not applying colors to boards [Github #3941](https://github.com/penpot/penpot/issues/3941)
- Fix problem with path editor undoing changes [Github #3998](https://github.com/penpot/penpot/issues/3998) - Fix problem with path editor undoing changes [Github #3998](https://github.com/penpot/penpot/issues/3998)
@@ -785,7 +1160,7 @@ time being.
- Selecting from Color Palette does not work for board when there is no existing fill [Taiga #6464](https://tree.taiga.io/project/penpot/issue/6464) - Selecting from Color Palette does not work for board when there is no existing fill [Taiga #6464](https://tree.taiga.io/project/penpot/issue/6464)
- Color thumbnails are consistently rounded in the inspect code mode [Taiga #5886](https://tree.taiga.io/project/penpot/issue/5886) - Color thumbnails are consistently rounded in the inspect code mode [Taiga #5886](https://tree.taiga.io/project/penpot/issue/5886)
- Adding vector path points before first point of existing open path not working [Taiga #6593](https://tree.taiga.io/project/penpot/issue/6593) - Adding vector path points before first point of existing open path not working [Taiga #6593](https://tree.taiga.io/project/penpot/issue/6593)
- Some image formats include the extension when importing [Taiga #5485](https://tree.taiga.io/project/penpot/issue/5485) - Some image formats include the extension when importing [Taiga #5485](https://tree.taiga.io/project/penpot/issue/5485)
- Gradient color tool doesn't work properly with flipped items [Taiga #6485](https://tree.taiga.io/project/penpot/issue/6485) - Gradient color tool doesn't work properly with flipped items [Taiga #6485](https://tree.taiga.io/project/penpot/issue/6485)
- [TEXT] Align options are not shown when several text are selected [Taiga #5948](https://tree.taiga.io/project/penpot/issue/5948) - [TEXT] Align options are not shown when several text are selected [Taiga #5948](https://tree.taiga.io/project/penpot/issue/5948)
- [VIEW MODE] Comments not working properly on multiple pages [Taiga #6281](https://tree.taiga.io/project/penpot/issue/6281) - [VIEW MODE] Comments not working properly on multiple pages [Taiga #6281](https://tree.taiga.io/project/penpot/issue/6281)
@@ -829,7 +1204,7 @@ time being.
### :sparkles: New features ### :sparkles: New features
- Improve selected colors [Taiga #5805]( https://tree.taiga.io/project/penpot/us/5805) - Improve selected colors [Taiga #5805](https://tree.taiga.io/project/penpot/us/5805)
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -864,7 +1239,6 @@ time being.
- Fix deleted pages comments shown in right sidebar [Taiga #5648](https://tree.taiga.io/project/penpot/us/5648) - Fix deleted pages comments shown in right sidebar [Taiga #5648](https://tree.taiga.io/project/penpot/us/5648)
- Fix tooltip on toggle visibility and toggle lock buttons [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141) - Fix tooltip on toggle visibility and toggle lock buttons [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141)
## 1.19.1 ## 1.19.1
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -978,7 +1352,6 @@ time being.
- Update google fonts catalog (at 2023/07/06) [Taiga #5592](https://tree.taiga.io/project/penpot/issue/5592) - Update google fonts catalog (at 2023/07/06) [Taiga #5592](https://tree.taiga.io/project/penpot/issue/5592)
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)
- Update Typography palette order (by @akshay-gupta7) [Github #3156](https://github.com/penpot/penpot/pull/3156) - Update Typography palette order (by @akshay-gupta7) [Github #3156](https://github.com/penpot/penpot/pull/3156)
@@ -1132,12 +1505,14 @@ time being.
- Fix problem with opacity in imported SVG's [Taiga #4923](https://tree.taiga.io/project/penpot/issue/4923) - Fix problem with opacity in imported SVG's [Taiga #4923](https://tree.taiga.io/project/penpot/issue/4923)
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)
- To @ondrejkonec: for contributing to the code with: - To @ondrejkonec: for contributing to the code with:
- Refactor CSS variables [Github #2948](https://github.com/penpot/penpot/pull/2948) - Refactor CSS variables [Github #2948](https://github.com/penpot/penpot/pull/2948)
## 1.17.3 ## 1.17.3
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix copy and paste very nested inside itself [Taiga #4848](https://tree.taiga.io/project/penpot/issue/4848) - Fix copy and paste very nested inside itself [Taiga #4848](https://tree.taiga.io/project/penpot/issue/4848)
- Fix custom fonts not rendered correctly [Taiga #4874](https://tree.taiga.io/project/penpot/issue/4874) - Fix custom fonts not rendered correctly [Taiga #4874](https://tree.taiga.io/project/penpot/issue/4874)
- Fix problem with shadows and blur on multiple selection - Fix problem with shadows and blur on multiple selection
@@ -1170,6 +1545,7 @@ time being.
## 1.17.1 ## 1.17.1
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix components groups items show the component name in list mode [Taiga #4770](https://tree.taiga.io/project/penpot/issue/4770) - Fix components groups items show the component name in list mode [Taiga #4770](https://tree.taiga.io/project/penpot/issue/4770)
- Fix typing CMD+Z on MacOS turns the cursor into a Zoom cursor [Taiga #4778](https://tree.taiga.io/project/penpot/issue/4778) - Fix typing CMD+Z on MacOS turns the cursor into a Zoom cursor [Taiga #4778](https://tree.taiga.io/project/penpot/issue/4778)
- Fix white space on small screens [Taiga #4774](https://tree.taiga.io/project/penpot/issue/4774) - Fix white space on small screens [Taiga #4774](https://tree.taiga.io/project/penpot/issue/4774)
@@ -1284,7 +1660,7 @@ time being.
### :boom: Breaking changes & Deprecations ### :boom: Breaking changes & Deprecations
- Removed the support for v2 internal file data blob format. This - Removed the support for v2 internal file data blob format. This
version has never been documented nor set as default value so version has never been documented nor set as default value so
technically this is not a breaking change because we are removing technically this is not a breaking change because we are removing
a "private API". a "private API".
@@ -1389,7 +1765,6 @@ time being.
- Fix when ungrouping, the items previously grouped should ALWAYS remain selected [Taiga #4064](https://tree.taiga.io/project/penpot/issue/4064) - Fix when ungrouping, the items previously grouped should ALWAYS remain selected [Taiga #4064](https://tree.taiga.io/project/penpot/issue/4064)
- Change shortcut for "Clear undo" [#2219](https://github.com/penpot/penpot/issues/2219) - Change shortcut for "Clear undo" [#2219](https://github.com/penpot/penpot/issues/2219)
## 1.15.2-beta ## 1.15.2-beta
### :bug: Bugs fixed ### :bug: Bugs fixed
@@ -1473,6 +1848,7 @@ time being.
- Fix bringing complete file data when launching the export dialog [Taiga #4006](https://tree.taiga.io/project/penpot/issue/4006) - Fix bringing complete file data when launching the export dialog [Taiga #4006](https://tree.taiga.io/project/penpot/issue/4006)
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)
## 1.14.2-beta ## 1.14.2-beta
@@ -1513,10 +1889,10 @@ time being.
- Prototype connection should be under the rules [Taiga #3384](https://tree.taiga.io/project/penpot/issue/3384) - Prototype connection should be under the rules [Taiga #3384](https://tree.taiga.io/project/penpot/issue/3384)
- Fix problem with empty text boxes events [Taiga #3627](https://tree.taiga.io/project/penpot/issue/3627) - Fix problem with empty text boxes events [Taiga #3627](https://tree.taiga.io/project/penpot/issue/3627)
## 1.13.5-beta ## 1.13.5-beta
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix orientation artboard preset not working with differently sized artboards [Taiga #3548](https://tree.taiga.io/project/penpot/issue/3548) - Fix orientation artboard preset not working with differently sized artboards [Taiga #3548](https://tree.taiga.io/project/penpot/issue/3548)
- Fix background on export arboards [Taiga #1991](https://tree.taiga.io/project/penpot/issue/1991) - Fix background on export arboards [Taiga #1991](https://tree.taiga.io/project/penpot/issue/1991)
@@ -1660,6 +2036,7 @@ time being.
- Fix problem when resizing a group with texts with auto-width/height [#3171](https://tree.taiga.io/project/penpot/issue/3171) - Fix problem when resizing a group with texts with auto-width/height [#3171](https://tree.taiga.io/project/penpot/issue/3171)
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)
## 1.12.4-beta ## 1.12.4-beta
@@ -1677,7 +2054,7 @@ time being.
### :bug: Bugs fixed ### :bug: Bugs fixed
- Fix issue with shift+select to deselect shapes [Taiga #3154](https://tree.taiga.io/project/penpot/issue/3154) - Fix issue with shift+select to deselect shapes [Taiga #3154](https://tree.taiga.io/project/penpot/issue/3154)
- Fix issue with drag-select shapes [Taiga #3165](https://tree.taiga.io/project/penpot/issue/3165) - Fix issue with drag-select shapes [Taiga #3165](https://tree.taiga.io/project/penpot/issue/3165)
- Fix issue on password persistence after registration process on private instances - Fix issue on password persistence after registration process on private instances
## 1.12.2-beta ## 1.12.2-beta
@@ -1695,7 +2072,6 @@ time being.
- Fix length of names in sidebar [Taiga #2962](https://tree.taiga.io/project/penpot/issue/2962) - Fix length of names in sidebar [Taiga #2962](https://tree.taiga.io/project/penpot/issue/2962)
- Fix issues on loki integration - Fix issues on loki integration
## 1.12.0-beta ## 1.12.0-beta
### :boom: Breaking changes ### :boom: Breaking changes
+3 -6
View File
@@ -77,17 +77,14 @@ Provide your team or organization with a completely owned collaborative design t
### Integrations ### ### Integrations ###
Penpot offers integration into the development toolchain, thanks to its support for webhooks and an API accessible through access tokens. Penpot offers integration into the development toolchain, thanks to its support for webhooks and an API accessible through access tokens.
### Whats great for design ### ### Building Design Systems: design tokens, components and variants ###
With Penpot you can design libraries to share and reuse; turn design elements into components and tokens to allow reusability and scalability; and build realistic user flows and interactions. Penpot brings design systems to code-minded teams: a single source of truth with native Design Tokens, Components, and Variants for scalable, reusable, and consistent UI across projects and platforms.
### Design Tokens ###
With Penpots standardized [design tokens](https://penpot.dev/collaboration/design-tokens) format, you can easily reuse and sync tokens across different platforms, workflows, and disciplines.
<br /> <br />
<p align="center"> <p align="center">
<img src="https://img.plasmic.app/img-optimizer/v1/img?src=https%3A%2F%2Fimg.plasmic.app%2Fimg-optimizer%2Fv1%2Fimg%2F9dd677c36afb477e9666ccd1d3f009ad.png" alt="Open Source" style="width: 65%;"> <img src="https://github.com/user-attachments/assets/cce75ad6-f783-473f-8803-da9eb8255fef">
</p> </p>
<br /> <br />
+22 -19
View File
@@ -3,10 +3,10 @@
:deps :deps
{penpot/common {:local/root "../common"} {penpot/common {:local/root "../common"}
org.clojure/clojure {:mvn/version "1.12.1"} org.clojure/clojure {:mvn/version "1.12.4"}
org.clojure/tools.namespace {:mvn/version "1.5.0"} org.clojure/tools.namespace {:mvn/version "1.5.0"}
com.github.luben/zstd-jni {:mvn/version "1.5.7-3"} com.github.luben/zstd-jni {:mvn/version "1.5.7-4"}
io.prometheus/simpleclient {:mvn/version "0.16.0"} io.prometheus/simpleclient {:mvn/version "0.16.0"}
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"}
@@ -17,7 +17,7 @@
io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"} io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"}
io.lettuce/lettuce-core {:mvn/version "6.7.0.RELEASE"} io.lettuce/lettuce-core {:mvn/version "6.8.1.RELEASE"}
;; Minimal dependencies required by lettuce, we need to include them ;; Minimal dependencies required by lettuce, we need to include them
;; explicitly because clojure dependency management does not support ;; explicitly because clojure dependency management does not support
;; yet the BOM format. ;; yet the BOM format.
@@ -28,29 +28,30 @@
com.google.guava/guava {:mvn/version "33.4.8-jre"} com.google.guava/guava {:mvn/version "33.4.8-jre"}
funcool/yetti funcool/yetti
{:git/tag "v11.4" {:git/tag "v11.9"
:git/sha "ce50d42" :git/sha "5fad7a9"
:git/url "https://github.com/funcool/yetti.git" :git/url "https://github.com/funcool/yetti.git"
:exclusions [org.slf4j/slf4j-api]} :exclusions [org.slf4j/slf4j-api]}
com.github.seancorfield/next.jdbc com.github.seancorfield/next.jdbc
{:mvn/version "1.3.1002"} {:mvn/version "1.3.1070"}
metosin/reitit-core {:mvn/version "0.9.1"} metosin/reitit-core {:mvn/version "0.9.1"}
nrepl/nrepl {:mvn/version "1.3.1"} nrepl/nrepl {:mvn/version "1.4.0"}
org.postgresql/postgresql {:mvn/version "42.7.6"} org.postgresql/postgresql {:mvn/version "42.7.9"}
org.xerial/sqlite-jdbc {:mvn/version "3.49.1.0"} org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
com.zaxxer/HikariCP {:mvn/version "6.3.0"} com.zaxxer/HikariCP {:mvn/version "7.0.2"}
io.whitfin/siphash {:mvn/version "2.0.0"} io.whitfin/siphash {:mvn/version "2.0.0"}
buddy/buddy-hashers {:mvn/version "2.0.167"} buddy/buddy-hashers {:mvn/version "2.0.167"}
buddy/buddy-sign {:mvn/version "3.6.1-359"} buddy/buddy-sign {:mvn/version "3.6.1-359"}
com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.0"} com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.3"}
org.jsoup/jsoup {:mvn/version "1.20.1"} org.jsoup/jsoup {:mvn/version "1.21.2"}
org.im4java/im4java org.im4java/im4java
{:git/tag "1.4.0-penpot-2" {:git/tag "1.4.0-penpot-2"
:git/sha "e2b3e16" :git/sha "e2b3e16"
@@ -60,12 +61,12 @@
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"} org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"}
dawran6/emoji {:mvn/version "0.1.5"} dawran6/emoji {:mvn/version "0.2.0"}
markdown-clj/markdown-clj {:mvn/version "1.12.3"} markdown-clj/markdown-clj {:mvn/version "1.12.4"}
;; Pretty Print specs ;; Pretty Print specs
pretty-spec/pretty-spec {:mvn/version "0.1.4"} pretty-spec/pretty-spec {:mvn/version "0.1.4"}
software.amazon.awssdk/s3 {:mvn/version "2.31.55"}} software.amazon.awssdk/s3 {:mvn/version "2.41.21"}}
:paths ["src" "resources" "target/classes"] :paths ["src" "resources" "target/classes"]
:aliases :aliases
@@ -80,12 +81,14 @@
:build :build
{:extra-deps {:extra-deps
{io.github.clojure/tools.build {:git/tag "v0.10.9" :git/sha "e405aac"}} {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build} :ns-default build}
:test :test
{:main-opts ["-m" "kaocha.runner"] {:main-opts ["-m" "kaocha.runner"]
:jvm-opts ["-Dlog4j2.configurationFile=log4j2-devenv-repl.xml"] :jvm-opts ["-Dlog4j2.configurationFile=log4j2-devenv-repl.xml"
"--sun-misc-unsafe-memory-access=allow"
"--enable-native-access=ALL-UNNAMED"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}} :extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
:outdated :outdated
@@ -94,8 +97,8 @@
:jmx-remote :jmx-remote
{:jvm-opts ["-Dcom.sun.management.jmxremote" {:jvm-opts ["-Dcom.sun.management.jmxremote"
"-Dcom.sun.management.jmxremote.port=9090" "-Dcom.sun.management.jmxremote.port=9000"
"-Dcom.sun.management.jmxremote.rmi.port=9090" "-Dcom.sun.management.jmxremote.rmi.port=9000"
"-Dcom.sun.management.jmxremote.local.only=false" "-Dcom.sun.management.jmxremote.local.only=false"
"-Dcom.sun.management.jmxremote.authenticate=false" "-Dcom.sun.management.jmxremote.authenticate=false"
"-Dcom.sun.management.jmxremote.ssl=false" "-Dcom.sun.management.jmxremote.ssl=false"
+8 -4
View File
@@ -6,12 +6,14 @@
(ns user (ns user
(:require (:require
[app.binfile.common :as bfc]
[app.common.data :as d] [app.common.data :as d]
[app.common.debug :as debug] [app.common.debug :as debug]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.fressian :as fres] [app.common.fressian :as fres]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.json :as json]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.perf :as perf] [app.common.perf :as perf]
[app.common.pprint :as pp] [app.common.pprint :as pp]
@@ -19,20 +21,22 @@
[app.common.schema.desc-js-like :as smdj] [app.common.schema.desc-js-like :as smdj]
[app.common.schema.desc-native :as smdn] [app.common.schema.desc-native :as smdn]
[app.common.schema.generators :as sg] [app.common.schema.generators :as sg]
[app.common.schema.openapi :as oapi]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.json :as json] [app.common.time :as ct]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.common.uri :as u]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.main :as main] [app.main :as main]
[app.srepl.helpers :as srepl.helpers] [app.srepl.helpers :as h]
[app.srepl.main :as srepl] [app.srepl.main :refer :all]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.time :as dt]
[clj-async-profiler.core :as prof] [clj-async-profiler.core :as prof]
[clojure.contrib.humanize :as hum] [clojure.contrib.humanize :as hum]
[clojure.datafy :refer [datafy]]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.pprint :refer [pprint print-table]] [clojure.pprint :refer [pprint print-table]]
[clojure.repl :refer :all] [clojure.repl :refer :all]
@@ -193,7 +193,7 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div <div
style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;"> style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">
Click to the link below to confirm the change:</div> Click the link below to confirm the change.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -217,8 +217,7 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div <div
style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;"> style="font-family:Source Sans Pro, sans-serif;font-size:16px;line-height:150%;text-align:left;color:#000000;">
If you received this email by mistake, please consider changing your password for security If you did not request this change, consider changing your password for security reasons.</div>
reasons.</div>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -2,12 +2,11 @@ Hello {{name|abbreviate:25}}!
We received a request to change your current email to {{ pending-email }}. We received a request to change your current email to {{ pending-email }}.
Click to the link below to confirm the change: Click the link below to confirm the change.
{{ public-uri }}/#/auth/verify-token?token={{token}} {{ public-uri }}/#/auth/verify-token?token={{token}}
If you received this email by mistake, please consider changing your password If you did not request this change, consider changing your password for security reasons.
for security reasons.
Enjoy! Enjoy!
The Penpot team. The Penpot team.
+28 -25
View File
@@ -8,38 +8,41 @@
<body> <body>
<p> <p>
<strong>Feedback from:</strong><br /> <strong>Feedback from:</strong><br />
{% if profile %} <span>
<span> <span>Name: </span>
<span>Name: </span> <span><code>{{profile.fullname|abbreviate:25}}</code></span>
<span><code>{{profile.fullname|abbreviate:25}}</code></span> </span>
</span> <br />
<br /> <span>
<span>Email: </span>
<span> <span>{{profile.email}}</span>
<span>Email: </span> </span>
<span>{{profile.email}}</span> <br />
</span> <span>
<br /> <span>ID: </span>
<span><code>{{profile.id}}</code></span>
<span> </span>
<span>ID: </span>
<span><code>{{profile.id}}</code></span>
</span>
{% else %}
<span>
<span>Email: </span>
<span>{{profile.email}}</span>
</span>
{% endif %}
</p> </p>
<p> <p>
<strong>Subject:</strong><br /> <strong>Subject:</strong><br />
<span>{{subject|abbreviate:300}}</span> <span>{{feedback-subject|abbreviate:300}}</span>
</p> </p>
<p>
<strong>Type:</strong><br />
<span>{{feedback-type|abbreviate:300}}</span>
</p>
{% if feedback-error-href %}
<p>
<strong>Error HREF:</strong><br />
<span>{{feedback-error-href|abbreviate:500}}</span>
</p>
{% endif %}
<p> <p>
<strong>Message:</strong><br /> <strong>Message:</strong><br />
{{content|linebreaks-br|safe}} {{feedback-content|linebreaks-br}}
</p> </p>
</body> </body>
</html> </html>
+1 -1
View File
@@ -1 +1 @@
[PENPOT FEEDBACK]: {{subject}} [PENPOT FEEDBACK]: {{feedback-subject}}
+9 -7
View File
@@ -1,9 +1,11 @@
{% if profile %} From: {{profile.fullname}} <{{profile.email}}> / {{profile.id}}
Feedback profile: {{profile.fullname}} <{{profile.email}}> / {{profile.id}} Subject: {{feedback-subject}}
{% else %} Type: {{feedback-type}}
Feedback from: {{email}}
{% endif %}
Subject: {{subject}} {% if feedback-error-href %}
HREF: {{feedback-error-href}}
{% endif -%}
{{content}} Message:
{{feedback-content}}
@@ -240,4 +240,4 @@
</div> </div>
</body> </body>
</html> </html>
@@ -1 +1 @@
Invitation to join {{team}} {{invited-by|abbreviate:25}} has invited you to join the team “{{ team|abbreviate:25 }}
+4 -4
View File
@@ -1,6 +1,9 @@
[{:id "tokens-starter-kit" [{:id "tokens-starter-kit"
:name "Design tokens starter kit" :name "Design tokens starter kit"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"}, :file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"}
{:id "penpot-design-system"
:name "Penpot Design System | Pencil"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Pencil-Penpot-Design-System.penpot"}
{:id "wireframing-kit" {:id "wireframing-kit"
:name "Wireframe library" :name "Wireframe library"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"} :file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}
@@ -10,9 +13,6 @@
{:id "plants-app" {:id "plants-app"
:name "UI mockup example" :name "UI mockup example"
:file-uri "https://github.com/penpot/penpot-files/raw/main/Plants-app.penpot"} :file-uri "https://github.com/penpot/penpot-files/raw/main/Plants-app.penpot"}
{:id "penpot-design-system"
:name "Design system example"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Penpot%20-%20Design%20System%20v2.1.penpot"}
{:id "tutorial-for-beginners" {:id "tutorial-for-beginners"
:name "Tutorial for beginners" :name "Tutorial for beginners"
:file-uri "https://github.com/penpot/penpot-files/raw/main/tutorial-for-beginners.penpot"} :file-uri "https://github.com/penpot/penpot-files/raw/main/tutorial-for-beginners.penpot"}
+15 -8
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="robots" content="noindex,nofollow"> <meta name="robots" content="noindex,nofollow">
<meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Builtin API Documentation - Penpot</title> <title>{{label|upper}} API Documentation</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -19,7 +19,7 @@
<body> <body>
<main> <main>
<header> <header>
<h1>Penpot API Documentation (v{{version}})</h1> <h1>{{label|upper}}: API Documentation (v{{version}})</h1>
<small class="menu"> <small class="menu">
[ [
<nav> <nav>
@@ -31,9 +31,10 @@
</header> </header>
<section class="doc-content"> <section class="doc-content">
<h2>INTRODUCTION</h2> <h2>INTRODUCTION</h2>
<p>This documentation is intended to be a general overview of the penpot RPC API. <p>This documentation is intended to be a general overview of
If you prefer, you can use <a href="/api/openapi.json">OpenAPI</a> the {{label}} API. If you prefer, you can
and/or <a href="/api/openapi">SwaggerUI</a> as alternative.</p> use <a href="{{openapi}}">Swagger/OpenAPI</a> as
alternative.</p>
<h2>GENERAL NOTES</h2> <h2>GENERAL NOTES</h2>
@@ -43,7 +44,7 @@
that starts with <b>get-</b> in the name, can use GET HTTP that starts with <b>get-</b> in the name, can use GET HTTP
method which in many cases benefits from the HTTP cache.</p> method which in many cases benefits from the HTTP cache.</p>
{% block auth-section %}
<h3>Authentication</h3> <h3>Authentication</h3>
<p>The penpot backend right now offers two way for authenticate the request: <p>The penpot backend right now offers two way for authenticate the request:
<b>cookies</b> (the same mechanism that we use ourselves on accessing the API from the <b>cookies</b> (the same mechanism that we use ourselves on accessing the API from the
@@ -56,9 +57,10 @@
<p>The access token can be obtained on the appropriate section on profile settings <p>The access token can be obtained on the appropriate section on profile settings
and it should be provided using <b>`Authorization`</b> header with <b>`Token and it should be provided using <b>`Authorization`</b> header with <b>`Token
&lt;token-string&gt;`</b> value.</p> &lt;token-string&gt;`</b> value.</p>
{% endblock %}
<h3>Content Negotiation</h3> <h3>Content Negotiation</h3>
<p>The penpot API by default operates indistinctly with: <b>`application/json`</b> <p>This API operates indistinctly with: <b>`application/json`</b>
and <b>`application/transit+json`</b> content types. You should specify the and <b>`application/transit+json`</b> content types. You should specify the
desired content-type on the <b>`Accept`</b> header, the transit encoding is used desired content-type on the <b>`Accept`</b> header, the transit encoding is used
by default.</p> by default.</p>
@@ -75,13 +77,16 @@
standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch
API</a></p> API</a></p>
{% block limits-section %}
<h3>Limits</h3> <h3>Limits</h3>
<p>The rate limit work per user basis (this means that different api keys share <p>The rate limit work per user basis (this means that different api keys share
the same rate limit). For now the limits are not documented because we are the same rate limit). For now the limits are not documented because we are
studying and analyzing the data. As a general rule, it should not be abused, if an studying and analyzing the data. As a general rule, it should not be abused, if an
abusive use is detected, we will proceed to block the user's access to the abusive use is detected, we will proceed to block the user's access to the
API.</p> API.</p>
{% endblock %}
{% block webhooks-section %}
<h3>Webhooks</h3> <h3>Webhooks</h3>
<p>All methods that emit webhook events are marked with flag <b>WEBHOOK</b>, the <p>All methods that emit webhook events are marked with flag <b>WEBHOOK</b>, the
data structure defined on each method represents the <i>payload</i> of the data structure defined on each method represents the <i>payload</i> of the
@@ -97,9 +102,11 @@
"profileId": "db601c95-045f-808b-8002-361312e63531" "profileId": "db601c95-045f-808b-8002-361312e63531"
} }
</pre> </pre>
{% endblock %}
</section> </section>
<section class="rpc-doc-content"> <section class="rpc-doc-content">
<h2>RPC METHODS REFERENCE:</h2> <h2>METHODS REFERENCE:</h2>
<ul class="rpc-items"> <ul class="rpc-items">
{% for item in methods %} {% for item in methods %}
{% include "app/templates/api-doc-entry.tmpl" with item=item %} {% include "app/templates/api-doc-entry.tmpl" with item=item %}
+93 -99
View File
@@ -17,38 +17,6 @@ Debug Main Page
<desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc> <desc><a href="/dbg/error">CLICK HERE TO SEE THE ERROR REPORTS</a> </desc>
</fieldset> </fieldset>
<fieldset>
<legend>Download file data:</legend>
<desc>Given an FILE-ID, downloads the file data as file. The file data is encoded using transit.</desc>
<form method="get" action="/dbg/file/data">
<div class="row">
<input type="text" style="width:300px" name="file-id" placeholder="file-id" />
</div>
<div class="row">
<input type="submit" name="download" value="Download" />
<input type="submit" name="clone" value="Clone" />
</div>
</form>
</fieldset>
<fieldset>
<legend>Upload File Data:</legend>
<desc>Create a new file on your draft projects using the file downloaded from the previous section.</desc>
<form method="post" enctype="multipart/form-data" action="/dbg/file/data">
<div class="row">
<input type="file" name="file" value="" />
</div>
<div class="row">
<label>Import with same id?</label>
<input type="checkbox" name="reuseid" />
</div>
<div class="row">
<input type="submit" value="Upload" />
</div>
</form>
</fieldset>
<fieldset> <fieldset>
<legend>Profile Management</legend> <legend>Profile Management</legend>
<form method="post" action="/dbg/actions/resend-email-verification"> <form method="post" action="/dbg/actions/resend-email-verification">
@@ -77,10 +45,88 @@ Debug Main Page
</form> </form>
</fieldset> </fieldset>
<fieldset>
<legend>VIRTUAL CLOCK</legend>
<desc>
<p>
CURRENT CLOCK: <b>{{current-clock}}</b>
<br />
CURRENT OFFSET: <b>{{current-offset}}</b>
<br />
CURRENT TIME: <b>{{current-time}}</b>
</p>
<p>Examples: 3h, -7h, 24h (allowed suffixes: h, s)</p>
</desc>
<form method="post" action="/dbg/actions/set-virtual-clock">
<div class="row">
<input type="text" name="offset" placeholder="3h" value="" />
</div>
<div class="row">
<label for="force-verify">Are you sure?</label>
<input id="force-verify" type="checkbox" name="force" />
<br />
<small>
This is a just a security double check for prevent non intentional submits.
</small>
</div>
<div class="row">
<input type="submit" name="submit" value="Submit" />
<input type="submit" name="reset" value="Reset" />
</div>
</form>
</fieldset>
</section> </section>
<section class="widget">
<fieldset>
<legend>Download RAW file data:</legend>
<desc>Given an FILE-ID, downloads the file AS-IS (no validation
checks, just exports the file data and related objects in raw)
<br/>
<br/>
<b>WARNING: this operation does not performs any checks</b>
</desc>
<form method="get" action="/dbg/actions/file-raw-export-import">
<div class="row">
<input type="text" style="width:300px" name="file-id" placeholder="file-id" />
</div>
<div class="row">
<input type="submit" name="download" value="Download" />
<input type="submit" name="clone" value="Clone" />
</div>
</form>
</fieldset>
<fieldset>
<legend>Upload File Data:</legend>
<desc>Create a new file on your draft projects using the file downloaded from the previous section.
<br/>
<br/>
<b>WARNING: this operation does not performs any checks</b>
</desc>
<form method="post" enctype="multipart/form-data" action="/dbg/actions/file-raw-export-import">
<div class="row">
<input type="file" name="file" value="" />
</div>
<div class="row">
<label>Import with same id?</label>
<input type="checkbox" name="reuseid" />
</div>
<div class="row">
<input type="submit" value="Upload" />
</div>
</form>
</fieldset>
</section>
<section class="widget"> <section class="widget">
<fieldset> <fieldset>
<legend>Export binfile:</legend> <legend>Export binfile:</legend>
@@ -88,7 +134,7 @@ Debug Main Page
the related libraries in a single custom formatted binary the related libraries in a single custom formatted binary
file.</desc> file.</desc>
<form method="get" action="/dbg/file/export"> <form method="get" action="/dbg/actions/file-export">
<div class="row set-of-inputs"> <div class="row set-of-inputs">
<input type="text" style="width:300px" name="file-ids" placeholder="file-id" /> <input type="text" style="width:300px" name="file-ids" placeholder="file-id" />
<input type="text" style="width:300px" name="file-ids" placeholder="file-id" /> <input type="text" style="width:300px" name="file-ids" placeholder="file-id" />
@@ -116,7 +162,7 @@ Debug Main Page
<legend>Import binfile:</legend> <legend>Import binfile:</legend>
<desc>Import penpot file in binary format.</desc> <desc>Import penpot file in binary format.</desc>
<form method="post" enctype="multipart/form-data" action="/dbg/file/import"> <form method="post" enctype="multipart/form-data" action="/dbg/actions/file-import">
<div class="row"> <div class="row">
<input type="file" name="file" value="" /> <input type="file" name="file" value="" />
</div> </div>
@@ -130,79 +176,27 @@ Debug Main Page
<section class="widget"> <section class="widget">
<fieldset> <fieldset>
<legend>Reset file version</legend> <legend>Feature Flags for Team</legend>
<desc>Allows reset file data version to a specific number/</desc>
<form method="post" action="/dbg/actions/reset-file-version">
<div class="row">
<input type="text" style="width:300px" name="file-id" placeholder="file-id" />
</div>
<div class="row">
<input type="number" style="width:100px" name="version" placeholder="version" value="32" />
</div>
<div class="row">
<label for="force-version">Are you sure?</label>
<input id="force-version" type="checkbox" name="force" />
<br />
<small>
This is a just a security double check for prevent non intentional submits.
</small>
</div>
<div class="row">
<input type="submit" value="Submit" />
</div>
</form>
</fieldset>
</section>
<section class="widget">
<h2>Feature Flags</h2>
<fieldset>
<legend>Enable</legend>
<desc>Add a feature flag to a team</desc> <desc>Add a feature flag to a team</desc>
<form method="post" action="/dbg/actions/add-team-feature"> <form method="post" action="/dbg/actions/handle-team-features">
<div class="row"> <div class="row">
<input type="text" style="width:300px" name="team-id" placeholder="team-id" /> <input type="text" style="width:300px" name="team-id" placeholder="team-id" />
</div> </div>
<div class="row"> <div class="row">
<input type="text" style="width:100px" name="feature" placeholder="feature" value="" /> <select type="text" style="width:100px" name="feature">
{% for feature in supported-features %}
<option value="{{feature}}">{{feature}}</option>
{% endfor %}
</select>
</div> </div>
<div class="row"> <div class="row">
<label for="check-feature">Skip feature check</label> <select style="width:100px" name="action">
<input id="check-feature" type="checkbox" name="skip-check" /> <option value="">Action...</option>
<br /> <option value="show">Show</option>
<small> <option value="enable">Enable</option>
Do not check if the feature is supported <option value="disable">Disable</option>
</small> </select>
</div>
<div class="row">
<label for="force-version">Are you sure?</label>
<input id="force-version" type="checkbox" name="force" />
<br />
<small>
This is a just a security double check for prevent non intentional submits.
</small>
</div>
<div class="row">
<input type="submit" value="Submit" />
</div>
</form>
</fieldset>
<fieldset>
<legend>Disable</legend>
<desc>Remove a feature flag from a team</desc>
<form method="post" action="/dbg/actions/remove-team-feature">
<div class="row">
<input type="text" style="width:300px" name="team-id" placeholder="team-id" />
</div>
<div class="row">
<input type="text" style="width:100px" name="feature" placeholder="feature" value="" />
</div> </div>
<div class="row"> <div class="row">
@@ -7,7 +7,9 @@ penpot - error list
{% block content %} {% block content %}
<nav> <nav>
<div class="title"> <div class="title">
<h1>Error reports (last 200)</h1> <h1>Error reports (last 200)
<a href="/dbg">[GO BACK]</a>
</h1>
</div> </div>
</nav> </nav>
<main class="horizontal-list"> <main class="horizontal-list">
@@ -0,0 +1 @@
{% extends "app/templates/api-doc.tmpl" %}
@@ -0,0 +1,10 @@
{% extends "app/templates/api-doc.tmpl" %}
{% block auth-section %}
{% endblock %}
{% block limits-section %}
{% endblock %}
{% block webhooks-section %}
{% endblock %}
+2 -2
View File
@@ -7,7 +7,7 @@
name="description" name="description"
content="SwaggerUI" content="SwaggerUI"
/> />
<title>PENPOT Swagger UI</title> <title>{{label|upper}} API</title>
<style>{{swagger-css|safe}}</style> <style>{{swagger-css|safe}}</style>
</head> </head>
<body> <body>
@@ -16,7 +16,7 @@
<script> <script>
window.onload = () => { window.onload = () => {
window.ui = SwaggerUIBundle({ window.ui = SwaggerUIBundle({
url: '{{public-uri}}/api/openapi.json', url: '{{uri}}',
dom_id: '#swagger-ui', dom_id: '#swagger-ui',
presets: [ presets: [
SwaggerUIBundle.presets.apis, SwaggerUIBundle.presets.apis,
+1 -2
View File
@@ -25,8 +25,7 @@
<Logger name="app.storage.tmp" level="info" /> <Logger name="app.storage.tmp" level="info" />
<Logger name="app.worker" level="trace" /> <Logger name="app.worker" level="trace" />
<Logger name="app.msgbus" level="info" /> <Logger name="app.msgbus" level="info" />
<Logger name="app.http.websocket" level="info" /> <Logger name="app.http" level="info" />
<Logger name="app.http.sse" level="info" />
<Logger name="app.util.websocket" level="info" /> <Logger name="app.util.websocket" level="info" />
<Logger name="app.redis" level="info" /> <Logger name="app.redis" level="info" />
<Logger name="app.rpc.rlimit" level="info" /> <Logger name="app.rpc.rlimit" level="info" />
+1 -2
View File
@@ -25,8 +25,7 @@
<Logger name="app.storage.tmp" level="info" /> <Logger name="app.storage.tmp" level="info" />
<Logger name="app.worker" level="trace" /> <Logger name="app.worker" level="trace" />
<Logger name="app.msgbus" level="info" /> <Logger name="app.msgbus" level="info" />
<Logger name="app.http.websocket" level="info" /> <Logger name="app.http" level="info" />
<Logger name="app.http.sse" level="info" />
<Logger name="app.util.websocket" level="info" /> <Logger name="app.util.websocket" level="info" />
<Logger name="app.redis" level="info" /> <Logger name="app.redis" level="info" />
<Logger name="app.rpc.rlimit" level="info" /> <Logger name="app.rpc.rlimit" level="info" />
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
export PENPOT_SECRET_KEY=super-secret-devenv-key
export PENPOT_HOST=devenv
export PENPOT_PUBLIC_URI=https://localhost:3449
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
enable-login-with-password
disable-login-with-ldap \
disable-login-with-oidc \
disable-login-with-google \
disable-login-with-github \
disable-login-with-gitlab \
enable-backend-worker \
enable-backend-asserts \
disable-feature-fdata-pointer-map \
enable-feature-fdata-objects-map \
enable-audit-log \
enable-transit-readable-response \
enable-demo-users \
enable-user-feedback \
disable-secure-session-cookies \
enable-smtp \
enable-prepl-server \
enable-urepl-server \
enable-rpc-climit \
enable-rpc-rlimit \
enable-quotes \
enable-soft-rpc-rlimit \
enable-auto-file-snapshot \
enable-webhooks \
enable-access-tokens \
disable-tiered-file-data-storage \
enable-file-validation \
enable-file-schema-validation \
enable-redis-cache \
enable-subscriptions";
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
# Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
export PENPOT_USER_FEEDBACK_DESTINATION="support@example.com"
export AWS_ACCESS_KEY_ID=penpot-devenv
export AWS_SECRET_ACCESS_KEY=penpot-devenv
export PENPOT_OBJECTS_STORAGE_BACKEND=s3
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseShenandoahGC \
-XX:+UseCompactObjectHeaders \
-XX:ShenandoahGCMode=generational \
-XX:-OmitStackTraceInFastThrow \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
function setup_minio() {
# Initialize MINIO config
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
mc admin user info penpot-s3 penpot-devenv |grep -F -q "readwrite"
if [ "$?" = "1" ]; then
mc admin policy attach penpot-s3 readwrite --user=penpot-devenv -q
fi
mc mb penpot-s3/penpot -p -q
}
+12 -2
View File
@@ -71,19 +71,27 @@ def run_cmd(params):
print("EXC:", str(cause)) print("EXC:", str(cause))
sys.exit(-2) sys.exit(-2)
def create_profile(fullname, email, password): def create_profile(fullname, email, password, skip_tutorial=False, skip_walkthrough=False):
props = {}
if skip_tutorial:
props["viewed-tutorial?"] = True
if skip_walkthrough:
props["viewed-walkthrough?"] = True
params = { params = {
"cmd": "create-profile", "cmd": "create-profile",
"params": { "params": {
"fullname": fullname, "fullname": fullname,
"email": email, "email": email,
"password": password "password": password,
**props
} }
} }
res = run_cmd(params) res = run_cmd(params)
print(f"Created: {res['email']} / {res['id']}") print(f"Created: {res['email']} / {res['id']}")
def update_profile(email, fullname, password, is_active): def update_profile(email, fullname, password, is_active):
params = { params = {
"cmd": "update-profile", "cmd": "update-profile",
@@ -170,6 +178,8 @@ parser.add_argument("-n", "--fullname", help="fullname", action="store")
parser.add_argument("-e", "--email", help="email", action="store") parser.add_argument("-e", "--email", help="email", action="store")
parser.add_argument("-p", "--password", help="password", action="store") parser.add_argument("-p", "--password", help="password", action="store")
parser.add_argument("-c", "--connect", help="connect to PREPL", action="store", default="tcp://localhost:6063") parser.add_argument("-c", "--connect", help="connect to PREPL", action="store", default="tcp://localhost:6063")
parser.add_argument("--skip-tutorial", help="mark tutorial as viewed", action="store_true")
parser.add_argument("--skip-walkthrough", help="mark walkthrough as viewed", action="store_true")
args = parser.parse_args() args = parser.parse_args()
+4 -103
View File
@@ -1,112 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export PENPOT_SECRET_KEY=super-secret-devenv-key SCRIPT_DIR=$(dirname $0);
export PENPOT_HOST=devenv source $SCRIPT_DIR/_env;
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
enable-login-with-ldap \
enable-login-with-password
enable-login-with-oidc \
enable-login-with-google \
enable-login-with-github \
enable-login-with-gitlab \
enable-backend-worker \
enable-backend-asserts \
enable-feature-fdata-pointer-map \
enable-feature-fdata-objects-map \
enable-audit-log \
enable-transit-readable-response \
enable-demo-users \
disable-secure-session-cookies \
enable-smtp \
enable-prepl-server \
enable-urepl-server \
enable-rpc-climit \
enable-rpc-rlimit \
enable-quotes \
enable-soft-rpc-rlimit \
enable-auto-file-snapshot \
enable-webhooks \
enable-access-tokens \
enable-tiered-file-data-storage \
enable-file-validation \
enable-file-schema-validation \
enable-subscriptions \
enable-subscriptions-old";
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
# Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot"
# export PENPOT_DATABASE_USERNAME="penpot"
# export PENPOT_DATABASE_PASSWORD="penpot"
# export PENPOT_DATABASE_READONLY=true
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot_pre"
# export PENPOT_DATABASE_USERNAME="penpot_pre"
# export PENPOT_DATABASE_PASSWORD="penpot_pre"
# export PENPOT_LOGGERS_LOKI_URI="http://172.17.0.1:3100/loki/api/v1/push"
# export PENPOT_AUDIT_LOG_ARCHIVE_URI="http://localhost:6070/api/audit"
# Initialize MINIO config # Initialize MINIO config
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q setup_minio;
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
mc admin user info penpot-s3 penpot-devenv |grep -F -q "readwrite"
if [ "$?" = "1" ]; then
mc admin policy attach penpot-s3 readwrite --user=penpot-devenv -q
fi
mc mb penpot-s3/penpot -p -q
export AWS_ACCESS_KEY_ID=penpot-devenv
export AWS_SECRET_ACCESS_KEY=penpot-devenv
export PENPOT_OBJECTS_STORAGE_BACKEND=s3
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
export PENPOT_OBJECTS_STORAGE_FS_DIRECTORY="assets"
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv-repl.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
export JAVA_OPTS="$JAVA_OPTS -Dlog4j2.configurationFile=log4j2-devenv-repl.xml"
export OPTIONS="-A:jmx-remote -A:dev" export OPTIONS="-A:jmx-remote -A:dev"
# Setup HEAP
# export OPTIONS="$OPTIONS -J-Xms50m -J-Xmx1024m"
# export OPTIONS="$OPTIONS -J-Xms1100m -J-Xmx1100m -J-XX:+AlwaysPreTouch"
# Increase virtual thread pool size
# export OPTIONS="$OPTIONS -J-Djdk.virtualThreadScheduler.parallelism=16"
# Disable C2 Compiler
# export OPTIONS="$OPTIONS -J-XX:TieredStopAtLevel=1"
# Disable all compilers
# export OPTIONS="$OPTIONS -J-Xint"
# Setup GC
# export OPTIONS="$OPTIONS -J-XX:+UseG1GC"
# Setup GC
# export OPTIONS="$OPTIONS -J-XX:+UseZGC"
export OPTIONS_EVAL="nil" export OPTIONS_EVAL="nil"
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)" # export OPTIONS_EVAL="(set! *warn-on-reflection* true)"
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env bash
source /home/penpot/environ
export PENPOT_FLAGS="$PENPOT_FLAGS disable-backend-worker"
export OPTIONS="
-A:jmx-remote -A:dev \
-J-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-J-Djdk.attach.allowAttachSelf \
-J-Dlog4j2.configurationFile=log4j2-experiments.xml \
-J-XX:-OmitStackTraceInFastThrow \
-J-XX:+UnlockDiagnosticVMOptions \
-J-XX:+DebugNonSafepoints \
-J-Djdk.tracePinnedThreads=full \
-J-XX:+UseTransparentHugePages \
-J-XX:ReservedCodeCacheSize=1g \
-J-Dpolyglot.engine.WarnInterpreterOnly=false \
-J--enable-preview";
# Setup HEAP
export OPTIONS="$OPTIONS -J-Xms320g -J-Xmx320g -J-XX:+AlwaysPreTouch"
export PENPOT_HTTP_SERVER_IO_THREADS=2
export PENPOT_HTTP_SERVER_WORKER_THREADS=2
# Increase virtual thread pool size
# export OPTIONS="$OPTIONS -J-Djdk.virtualThreadScheduler.parallelism=16"
# Disable C2 Compiler
# export OPTIONS="$OPTIONS -J-XX:TieredStopAtLevel=1"
# Disable all compilers
# export OPTIONS="$OPTIONS -J-Xint"
# Setup GC
export OPTIONS="$OPTIONS -J-XX:+UseG1GC -J-Xlog:gc:logs/gc.log"
# Setup GC
#export OPTIONS="$OPTIONS -J-XX:+UseZGC -J-XX:+ZGenerational -J-Xlog:gc:logs/gc.log"
# Enable ImageMagick v7.x support
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
export OPTIONS_EVAL="nil"
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"
set -ex
exec clojure $OPTIONS -M -e "$OPTIONS_EVAL" -m rebel-readline.main
+5 -36
View File
@@ -1,44 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export PENPOT_SECRET_KEY=super-secret-devenv-key SCRIPT_DIR=$(dirname $0);
export PENPOT_HOST=devenv
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
enable-backend-asserts \
enable-feature-fdata-pointer-map \
enable-feature-fdata-objects-map \
enable-file-snapshot \
enable-tiered-file-data-storage";
export JAVA_OPTS=" source $SCRIPT_DIR/_env;
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \ export OPTIONS="-A:dev"
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv.xml \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints";
export CLOJURE_OPTIONS="-A:dev"
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
# Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
export AWS_ACCESS_KEY_ID=penpot-devenv
export AWS_SECRET_ACCESS_KEY=penpot-devenv
export PENPOT_OBJECTS_STORAGE_BACKEND=s3
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
entrypoint=${1:-app.main}; entrypoint=${1:-app.main};
shift 1; shift 1;
set -ex set -ex
clojure $CLOJURE_OPTIONS -A:dev -M -m $entrypoint "$@"; exec clojure $OPTIONS -A:dev -M -m $entrypoint "$@";
+5 -64
View File
@@ -1,70 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export PENPOT_SECRET_KEY=super-secret-devenv-key SCRIPT_DIR=$(dirname $0);
export PENPOT_HOST=devenv source $SCRIPT_DIR/_env;
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
enable-prepl-server \
enable-urepl-server \
enable-nrepl-server \
enable-webhooks \
enable-backend-asserts \
enable-audit-log \
enable-login-with-ldap \
enable-transit-readable-response \
enable-demo-users \
enable-feature-fdata-pointer-map \
enable-feature-fdata-objects-map \
disable-secure-session-cookies \
enable-rpc-climit \
enable-smtp \
enable-quotes \
enable-file-snapshot \
enable-access-tokens \
enable-tiered-file-data-storage \
enable-file-validation \
enable-file-schema-validation \
enable-subscriptions \
enable-subscriptions-old ";
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
# Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# Initialize MINIO config # Initialize MINIO config
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q setup_minio;
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
mc admin user info penpot-s3 penpot-devenv |grep -F -q "readwrite"
if [ "$?" = "1" ]; then
mc admin policy attach penpot-s3 readwrite --user=penpot-devenv -q
fi
mc mb penpot-s3/penpot -p -q
export AWS_ACCESS_KEY_ID=penpot-devenv
export AWS_SECRET_ACCESS_KEY=penpot-devenv
export PENPOT_OBJECTS_STORAGE_BACKEND=s3
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
entrypoint=${1:-app.main};
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:-OmitStackTraceInFastThrow \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
export OPTIONS="-A:jmx-remote -A:dev"
shift 1;
set -ex set -ex
clojure $OPTIONS -M -m $entrypoint; exec clojure -A:jmx-remote -A:dev -M -m app.main "$@";
File diff suppressed because it is too large. Load diff
+376 -103
View File
@@ -15,19 +15,21 @@
[app.common.files.migrations :as fmg] [app.common.files.migrations :as fmg]
[app.common.files.validate :as fval] [app.common.files.validate :as fval]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.common.weak :as weak]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.db.sql :as sql] [app.db.sql :as sql]
[app.features.fdata :as feat.fdata] [app.features.fdata :as fdata]
[app.features.file-migrations :as feat.fmigr] [app.features.file-migrations :as fmigr]
[app.loggers.audit :as-alias audit] [app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks] [app.loggers.webhooks :as-alias webhooks]
[app.storage :as sto] [app.storage :as sto]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.pointer-map :as pmap] [app.util.pointer-map :as pmap]
[app.util.time :as dt]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str] [cuerdas.core :as str]
@@ -38,6 +40,7 @@
(def ^:dynamic *state* nil) (def ^:dynamic *state* nil)
(def ^:dynamic *options* nil) (def ^:dynamic *options* nil)
(def ^:dynamic *reference-file* nil)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEFAULTS ;; DEFAULTS
@@ -53,17 +56,12 @@
(* 1024 1024 100)) (* 1024 1024 100))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare get-resolved-file-libraries) (declare get-resolved-file-libraries)
(declare update-file!)
(def file-attrs (def file-attrs
#{:id (sm/keys ctf/schema:file))
:name
:migrations
:features
:project-id
:is-shared
:version
:data})
(defn parse-file-format (defn parse-file-format
[template] [template]
@@ -143,32 +141,176 @@
([index coll attr] ([index coll attr]
(reduce #(index-object %1 %2 attr) index coll))) (reduce #(index-object %1 %2 attr) index coll)))
(defn decode-row (defn- decode-row-features
[{:keys [data changes features] :as row}] [{:keys [features] :as row}]
(when row (when row
(cond-> row (cond-> row
features (assoc :features (db/decode-pgarray features #{})) (db/pgarray? features) (assoc :features (db/decode-pgarray features #{})))))
changes (assoc :changes (blob/decode changes))
data (assoc :data (blob/decode data)))))
(def sql:get-minimal-file
"SELECT f.id,
f.revn,
f.modified_at,
f.deleted_at
FROM file AS f
WHERE f.id = ?")
(defn decode-file (defn get-minimal-file
"A general purpose file decoding function that resolves all external [cfg id & {:as opts}]
pointers, run migrations and return plain vanilla file map" (db/get-with-sql cfg [sql:get-minimal-file id] opts))
[cfg {:keys [id] :as file}]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(let [file (->> file
(feat.fmigr/resolve-applied-migrations cfg)
(feat.fdata/resolve-file-data cfg))
libs (delay (get-resolved-file-libraries cfg file))]
(-> file (def sql:files-with-data
(update :features db/decode-pgarray #{}) "SELECT f.id,
(update :data blob/decode) f.project_id,
(update :data feat.fdata/process-pointers deref) f.created_at,
(update :data feat.fdata/process-objects (partial into {})) f.modified_at,
(update :data assoc :id id) f.deleted_at,
(fmg/migrate-file libs))))) f.name,
f.is_shared,
f.has_media_trimmed,
f.revn,
f.data AS legacy_data,
f.ignore_sync_until,
f.comment_thread_seqn,
f.features,
f.version,
f.vern,
p.team_id,
coalesce(fd.backend, 'legacy-db') AS backend,
fd.metadata AS metadata,
fd.data AS data
FROM file AS f
LEFT JOIN file_data AS fd ON (fd.file_id = f.id AND fd.id = f.id)
INNER JOIN project AS p ON (p.id = f.project_id)")
(def sql:get-file
(str sql:files-with-data " WHERE f.id = ?"))
(def sql:get-file-without-data
(str "WITH files AS (" sql:files-with-data ")"
"SELECT f.id,
f.project_id,
f.created_at,
f.modified_at,
f.deleted_at,
f.name,
f.is_shared,
f.has_media_trimmed,
f.revn,
f.ignore_sync_until,
f.comment_thread_seqn,
f.features,
f.version,
f.vern,
f.team_id
FROM files AS f
WHERE f.id = ?"))
(defn- migrate-file
[{:keys [::db/conn] :as cfg} {:keys [read-only?]} {:keys [id] :as file}]
(binding [pmap/*load-fn* (partial fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)]
(let [libs (delay (get-resolved-file-libraries cfg file))
;; For avoid unnecesary overhead of creating multiple
;; pointers and handly internally with objects map in their
;; worst case (when probably all shapes and all pointers
;; will be readed in any case), we just realize/resolve them
;; before applying the migration to the file.
file (-> (fdata/realize cfg file)
(fmg/migrate-file libs))]
(if (or read-only? (db/read-only? conn))
file
(do ;; When file is migrated, we break the rule of no
;; perform mutations on get operations and update the
;; file with all migrations applied
(update-file! cfg file)
(fmigr/resolve-applied-migrations cfg file))))))
(defn- get-file*
[{:keys [::db/conn] :as cfg} id
{:keys [migrate?
realize?
decode?
skip-locked?
include-deleted?
load-data?
throw-if-not-exists?
lock-for-update?
lock-for-share?]
:or {lock-for-update? false
lock-for-share? false
load-data? true
migrate? true
decode? true
include-deleted? false
throw-if-not-exists? true
realize? false}
:as options}]
(assert (db/connection? conn) "expected cfg with valid connection")
(when (and (not load-data?)
(or lock-for-share? lock-for-share? skip-locked?))
(throw (IllegalArgumentException. "locking is incompatible when `load-data?` is false")))
(let [sql
(if load-data?
sql:get-file
sql:get-file-without-data)
sql
(cond
lock-for-update?
(str sql " FOR UPDATE of f")
lock-for-share?
(str sql " FOR SHARE of f")
:else
sql)
sql
(if skip-locked?
(str sql " SKIP LOCKED")
sql)
file
(db/get-with-sql conn [sql id]
{::db/throw-if-not-exists false
::db/remove-deleted (not include-deleted?)})
file
(-> file
(d/update-when :features db/decode-pgarray #{})
(d/update-when :metadata fdata/decode-metadata))]
(if file
(if load-data?
(let [file
(->> file
(fmigr/resolve-applied-migrations cfg)
(fdata/resolve-file-data cfg))
will-migrate?
(and migrate? (fmg/need-migration? file))]
(if decode?
(cond->> (fdata/decode-file-data cfg file)
(and realize? (not will-migrate?))
(fdata/realize cfg)
will-migrate?
(migrate-file cfg options))
file))
file)
(when-not (or skip-locked? (not throw-if-not-exists?))
(ex/raise :type :not-found
:code :object-not-found
:hint "database object not found"
:table :file
:file-id id)))))
(defn get-file (defn get-file
"Get file, resolve all features and apply migrations. "Get file, resolve all features and apply migrations.
@@ -177,10 +319,7 @@
operations on file, because it removes the ovehead of lazy fetching operations on file, because it removes the ovehead of lazy fetching
and decoding." and decoding."
[cfg file-id & {:as opts}] [cfg file-id & {:as opts}]
(db/run! cfg (fn [{:keys [::db/conn] :as cfg}] (db/run! cfg get-file* file-id opts))
(some->> (db/get* conn :file {:id file-id}
(assoc opts ::db/remove-deleted false))
(decode-file cfg)))))
(defn clean-file-features (defn clean-file-features
[file] [file]
@@ -192,6 +331,81 @@
(set/difference cfeat/backend-only-features)) (set/difference cfeat/backend-only-features))
#{})))) #{}))))
(defn check-file-exists
[cfg id & {:keys [include-deleted?]
:or {include-deleted? false}
:as options}]
(db/get-with-sql cfg [sql:get-minimal-file id]
{:db/remove-deleted (not include-deleted?)}))
(def ^:private sql:file-permissions
"select fpr.is_owner,
fpr.is_admin,
fpr.can_edit
from file_profile_rel as fpr
inner join file as f on (f.id = fpr.file_id)
where fpr.file_id = ?
and fpr.profile_id = ?
union all
select tpr.is_owner,
tpr.is_admin,
tpr.can_edit
from team_profile_rel as tpr
inner join project as p on (p.team_id = tpr.team_id)
inner join file as f on (p.id = f.project_id)
where f.id = ?
and tpr.profile_id = ?
union all
select ppr.is_owner,
ppr.is_admin,
ppr.can_edit
from project_profile_rel as ppr
inner join file as f on (f.project_id = ppr.project_id)
where f.id = ?
and ppr.profile_id = ?")
(defn- get-file-permissions*
[conn profile-id file-id]
(when (and profile-id file-id)
(db/exec! conn [sql:file-permissions
file-id profile-id
file-id profile-id
file-id profile-id])))
(defn get-file-permissions
([conn profile-id file-id]
(let [rows (get-file-permissions* conn profile-id file-id)
is-owner (boolean (some :is-owner rows))
is-admin (boolean (some :is-admin rows))
can-edit (boolean (some :can-edit rows))]
(when (seq rows)
{:type :membership
:is-owner is-owner
:is-admin (or is-owner is-admin)
:can-edit (or is-owner is-admin can-edit)
:can-read true
:is-logged (some? profile-id)})))
([conn profile-id file-id share-id]
(let [perms (get-file-permissions conn profile-id file-id)
ldata (some-> (db/get* conn :share-link {:id share-id :file-id file-id})
(dissoc :flags)
(update :pages db/decode-pgarray #{}))]
;; NOTE: in a future when share-link becomes more powerful and
;; will allow us specify which parts of the app is available, we
;; will probably need to tweak this function in order to expose
;; this flags to the frontend.
(cond
(some? perms) perms
(some? ldata) {:type :share-link
:can-read true
:pages (:pages ldata)
:is-logged (some? profile-id)
:who-comment (:who-comment ldata)
:who-inspect (:who-inspect ldata)}))))
(defn get-project (defn get-project
[cfg project-id] [cfg project-id]
(db/get cfg :project {:id project-id})) (db/get cfg :project {:id project-id}))
@@ -204,12 +418,12 @@
(let [conn (db/get-connection cfg) (let [conn (db/get-connection cfg)
ids (db/create-array conn "uuid" ids)] ids (db/create-array conn "uuid" ids)]
(->> (db/exec! conn [sql:get-teams ids]) (->> (db/exec! conn [sql:get-teams ids])
(map decode-row)))) (map decode-row-features))))
(defn get-team (defn get-team
[cfg team-id] [cfg team-id]
(-> (db/get cfg :team {:id team-id}) (-> (db/get cfg :team {:id team-id})
(decode-row))) (decode-row-features)))
(defn get-fonts (defn get-fonts
[cfg team-id] [cfg team-id]
@@ -301,7 +515,6 @@
(do (do
(l/trc :hint "lookup index" (l/trc :hint "lookup index"
:file-id (str file-id) :file-id (str file-id)
:snap-id (str (:snapshot-id file))
:id (str id) :id (str id)
:result (str (get mobj :id))) :result (str (get mobj :id)))
(get mobj :id)) (get mobj :id))
@@ -318,7 +531,6 @@
(doseq [[old-id item] missing-index] (doseq [[old-id item] missing-index]
(l/dbg :hint "create missing references" (l/dbg :hint "create missing references"
:file-id (str file-id) :file-id (str file-id)
:snap-id (str (:snapshot-id file))
:old-id (str old-id) :old-id (str old-id)
:id (str (:id item))) :id (str (:id item)))
(db/insert! conn :file-media-object item (db/insert! conn :file-media-object item
@@ -329,12 +541,16 @@
(def sql:get-file-media (def sql:get-file-media
"SELECT * FROM file_media_object WHERE id = ANY(?)") "SELECT * FROM file_media_object WHERE id = ANY(?)")
(defn get-file-media*
[{:keys [::db/conn] :as cfg} {:keys [data id] :as file}]
(let [used (cfh/collect-used-media data)
used (db/create-array conn "uuid" used)]
(->> (db/exec! conn [sql:get-file-media used])
(mapv (fn [row] (assoc row :file-id id))))))
(defn get-file-media (defn get-file-media
[cfg {:keys [data] :as file}] [cfg file]
(db/run! cfg (fn [{:keys [::db/conn]}] (db/run! cfg get-file-media* file))
(let [used (cfh/collect-used-media data)
used (db/create-array conn "uuid" used)]
(db/exec! conn [sql:get-file-media used])))))
(def ^:private sql:get-team-files-ids (def ^:private sql:get-team-files-ids
"SELECT f.id FROM file AS f "SELECT f.id FROM file AS f
@@ -409,7 +625,7 @@
[cfg data file-id] [cfg data file-id]
(let [library-ids (get-libraries cfg [file-id])] (let [library-ids (get-libraries cfg [file-id])]
(reduce (fn [data library-id] (reduce (fn [data library-id]
(if-let [library (get-file cfg library-id)] (if-let [library (get-file cfg library-id :include-deleted? true)]
(ctf/absorb-assets data (:data library)) (ctf/absorb-assets data (:data library))
data)) data))
data data
@@ -421,6 +637,27 @@
(db/exec-one! conn ["SET LOCAL idle_in_transaction_session_timeout = 0"]) (db/exec-one! conn ["SET LOCAL idle_in_transaction_session_timeout = 0"])
(db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"]))) (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])))
(defn invalidate-thumbnails
[cfg file-id]
(let [storage (sto/resolve cfg)
sql-1
(str "update file_tagged_object_thumbnail "
" set deleted_at = now() "
" where file_id=? returning media_id")
sql-2
(str "update file_thumbnail "
" set deleted_at = now() "
" where file_id=? returning media_id")]
(run! #(sto/touch-object! storage %)
(sequence
(keep :media-id)
(concat
(db/exec! cfg [sql-1 file-id])
(db/exec! cfg [sql-2 file-id]))))))
(defn process-file (defn process-file
[cfg {:keys [id] :as file}] [cfg {:keys [id] :as file}]
(let [libs (delay (get-resolved-file-libraries cfg file))] (let [libs (delay (get-resolved-file-libraries cfg file))]
@@ -444,78 +681,102 @@
;; all of them, not only the applied ;; all of them, not only the applied
(vary-meta dissoc ::fmg/migrated)))) (vary-meta dissoc ::fmg/migrated))))
(defn encode-file (defn- encode-file
[{:keys [::db/conn] :as cfg} {:keys [id features] :as file}] [cfg {:keys [id features] :as file}]
(let [file (if (contains? features "fdata/objects-map") (let [file (if (and (contains? features "fdata/objects-map")
(feat.fdata/enable-objects-map file) (:data file))
(fdata/enable-objects-map file)
file) file)
file (if (contains? features "fdata/pointer-map") file (if (and (contains? features "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)] (:data file))
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! cfg id) (binding [pmap/*tracked* (pmap/create-tracked :inherit true)]
(let [file (fdata/enable-pointer-map file)]
(fdata/persist-pointers! cfg id)
file)) file))
file)] file)]
(-> file (-> file
(update :features db/encode-pgarray conn "text") (d/update-when :features into-array)
(update :data blob/encode)))) (d/update-when :data blob/encode))))
(defn get-params-from-file (defn- file->params
[file] [file]
(let [params {:has-media-trimmed (:has-media-trimmed file) (-> (select-keys file file-attrs)
:ignore-sync-until (:ignore-sync-until file) (assoc :data nil)
:project-id (:project-id file) (dissoc :team-id)
:features (:features file) (dissoc :migrations)))
:name (:name file)
:is-shared (:is-shared file)
:version (:version file)
:data (:data file)
:id (:id file)
:deleted-at (:deleted-at file)
:created-at (:created-at file)
:modified-at (:modified-at file)
:revn (:revn file)
:vern (:vern file)}]
(-> (d/without-nils params) (defn- file->file-data-params
(assoc :data-backend nil) [{:keys [id] :as file} & {:as opts}]
(assoc :data-ref-id nil)))) (let [created-at (or (:created-at file) (ct/now))
modified-at (or (:modified-at file) created-at)]
(d/without-nils
{:id id
:type "main"
:file-id id
:data (:data file)
:metadata (:metadata file)
:created-at created-at
:modified-at modified-at})))
(defn insert-file! (defn insert-file!
"Insert a new file into the database table" "Insert a new file into the database table. Expectes a not-encoded file.
Returns nil."
[{:keys [::db/conn] :as cfg} file & {:as opts}] [{:keys [::db/conn] :as cfg} file & {:as opts}]
(feat.fmigr/upsert-migrations! conn file) (db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])
(let [params (-> (encode-file cfg file)
(get-params-from-file))] (when (:migrations file)
(db/insert! conn :file params opts))) (fmigr/upsert-migrations! conn file))
(let [file (encode-file cfg file)]
(db/insert! conn :file
(file->params file)
(assoc opts ::db/return-keys false))
(->> (file->file-data-params file)
(fdata/upsert! cfg))
nil))
(defn update-file! (defn update-file!
"Update an existing file on the database." "Update an existing file on the database. Expects not encoded file."
[{:keys [::db/conn ::sto/storage] :as cfg} {:keys [id] :as file} & {:as opts}] [{:keys [::db/conn] :as cfg} {:keys [id] :as file} & {:as opts}]
(let [file (encode-file cfg file)
params (-> (get-params-from-file file)
(dissoc :id))]
;; If file was already offloaded, we touch the underlying storage (if (::reset-migrations? opts false)
;; object for properly trigger storage-gc-touched task (fmigr/reset-migrations! conn file)
(when (feat.fdata/offloaded? file) (fmigr/upsert-migrations! conn file))
(some->> (:data-ref-id file) (sto/touch-object! storage)))
(feat.fmigr/upsert-migrations! conn file) (let [file
(db/update! conn :file params {:id id} opts))) (encode-file cfg file)
file-params
(file->params (dissoc file :id))
file-data-params
(file->file-data-params file)]
(db/update! conn :file file-params
{:id id}
{::db/return-keys false})
(fdata/upsert! cfg file-data-params)
nil))
(defn save-file! (defn save-file!
"Applies all the final validations and perist the file, binfile "Applies all the final validations and perist the file, binfile
specific, should not be used outside of binfile domain" specific, should not be used outside of binfile domain.
Returns nil"
[{:keys [::timestamp] :as cfg} file & {:as opts}] [{:keys [::timestamp] :as cfg} file & {:as opts}]
(assert (dt/instant? timestamp) "expected valid timestamp") (assert (ct/inst? timestamp) "expected valid timestamp")
(let [file (-> file (let [file (-> file
(assoc :created-at timestamp) (assoc :created-at timestamp)
(assoc :modified-at timestamp) (assoc :modified-at timestamp)
(assoc :ignore-sync-until (dt/plus timestamp (dt/duration {:seconds 5}))) (cond-> (not (::overwrite cfg))
(assoc :ignore-sync-until (ct/plus timestamp (ct/duration {:seconds 5}))))
(update :features (update :features
(fn [features] (fn [features]
(-> (::features cfg #{}) (-> (::features cfg #{})
@@ -532,8 +793,9 @@
(when (ex/exception? result) (when (ex/exception? result)
(l/error :hint "file schema validation error" :cause result)))) (l/error :hint "file schema validation error" :cause result))))
(insert-file! cfg file opts))) (if (::overwrite cfg)
(update-file! cfg file (assoc opts ::reset-migrations? true))
(insert-file! cfg file opts))))
(def ^:private sql:get-file-libraries (def ^:private sql:get-file-libraries
"WITH RECURSIVE libs AS ( "WITH RECURSIVE libs AS (
@@ -558,10 +820,11 @@
l.revn, l.revn,
l.vern, l.vern,
l.synced_at, l.synced_at,
l.is_shared l.is_shared,
l.version
FROM libs AS l FROM libs AS l
INNER JOIN project AS p ON (p.id = l.project_id) INNER JOIN project AS p ON (p.id = l.project_id)
WHERE l.deleted_at IS NULL OR l.deleted_at > now();") WHERE l.deleted_at IS NULL;")
(defn get-file-libraries (defn get-file-libraries
[conn file-id] [conn file-id]
@@ -570,12 +833,22 @@
;; FIXME: :is-indirect set to false to all rows looks ;; FIXME: :is-indirect set to false to all rows looks
;; completly useless ;; completly useless
(map #(assoc % :is-indirect false)) (map #(assoc % :is-indirect false))
(map decode-row)) (map decode-row-features))
(db/exec! conn [sql:get-file-libraries file-id]))) (db/exec! conn [sql:get-file-libraries file-id])))
(defn get-resolved-file-libraries (defn get-resolved-file-libraries
"A helper for preload file libraries" "Get all file libraries including itself. Returns an instance of
[{:keys [::db/conn] :as cfg} file] LoadableWeakValueMap that allows do not have strong references to
(->> (get-file-libraries conn (:id file)) the loaded libraries and reduce possible memory pressure on having
(into [file] (map #(get-file cfg (:id %)))) all this libraries loaded at same time on processing file validation
(d/index-by :id))) or file migration.
This still requires at least one library at time to be loaded while
access to it is performed, but it improves considerable not having
the need of loading all the libraries at the same time."
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
(let [library-ids (->> (get-file-libraries conn (:id file))
(map :id)
(cons (:id file)))
load-fn #(get-file cfg % :migrate? false)]
(weak/loadable-weak-value-map library-ids load-fn {id file})))
-5
View File
@@ -36,11 +36,6 @@
"fdata/shape-data-type" "fdata/shape-data-type"
nil nil
;; There is no migration needed, but we don't want to allow
;; copy paste nor import of variant files into no-variant teams
"variants/v1"
nil
(ex/raise :type :internal (ex/raise :type :internal
:code :no-migration-defined :code :no-migration-defined
:hint (str/ffmt "no migation for feature '%' on file importation" feature) :hint (str/ffmt "no migation for feature '%' on file importation" feature)
+7 -7
View File
@@ -17,6 +17,7 @@
[app.common.fressian :as fres] [app.common.fressian :as fres]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.time :as ct]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@@ -30,7 +31,6 @@
[app.storage.tmp :as tmp] [app.storage.tmp :as tmp]
[app.tasks.file-gc] [app.tasks.file-gc]
[app.util.events :as events] [app.util.events :as events]
[app.util.time :as dt]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[clojure.java.io :as jio] [clojure.java.io :as jio]
[clojure.set :as set] [clojure.set :as set]
@@ -346,7 +346,7 @@
thumbnails (->> (bfc/get-file-object-thumbnails cfg file-id) thumbnails (->> (bfc/get-file-object-thumbnails cfg file-id)
(mapv #(dissoc % :file-id))) (mapv #(dissoc % :file-id)))
file (cond-> (bfc/get-file cfg file-id) file (cond-> (bfc/get-file cfg file-id :realize? true)
detach? detach?
(-> (ctf/detach-external-references file-id) (-> (ctf/detach-external-references file-id)
(dissoc :libraries)) (dissoc :libraries))
@@ -434,7 +434,7 @@
(defn read-import! (defn read-import!
"Do the importation of the specified resource in penpot custom binary "Do the importation of the specified resource in penpot custom binary
format." format."
[{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (dt/now)} :as options}] [{:keys [::bfc/input ::bfc/timestamp] :or {timestamp (ct/now)} :as options}]
(dm/assert! (dm/assert!
"expected input stream" "expected input stream"
@@ -442,7 +442,7 @@
(dm/assert! (dm/assert!
"expected valid instant" "expected valid instant"
(dt/instant? timestamp)) (ct/inst? timestamp))
(let [version (read-header! input)] (let [version (read-header! input)]
(read-import (assoc options ::version version ::bfc/timestamp timestamp)))) (read-import (assoc options ::version version ::bfc/timestamp timestamp))))
@@ -682,7 +682,7 @@
(io/coercible? output)) (io/coercible? output))
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
ab (volatile! false) ab (volatile! false)
cs (volatile! nil)] cs (volatile! nil)]
(try (try
@@ -720,7 +720,7 @@
(satisfies? jio/IOFactory input)) (satisfies? jio/IOFactory input))
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
cs (volatile! nil)] cs (volatile! nil)]
(l/info :hint "import: started" :id (str id)) (l/info :hint "import: started" :id (str id))
@@ -742,6 +742,6 @@
(finally (finally
(l/info :hint "import: terminated" (l/info :hint "import: terminated"
:id (str id) :id (str id)
:elapsed (dt/format-duration (tp)) :elapsed (ct/format-duration (tp))
:error? (some? @cs)))))) :error? (some? @cs))))))
+7 -7
View File
@@ -13,6 +13,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.time :as ct]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@@ -23,7 +24,6 @@
[app.storage :as sto] [app.storage :as sto]
[app.storage.tmp :as tmp] [app.storage.tmp :as tmp]
[app.util.events :as events] [app.util.events :as events]
[app.util.time :as dt]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str] [cuerdas.core :as str]
@@ -153,7 +153,7 @@
(defn- write-file! (defn- write-file!
[cfg file-id] [cfg file-id]
(let [file (bfc/get-file cfg file-id) (let [file (bfc/get-file cfg file-id :realize? true)
thumbs (bfc/get-file-object-thumbnails cfg file-id) thumbs (bfc/get-file-object-thumbnails cfg file-id)
media (bfc/get-file-media cfg file) media (bfc/get-file-media cfg file)
rels (bfc/get-files-rels cfg #{file-id})] rels (bfc/get-files-rels cfg #{file-id})]
@@ -344,7 +344,7 @@
(defn export-team! (defn export-team!
[cfg team-id] [cfg team-id]
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
cfg (create-database cfg)] cfg (create-database cfg)]
(l/inf :hint "start" (l/inf :hint "start"
@@ -378,15 +378,15 @@
(l/inf :hint "end" (l/inf :hint "end"
:operation "export" :operation "export"
:id (str id) :id (str id)
:elapsed (dt/format-duration elapsed))))))) :elapsed (ct/format-duration elapsed)))))))
(defn import-team! (defn import-team!
[cfg path] [cfg path]
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
cfg (-> (create-database cfg path) cfg (-> (create-database cfg path)
(assoc ::bfc/timestamp (dt/now)))] (assoc ::bfc/timestamp (ct/now)))]
(l/inf :hint "start" (l/inf :hint "start"
:operation "import" :operation "import"
@@ -434,4 +434,4 @@
(l/inf :hint "end" (l/inf :hint "end"
:operation "import" :operation "import"
:id (str id) :id (str id)
:elapsed (dt/format-duration elapsed))))))) :elapsed (ct/format-duration elapsed)))))))
+221 -204
View File
@@ -12,7 +12,6 @@
[app.binfile.common :as bfc] [app.binfile.common :as bfc]
[app.binfile.migrations :as bfm] [app.binfile.migrations :as bfm]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.files.migrations :as-alias fmg] [app.common.files.migrations :as-alias fmg]
@@ -21,13 +20,14 @@
[app.common.media :as cmedia] [app.common.media :as cmedia]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.thumbnails :as cth] [app.common.thumbnails :as cth]
[app.common.time :as ct]
[app.common.types.color :as ctcl] [app.common.types.color :as ctcl]
[app.common.types.component :as ctc] [app.common.types.component :as ctc]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
[app.common.types.page :as ctp] [app.common.types.page :as ctp]
[app.common.types.plugins :as ctpg] [app.common.types.plugins :as ctpg]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.tokens-lib :as cto] [app.common.types.tokens-lib :as ctob]
[app.common.types.typography :as cty] [app.common.types.typography :as cty]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@@ -36,14 +36,15 @@
[app.storage :as sto] [app.storage :as sto]
[app.storage.impl :as sto.impl] [app.storage.impl :as sto.impl]
[app.util.events :as events] [app.util.events :as events]
[app.util.time :as dt]
[clojure.java.io :as jio] [clojure.java.io :as jio]
[cuerdas.core :as str] [cuerdas.core :as str]
[datoteka.fs :as fs] [datoteka.fs :as fs]
[datoteka.io :as io]) [datoteka.io :as io])
(:import (:import
java.io.File
java.io.InputStream java.io.InputStream
java.io.OutputStreamWriter java.io.OutputStreamWriter
java.lang.AutoCloseable
java.util.zip.ZipEntry java.util.zip.ZipEntry
java.util.zip.ZipFile java.util.zip.ZipFile
java.util.zip.ZipOutputStream)) java.util.zip.ZipOutputStream))
@@ -54,7 +55,7 @@
[:map {:title "Manifest"} [:map {:title "Manifest"}
[:version ::sm/int] [:version ::sm/int]
[:type :string] [:type :string]
[:referer {:optional true} :string]
[:generated-by {:optional true} :string] [:generated-by {:optional true} :string]
[:files [:files
@@ -93,7 +94,7 @@
(defn- default-now (defn- default-now
[o] [o]
(or o (dt/now))) (or o (ct/now)))
;; --- ENCODERS ;; --- ENCODERS
@@ -104,25 +105,25 @@
(sm/encoder ctp/schema:page sm/json-transformer)) (sm/encoder ctp/schema:page sm/json-transformer))
(def encode-shape (def encode-shape
(sm/encoder ::cts/shape sm/json-transformer)) (sm/encoder cts/schema:shape sm/json-transformer))
(def encode-media (def encode-media
(sm/encoder ::ctf/media sm/json-transformer)) (sm/encoder ctf/schema:media sm/json-transformer))
(def encode-component (def encode-component
(sm/encoder ::ctc/component sm/json-transformer)) (sm/encoder ctc/schema:component sm/json-transformer))
(def encode-color (def encode-color
(sm/encoder ctcl/schema:library-color sm/json-transformer)) (sm/encoder ctcl/schema:library-color sm/json-transformer))
(def encode-typography (def encode-typography
(sm/encoder ::cty/typography sm/json-transformer)) (sm/encoder cty/schema:typography sm/json-transformer))
(def encode-tokens-lib (def encode-tokens-lib
(sm/encoder ::cto/tokens-lib sm/json-transformer)) (sm/encoder ctob/schema:tokens-lib sm/json-transformer))
(def encode-plugin-data (def encode-plugin-data
(sm/encoder ::ctpg/plugin-data sm/json-transformer)) (sm/encoder ctpg/schema:plugin-data sm/json-transformer))
(def encode-storage-object (def encode-storage-object
(sm/encoder schema:storage-object sm/json-transformer)) (sm/encoder schema:storage-object sm/json-transformer))
@@ -139,7 +140,7 @@
(sm/decoder ctf/schema:media sm/json-transformer)) (sm/decoder ctf/schema:media sm/json-transformer))
(def decode-component (def decode-component
(sm/decoder ::ctc/component sm/json-transformer)) (sm/decoder ctc/schema:component sm/json-transformer))
(def decode-color (def decode-color
(sm/decoder ctcl/schema:library-color sm/json-transformer)) (sm/decoder ctcl/schema:library-color sm/json-transformer))
@@ -148,19 +149,19 @@
(sm/decoder schema:file sm/json-transformer)) (sm/decoder schema:file sm/json-transformer))
(def decode-page (def decode-page
(sm/decoder ::ctp/page sm/json-transformer)) (sm/decoder ctp/schema:page sm/json-transformer))
(def decode-shape (def decode-shape
(sm/decoder ::cts/shape sm/json-transformer)) (sm/decoder cts/schema:shape sm/json-transformer))
(def decode-typography (def decode-typography
(sm/decoder ::cty/typography sm/json-transformer)) (sm/decoder cty/schema:typography sm/json-transformer))
(def decode-tokens-lib (def decode-tokens-lib
(sm/decoder cto/schema:tokens-lib sm/json-transformer)) (sm/decoder ctob/schema:tokens-lib sm/json-transformer))
(def decode-plugin-data (def decode-plugin-data
(sm/decoder ::ctpg/plugin-data sm/json-transformer)) (sm/decoder ctpg/schema:plugin-data sm/json-transformer))
(def decode-storage-object (def decode-storage-object
(sm/decoder schema:storage-object sm/json-transformer)) (sm/decoder schema:storage-object sm/json-transformer))
@@ -174,31 +175,31 @@
(sm/check-fn schema:manifest)) (sm/check-fn schema:manifest))
(def validate-file (def validate-file
(sm/check-fn ::ctf/file)) (sm/check-fn ctf/schema:file))
(def validate-page (def validate-page
(sm/check-fn ::ctp/page)) (sm/check-fn ctp/schema:page))
(def validate-shape (def validate-shape
(sm/check-fn ::cts/shape)) (sm/check-fn cts/schema:shape))
(def validate-media (def validate-media
(sm/check-fn ::ctf/media)) (sm/check-fn ctf/schema:media))
(def validate-color (def validate-color
(sm/check-fn ctcl/schema:library-color)) (sm/check-fn ctcl/schema:library-color))
(def validate-component (def validate-component
(sm/check-fn ::ctc/component)) (sm/check-fn ctc/schema:component))
(def validate-typography (def validate-typography
(sm/check-fn ::cty/typography)) (sm/check-fn cty/schema:typography))
(def validate-tokens-lib (def validate-tokens-lib
(sm/check-fn ::cto/tokens-lib)) (sm/check-fn ctob/schema:tokens-lib))
(def validate-plugin-data (def validate-plugin-data
(sm/check-fn ::ctpg/plugin-data)) (sm/check-fn ctpg/schema:plugin-data))
(def validate-storage-object (def validate-storage-object
(sm/check-fn schema:storage-object)) (sm/check-fn schema:storage-object))
@@ -223,9 +224,12 @@
(throw (IllegalArgumentException. (throw (IllegalArgumentException.
"the `include-libraries` and `embed-assets` are mutally excluding options"))) "the `include-libraries` and `embed-assets` are mutally excluding options")))
(let [detach? (and (not embed-assets) (not include-libraries))] (let [detach? (and (not embed-assets) (not include-libraries))]
(db/tx-run! cfg (fn [cfg] (db/tx-run! cfg (fn [cfg]
(cond-> (bfc/get-file cfg file-id {::sql/for-update true}) (cond-> (bfc/get-file cfg file-id
{:realize? true
:include-deleted? true
:lock-for-update? true})
detach? detach?
(-> (ctf/detach-external-references file-id) (-> (ctf/detach-external-references file-id)
(dissoc :libraries)) (dissoc :libraries))
@@ -251,10 +255,12 @@
(write-entry! output path params) (write-entry! output path params)
(events/tap :progress {:section :storage-object :id id})
(with-open [input (sto/get-object-data storage sobject)] (with-open [input (sto/get-object-data storage sobject)]
(.putNextEntry output (ZipEntry. (str "objects/" id ext))) (.putNextEntry ^ZipOutputStream output (ZipEntry. (str "objects/" id ext)))
(io/copy input output :size (:size sobject)) (io/copy input output :size (:size sobject))
(.closeEntry output)))))) (.closeEntry ^ZipOutputStream output))))))
(defn- export-file (defn- export-file
[{:keys [::file-id ::output] :as cfg}] [{:keys [::file-id ::output] :as cfg}]
@@ -275,6 +281,8 @@
thumbnails (bfc/get-file-object-thumbnails cfg file-id)] thumbnails (bfc/get-file-object-thumbnails cfg file-id)]
(events/tap :progress {:section :file :id file-id})
(vswap! bfc/*state* update :files assoc file-id (vswap! bfc/*state* update :files assoc file-id
{:id file-id {:id file-id
:name (:name file) :name (:name file)
@@ -282,13 +290,13 @@
(let [file (cond-> (select-keys file bfc/file-attrs) (let [file (cond-> (select-keys file bfc/file-attrs)
(:options data) (:options data)
(assoc :options (:options data)) (assoc :options (:options data)))
:always file (-> file
(dissoc :data) (dissoc :data)
(dissoc :deleted-at)
(encode-file))
:always
(encode-file))
path (str "files/" file-id ".json")] path (str "files/" file-id ".json")]
(write-entry! output path file)) (write-entry! output path file))
@@ -346,7 +354,8 @@
typography (encode-typography object)] typography (encode-typography object)]
(write-entry! output path typography))) (write-entry! output path typography)))
(when tokens-lib (when (and tokens-lib
(not (ctob/empty-lib? tokens-lib)))
(let [path (str "files/" file-id "/tokens.json") (let [path (str "files/" file-id "/tokens.json")
encoded-tokens (encode-tokens-lib tokens-lib)] encoded-tokens (encode-tokens-lib tokens-lib)]
(write-entry! output path encoded-tokens))))) (write-entry! output path encoded-tokens)))))
@@ -373,6 +382,7 @@
params {:type "penpot/export-files" params {:type "penpot/export-files"
:version 1 :version 1
:generated-by (str "penpot/" (:full cf/version)) :generated-by (str "penpot/" (:full cf/version))
:refer "penpot"
:files (vec (vals files)) :files (vec (vals files))
:relations rels}] :relations rels}]
(write-entry! output "manifest.json" params)))) (write-entry! output "manifest.json" params))))
@@ -445,7 +455,7 @@
(defn- read-manifest (defn- read-manifest
[^ZipFile input] [^ZipFile input]
(let [entry (get-zip-entry input "manifest.json")] (let [entry (get-zip-entry input "manifest.json")]
(with-open [reader (zip-entry-reader input entry)] (with-open [^AutoCloseable reader (zip-entry-reader input entry)]
(let [manifest (json/read reader :key-fn json/read-kebab-key)] (let [manifest (json/read reader :key-fn json/read-kebab-key)]
(decode-manifest manifest))))) (decode-manifest manifest)))))
@@ -535,24 +545,27 @@
(defn- read-entry (defn- read-entry
[^ZipFile input entry] [^ZipFile input entry]
(with-open [reader (zip-entry-reader input entry)] (with-open [^AutoCloseable reader (zip-entry-reader input entry)]
(json/read reader :key-fn json/read-kebab-key))) (json/read reader :key-fn json/read-kebab-key)))
(defn- read-plain-entry (defn- read-plain-entry
[^ZipFile input entry] [^ZipFile input entry]
(with-open [reader (zip-entry-reader input entry)] (with-open [^AutoCloseable reader (zip-entry-reader input entry)]
(json/read reader))) (json/read reader)))
(defn- read-file (defn- read-file
[{:keys [::bfc/input ::file-id]}] [{:keys [::bfc/input ::bfc/timestamp]} file-id]
(let [path (str "files/" file-id ".json") (let [path (str "files/" file-id ".json")
entry (get-zip-entry input path)] entry (get-zip-entry input path)]
(-> (read-entry input entry) (-> (read-entry input entry)
(decode-file) (decode-file)
(update :revn d/nilv 1)
(update :created-at d/nilv timestamp)
(update :modified-at d/nilv timestamp)
(validate-file)))) (validate-file))))
(defn- read-file-plugin-data (defn- read-file-plugin-data
[{:keys [::bfc/input ::file-id]}] [{:keys [::bfc/input]} file-id]
(let [path (str "files/" file-id "/plugin-data.json") (let [path (str "files/" file-id "/plugin-data.json")
entry (get-zip-entry* input path)] entry (get-zip-entry* input path)]
(some->> entry (some->> entry
@@ -561,7 +574,7 @@
(validate-plugin-data)))) (validate-plugin-data))))
(defn- read-file-media (defn- read-file-media
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::entries]} file-id]
(->> (keep (match-media-entry-fn file-id) entries) (->> (keep (match-media-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}] (reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry) (let [object (->> (read-entry input entry)
@@ -581,7 +594,7 @@
(not-empty))) (not-empty)))
(defn- read-file-colors (defn- read-file-colors
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::entries]} file-id]
(->> (keep (match-color-entry-fn file-id) entries) (->> (keep (match-color-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}] (reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry) (let [object (->> (read-entry input entry)
@@ -594,7 +607,7 @@
(not-empty))) (not-empty)))
(defn- read-file-components (defn- read-file-components
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::entries]} file-id]
(let [clean-component-post-decode (let [clean-component-post-decode
(fn [component] (fn [component]
(d/update-when component :objects (d/update-when component :objects
@@ -625,7 +638,7 @@
(not-empty)))) (not-empty))))
(defn- read-file-typographies (defn- read-file-typographies
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::entries]} file-id]
(->> (keep (match-typography-entry-fn file-id) entries) (->> (keep (match-typography-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}] (reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry) (let [object (->> (read-entry input entry)
@@ -638,14 +651,14 @@
(not-empty))) (not-empty)))
(defn- read-file-tokens-lib (defn- read-file-tokens-lib
[{:keys [::bfc/input ::file-id ::entries]}] [{:keys [::bfc/input ::entries]} file-id]
(when-let [entry (d/seek (match-tokens-lib-entry-fn file-id) entries)] (when-let [entry (d/seek (match-tokens-lib-entry-fn file-id) entries)]
(->> (read-plain-entry input entry) (->> (read-plain-entry input entry)
(decode-tokens-lib) (decode-tokens-lib)
(validate-tokens-lib)))) (validate-tokens-lib))))
(defn- read-file-shapes (defn- read-file-shapes
[{:keys [::bfc/input ::file-id ::page-id ::entries] :as cfg}] [{:keys [::bfc/input ::entries] :as cfg} file-id page-id]
(->> (keep (match-shape-entry-fn file-id page-id) entries) (->> (keep (match-shape-entry-fn file-id page-id) entries)
(reduce (fn [result {:keys [id entry]}] (reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry) (let [object (->> (read-entry input entry)
@@ -659,15 +672,14 @@
(not-empty))) (not-empty)))
(defn- read-file-pages (defn- read-file-pages
[{:keys [::bfc/input ::file-id ::entries] :as cfg}] [{:keys [::bfc/input ::entries] :as cfg} file-id]
(->> (keep (match-page-entry-fn file-id) entries) (->> (keep (match-page-entry-fn file-id) entries)
(keep (fn [{:keys [id entry]}] (keep (fn [{:keys [id entry]}]
(let [page (->> (read-entry input entry) (let [page (->> (read-entry input entry)
(decode-page)) (decode-page))
page (dissoc page :options)] page (dissoc page :options)]
(when (= id (:id page)) (when (= id (:id page))
(let [objects (-> (assoc cfg ::page-id id) (let [objects (read-file-shapes cfg file-id id)]
(read-file-shapes))]
(assoc page :objects objects)))))) (assoc page :objects objects))))))
(sort-by :index) (sort-by :index)
(reduce (fn [result {:keys [id] :as page}] (reduce (fn [result {:keys [id] :as page}]
@@ -675,7 +687,7 @@
(d/ordered-map)))) (d/ordered-map))))
(defn- read-file-thumbnails (defn- read-file-thumbnails
[{:keys [::bfc/input ::file-id ::entries] :as cfg}] [{:keys [::bfc/input ::entries] :as cfg} file-id]
(->> (keep (match-thumbnail-entry-fn file-id) entries) (->> (keep (match-thumbnail-entry-fn file-id) entries)
(reduce (fn [result {:keys [page-id frame-id tag entry]}] (reduce (fn [result {:keys [page-id frame-id tag entry]}]
(let [object (->> (read-entry input entry) (let [object (->> (read-entry input entry)
@@ -690,13 +702,13 @@
(not-empty))) (not-empty)))
(defn- read-file-data (defn- read-file-data
[cfg] [cfg file-id]
(let [colors (read-file-colors cfg) (let [colors (read-file-colors cfg file-id)
typographies (read-file-typographies cfg) typographies (read-file-typographies cfg file-id)
tokens-lib (read-file-tokens-lib cfg) tokens-lib (read-file-tokens-lib cfg file-id)
components (read-file-components cfg) components (read-file-components cfg file-id)
plugin-data (read-file-plugin-data cfg) plugin-data (read-file-plugin-data cfg file-id)
pages (read-file-pages cfg)] pages (read-file-pages cfg file-id)]
{:pages (-> pages keys vec) {:pages (-> pages keys vec)
:pages-index (into {} pages) :pages-index (into {} pages)
:colors colors :colors colors
@@ -706,11 +718,11 @@
:plugin-data plugin-data})) :plugin-data plugin-data}))
(defn- import-file (defn- import-file
[{:keys [::bfc/project-id ::file-id ::file-name] :as cfg}] [{:keys [::db/conn ::bfc/project-id] :as cfg} {file-id :id file-name :name}]
(let [file-id' (bfc/lookup-index file-id) (let [file-id' (bfc/lookup-index file-id)
file (read-file cfg) file (read-file cfg file-id)
media (read-file-media cfg) media (read-file-media cfg file-id)
thumbnails (read-file-thumbnails cfg)] thumbnails (read-file-thumbnails cfg file-id)]
(l/dbg :hint "processing file" (l/dbg :hint "processing file"
:id (str file-id') :id (str file-id')
@@ -719,28 +731,50 @@
:version (:version file) :version (:version file)
::l/sync? true) ::l/sync? true)
(events/tap :progress {:section :file :name file-name}) (vswap! bfc/*state* update :index bfc/update-index media :id)
(when media (events/tap :progress {:section :media :file-id file-id})
;; Update index with media
(l/dbg :hint "update media index"
:file-id (str file-id')
:total (count media)
::l/sync? true)
(vswap! bfc/*state* update :index bfc/update-index (map :id media)) (doseq [item media]
(vswap! bfc/*state* update :media into media)) (let [params (-> item
(update :id bfc/lookup-index)
(assoc :file-id file-id')
(d/update-when :media-id bfc/lookup-index)
(d/update-when :thumbnail-id bfc/lookup-index))]
(when thumbnails (l/dbg :hint "inserting media object"
(l/dbg :hint "update thumbnails index" :file-id (str file-id')
:file-id (str file-id') :id (str (:id params))
:total (count thumbnails) :media-id (str (:media-id params))
::l/sync? true) :thumbnail-id (str (:thumbnail-id params))
:old-id (str (:id item))
::l/sync? true)
(vswap! bfc/*state* update :index bfc/update-index (map :media-id thumbnails)) (db/insert! conn :file-media-object params
(vswap! bfc/*state* update :thumbnails into thumbnails)) ::db/on-conflict-do-nothing? (::bfc/overwrite cfg))))
(let [data (-> (read-file-data cfg) (events/tap :progress {:section :thumbnails :file-id file-id})
(doseq [item thumbnails]
(let [media-id (bfc/lookup-index (:media-id item))
object-id (-> (assoc item :file-id file-id')
(cth/fmt-object-id))
params {:file-id file-id'
:object-id object-id
:tag (:tag item)
:media-id media-id}]
(l/dbg :hint "inserting object thumbnail"
:file-id (str file-id')
:media-id (str media-id)
::l/sync? true)
(db/insert! conn :file-tagged-object-thumbnail params
::db/on-conflict-do-nothing? true)))
(events/tap :progress {:section :file :file-id file-id})
(let [data (-> (read-file-data cfg file-id)
(d/without-nils) (d/without-nils)
(assoc :id file-id') (assoc :id file-id')
(cond-> (:options file) (cond-> (:options file)
@@ -757,7 +791,7 @@
file (ctf/check-file file)] file (ctf/check-file file)]
(bfm/register-pending-migrations! cfg file) (bfm/register-pending-migrations! cfg file)
(bfc/save-file! cfg file ::db/return-keys false) (bfc/save-file! cfg file)
file-id'))) file-id')))
@@ -787,108 +821,111 @@
entries (keep (match-storage-entry-fn) entries)] entries (keep (match-storage-entry-fn) entries)]
(doseq [{:keys [id entry]} entries] (doseq [{:keys [id entry]} entries]
(let [object (->> (read-entry input entry) (let [object (-> (read-entry input entry)
(decode-storage-object) (decode-storage-object)
(validate-storage-object))] (update :bucket d/nilv sto/default-bucket)
(validate-storage-object))
(when (not= id (:id object)) ext (cmedia/mtype->extension (:content-type object))
path (str "objects/" id ext)
content (->> path
(get-zip-entry input)
(zip-entry-storage-content input))]
(when (not= (:size object) (sto/get-size content))
(ex/raise :type :validation (ex/raise :type :validation
:code :inconsistent-penpot-file :code :inconsistent-penpot-file
:hint "the penpot file seems corrupt, found unexpected uuid (storage-object-id)" :hint "found corrupted storage object: size does not match"
:expected-id (str id) :path path
:found-id (str (:id object)))) :expected-size (:size object)
:found-size (sto/get-size content)))
(let [ext (cmedia/mtype->extension (:content-type object)) (when-let [hash (get object :hash)]
path (str "objects/" id ext) (when (not= hash (sto/get-hash content))
content (->> path
(get-zip-entry input)
(zip-entry-storage-content input))]
(when (not= (:size object) (sto/get-size content))
(ex/raise :type :validation (ex/raise :type :validation
:code :inconsistent-penpot-file :code :inconsistent-penpot-file
:hint "found corrupted storage object: size does not match" :hint "found corrupted storage object: hash does not match"
:path path :path path
:expected-size (:size object) :expected-hash (:hash object)
:found-size (sto/get-size content))) :found-hash (sto/get-hash content))))
(when-let [hash (get object :hash)] (let [params (-> object
(when (not= hash (sto/get-hash content)) (dissoc :id :size)
(ex/raise :type :validation (assoc ::sto/content content)
:code :inconsistent-penpot-file (assoc ::sto/deduplicate? true)
:hint "found corrupted storage object: hash does not match" (assoc ::sto/touched-at timestamp))
:path path sobject (sto/put-object! storage params)]
:expected-hash (:hash object)
:found-hash (sto/get-hash content))))
(let [params (-> object (l/dbg :hint "persisted storage object"
(dissoc :id :size) :id (str (:id sobject))
(assoc ::sto/content content) :prev-id (str id)
(assoc ::sto/deduplicate? true) :bucket (:bucket params)
(assoc ::sto/touched-at timestamp)) ::l/sync? true)
sobject (sto/put-object! storage params)]
(l/dbg :hint "persisted storage object" (vswap! bfc/*state* update :index assoc id (:id sobject)))))))
:id (str (:id sobject))
:prev-id (str id)
:bucket (:bucket params)
::l/sync? true)
(vswap! bfc/*state* update :index assoc id (:id sobject)))))))) (defn- import-files*
[{:keys [::manifest] :as cfg}]
(bfc/disable-database-timeouts! cfg)
(defn- import-file-media (vswap! bfc/*state* update :index bfc/update-index (:files manifest) :id)
[{:keys [::db/conn] :as cfg}]
(events/tap :progress {:section :media})
(doseq [item (:media @bfc/*state*)] (import-storage-objects cfg)
(let [params (-> item
(update :id bfc/lookup-index)
(update :file-id bfc/lookup-index)
(d/update-when :media-id bfc/lookup-index)
(d/update-when :thumbnail-id bfc/lookup-index))]
(l/dbg :hint "inserting file media object" (let [files (get manifest :files)
:old-id (str (:id item)) result (reduce (fn [result file]
:id (str (:id params)) (let [name' (get file :name)
:file-id (str (:file-id params)) file (assoc file :name name')]
::l/sync? true) (conj result (import-file cfg file))))
[]
files)]
(db/insert! conn :file-media-object params)))) (import-file-relations cfg)
(bfm/apply-pending-migrations! cfg)
(defn- import-file-thumbnails result))
[{:keys [::db/conn] :as cfg}]
(events/tap :progress {:section :thumbnails})
(doseq [item (:thumbnails @bfc/*state*)]
(let [file-id (bfc/lookup-index (:file-id item))
media-id (bfc/lookup-index (:media-id item))
object-id (-> (assoc item :file-id file-id)
(cth/fmt-object-id))
params {:file-id file-id
:object-id object-id
:tag (:tag item)
:media-id media-id}]
(l/dbg :hint "inserting file object thumbnail" (defn- import-file-and-overwrite*
:file-id (str file-id) [{:keys [::manifest ::bfc/file-id] :as cfg}]
:media-id (str media-id)
::l/sync? true)
(db/insert! conn :file-tagged-object-thumbnail params)))) (when (not= 1 (count (:files manifest)))
(ex/raise :type :validation
:code :invalid-condition
:hint "unable to perform in-place update with binfile containing more than 1 file"
:manifest manifest))
(bfc/disable-database-timeouts! cfg)
(let [ref-file (bfc/get-minimal-file cfg file-id ::db/for-update true)
file (first (get manifest :files))
cfg (assoc cfg ::bfc/overwrite true)]
(vswap! bfc/*state* update :index assoc (:id file) file-id)
(binding [bfc/*options* cfg
bfc/*reference-file* ref-file]
(import-storage-objects cfg)
(import-file cfg file)
(bfc/invalidate-thumbnails cfg file-id)
(bfm/apply-pending-migrations! cfg)
[file-id])))
(defn- import-files (defn- import-files
[{:keys [::bfc/timestamp ::bfc/input ::bfc/name] :or {timestamp (dt/now)} :as cfg}] [{:keys [::bfc/timestamp ::bfc/input] :or {timestamp (ct/now)} :as cfg}]
(dm/assert! (assert (instance? ZipFile input) "expected zip file")
"expected zip file" (assert (ct/inst? timestamp) "expected valid instant")
(instance? ZipFile input))
(dm/assert!
"expected valid instant"
(dt/instant? timestamp))
(let [manifest (-> (read-manifest input) (let [manifest (-> (read-manifest input)
(validate-manifest)) (validate-manifest))
entries (read-zip-entries input)] entries (read-zip-entries input)
cfg (-> cfg
(assoc ::entries entries)
(assoc ::manifest manifest)
(assoc ::bfc/timestamp timestamp))]
(when-not (= "penpot/export-files" (:type manifest)) (when-not (= "penpot/export-files" (:type manifest))
(ex/raise :type :validation (ex/raise :type :validation
@@ -911,35 +948,10 @@
(events/tap :progress {:section :manifest}) (events/tap :progress {:section :manifest})
(let [index (bfc/update-index (map :id (:files manifest))) (binding [bfc/*state* (volatile! {:media [] :index {}})]
state {:media [] :index index} (if (::bfc/file-id cfg)
cfg (-> cfg (db/tx-run! cfg import-file-and-overwrite*)
(assoc ::entries entries) (db/tx-run! cfg import-files*)))))
(assoc ::manifest manifest)
(assoc ::bfc/timestamp timestamp))]
(binding [bfc/*state* (volatile! state)]
(db/tx-run! cfg (fn [cfg]
(bfc/disable-database-timeouts! cfg)
(let [ids (->> (:files manifest)
(reduce (fn [result {:keys [id] :as file}]
(let [name' (get file :name)
name' (if (map? name)
(get name id)
name')]
(conj result (-> cfg
(assoc ::file-id id)
(assoc ::file-name name')
(import-file)))))
[]))]
(import-file-relations cfg)
(import-storage-objects cfg)
(import-file-media cfg)
(import-file-thumbnails cfg)
(bfm/apply-pending-migrations! cfg)
ids)))))))
;; --- PUBLIC API ;; --- PUBLIC API
@@ -956,24 +968,23 @@
[{:keys [::bfc/ids] :as cfg} output] [{:keys [::bfc/ids] :as cfg} output]
(dm/assert! (assert
"expected a set of uuid's for `::bfc/ids` parameter" (and (set? ids) (every? uuid? ids))
(and (set? ids) "expected a set of uuid's for `::bfc/ids` parameter")
(every? uuid? ids)))
(dm/assert! (assert
"expected instance of jio/IOFactory for `input`" (satisfies? jio/IOFactory output)
(satisfies? jio/IOFactory output)) "expected instance of jio/IOFactory for `input`")
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
ab (volatile! false) ab (volatile! false)
cs (volatile! nil)] cs (volatile! nil)]
(try (try
(l/info :hint "start exportation" :export-id (str id)) (l/info :hint "start exportation" :export-id (str id))
(binding [bfc/*state* (volatile! (bfc/initial-state))] (binding [bfc/*state* (volatile! (bfc/initial-state))]
(with-open [output (io/output-stream output)] (with-open [^AutoCloseable output (io/output-stream output)]
(with-open [output (ZipOutputStream. output)] (with-open [^AutoCloseable output (ZipOutputStream. output)]
(let [cfg (assoc cfg ::output output)] (let [cfg (assoc cfg ::output output)]
(export-files cfg) (export-files cfg)
(export-storage-objects cfg))))) (export-storage-objects cfg)))))
@@ -1002,22 +1013,22 @@
(defn import-files! (defn import-files!
[{:keys [::bfc/input] :as cfg}] [{:keys [::bfc/input] :as cfg}]
(dm/assert! (assert
"expected valid profile-id and project-id on `cfg`"
(and (uuid? (::bfc/profile-id cfg)) (and (uuid? (::bfc/profile-id cfg))
(uuid? (::bfc/project-id cfg)))) (uuid? (::bfc/project-id cfg)))
"expected valid profile-id and project-id on `cfg`")
(dm/assert! (assert
"expected instance of jio/IOFactory for `input`" (io/coercible? input)
(io/coercible? input)) "expected instance of jio/IOFactory for `input`")
(let [id (uuid/next) (let [id (uuid/next)
tp (dt/tpoint) tp (ct/tpoint)
cs (volatile! nil)] cs (volatile! nil)]
(l/info :hint "import: started" :id (str id)) (l/info :hint "import: started" :id (str id))
(try (try
(with-open [input (ZipFile. (fs/file input))] (with-open [input (ZipFile. ^File (fs/file input))]
(import-files (assoc cfg ::bfc/input input))) (import-files (assoc cfg ::bfc/input input)))
(catch Throwable cause (catch Throwable cause
@@ -1027,5 +1038,11 @@
(finally (finally
(l/info :hint "import: terminated" (l/info :hint "import: terminated"
:id (str id) :id (str id)
:elapsed (dt/format-duration (tp)) :elapsed (ct/format-duration (tp))
:error? (some? @cs)))))) :error? (some? @cs))))))
(defn get-manifest
[path]
(with-open [^AutoCloseable input (ZipFile. ^File (fs/file path))]
(-> (read-manifest input)
(validate-manifest))))
+37 -19
View File
@@ -5,17 +5,16 @@
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.config (ns app.config
"A configuration management."
(:refer-clojure :exclude [get]) (:refer-clojure :exclude [get])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.flags :as flags] [app.common.flags :as flags]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u] [app.common.uri :as u]
[app.common.version :as v] [app.common.version :as v]
[app.util.overrides] [app.util.overrides]
[app.util.time :as dt]
[clojure.core :as c] [clojure.core :as c]
[clojure.java.io :as io] [clojure.java.io :as io]
[cuerdas.core :as str] [cuerdas.core :as str]
@@ -47,22 +46,27 @@
:auto-file-snapshot-timeout "3h" :auto-file-snapshot-timeout "3h"
:public-uri "http://localhost:3449" :public-uri "http://localhost:3449"
:host "localhost" :host "localhost"
:tenant "default" :tenant "default"
:redis-uri "redis://redis/0" :redis-uri "redis://redis/0"
:file-data-backend "legacy-db"
:objects-storage-backend "fs" :objects-storage-backend "fs"
:objects-storage-fs-directory "assets" :objects-storage-fs-directory "assets"
:auth-token-cookie-name "auth-token"
:assets-path "/internal/assets/" :assets-path "/internal/assets/"
:smtp-default-reply-to "Penpot <no-reply@example.com>" :smtp-default-reply-to "Penpot <no-reply@example.com>"
:smtp-default-from "Penpot <no-reply@example.com>" :smtp-default-from "Penpot <no-reply@example.com>"
:profile-complaint-max-age (dt/duration {:days 7}) :profile-complaint-max-age (ct/duration {:days 7})
:profile-complaint-threshold 2 :profile-complaint-threshold 2
:profile-bounce-max-age (dt/duration {:days 7}) :profile-bounce-max-age (ct/duration {:days 7})
:profile-bounce-threshold 10 :profile-bounce-threshold 10
:telemetry-uri "https://telemetry.penpot.app/" :telemetry-uri "https://telemetry.penpot.app/"
@@ -88,7 +92,7 @@
[:secret-key {:optional true} :string] [:secret-key {:optional true} :string]
[:tenant {:optional false} :string] [:tenant {:optional false} :string]
[:public-uri {:optional false} :string] [:public-uri {:optional false} ::sm/uri]
[:host {:optional false} :string] [:host {:optional false} :string]
[:http-server-port {:optional true} ::sm/int] [:http-server-port {:optional true} ::sm/int]
@@ -96,16 +100,19 @@
[:http-server-max-body-size {:optional true} ::sm/int] [:http-server-max-body-size {:optional true} ::sm/int]
[:http-server-max-multipart-body-size {:optional true} ::sm/int] [:http-server-max-multipart-body-size {:optional true} ::sm/int]
[:http-server-io-threads {:optional true} ::sm/int] [:http-server-io-threads {:optional true} ::sm/int]
[:http-server-worker-threads {:optional true} ::sm/int] [:http-server-max-worker-threads {:optional true} ::sm/int]
[:management-api-key {:optional true} :string]
[:telemetry-uri {:optional true} :string] [:telemetry-uri {:optional true} :string]
[:telemetry-with-taiga {:optional true} ::sm/boolean] ;; DELETE [:telemetry-with-taiga {:optional true} ::sm/boolean] ;; DELETE
[:auto-file-snapshot-every {:optional true} ::sm/int] [:auto-file-snapshot-every {:optional true} ::sm/int]
[:auto-file-snapshot-timeout {:optional true} ::dt/duration] [:auto-file-snapshot-timeout {:optional true} ::ct/duration]
[:media-max-file-size {:optional true} ::sm/int] [:media-max-file-size {:optional true} ::sm/int]
[:deletion-delay {:optional true} ::dt/duration] ;; REVIEW [:deletion-delay {:optional true} ::ct/duration]
[:file-clean-delay {:optional true} ::ct/duration]
[:telemetry-enabled {:optional true} ::sm/boolean] [:telemetry-enabled {:optional true} ::sm/boolean]
[:default-blob-version {:optional true} ::sm/int] [:default-blob-version {:optional true} ::sm/int]
[:allow-demo-users {:optional true} ::sm/boolean] [:allow-demo-users {:optional true} ::sm/boolean]
@@ -146,12 +153,11 @@
[:quotes-team-access-requests-per-team {:optional true} ::sm/int] [:quotes-team-access-requests-per-team {:optional true} ::sm/int]
[:quotes-team-access-requests-per-requester {:optional true} ::sm/int] [:quotes-team-access-requests-per-requester {:optional true} ::sm/int]
[:auth-data-cookie-domain {:optional true} :string]
[:auth-token-cookie-name {:optional true} :string] [:auth-token-cookie-name {:optional true} :string]
[:auth-token-cookie-max-age {:optional true} ::dt/duration] [:auth-token-cookie-max-age {:optional true} ::ct/duration]
[:registration-domain-whitelist {:optional true} [::sm/set :string]] [:registration-domain-whitelist {:optional true} [::sm/set :string]]
[:email-verify-threshold {:optional true} ::dt/duration] [:email-verify-threshold {:optional true} ::ct/duration]
[:github-client-id {:optional true} :string] [:github-client-id {:optional true} :string]
[:github-client-secret {:optional true} :string] [:github-client-secret {:optional true} :string]
@@ -161,7 +167,7 @@
[:google-client-id {:optional true} :string] [:google-client-id {:optional true} :string]
[:google-client-secret {:optional true} :string] [:google-client-secret {:optional true} :string]
[:oidc-client-id {:optional true} :string] [:oidc-client-id {:optional true} :string]
[:oidc-user-info-source {:optional true} :keyword] [:oidc-user-info-source {:optional true} [:enum "auto" "userinfo" "token"]]
[:oidc-client-secret {:optional true} :string] [:oidc-client-secret {:optional true} :string]
[:oidc-base-uri {:optional true} :string] [:oidc-base-uri {:optional true} :string]
[:oidc-token-uri {:optional true} :string] [:oidc-token-uri {:optional true} :string]
@@ -186,9 +192,9 @@
[:ldap-starttls {:optional true} ::sm/boolean] [:ldap-starttls {:optional true} ::sm/boolean]
[:ldap-user-query {:optional true} :string] [:ldap-user-query {:optional true} :string]
[:profile-bounce-max-age {:optional true} ::dt/duration] [:profile-bounce-max-age {:optional true} ::ct/duration]
[:profile-bounce-threshold {:optional true} ::sm/int] [:profile-bounce-threshold {:optional true} ::sm/int]
[:profile-complaint-max-age {:optional true} ::dt/duration] [:profile-complaint-max-age {:optional true} ::ct/duration]
[:profile-complaint-threshold {:optional true} ::sm/int] [:profile-complaint-threshold {:optional true} ::sm/int]
[:redis-uri {:optional true} ::sm/uri] [:redis-uri {:optional true} ::sm/uri]
@@ -210,24 +216,27 @@
[:prepl-host {:optional true} :string] [:prepl-host {:optional true} :string]
[:prepl-port {:optional true} ::sm/int] [:prepl-port {:optional true} ::sm/int]
[:file-data-backend {:optional true} [:enum "db" "legacy-db" "storage"]]
[:media-directory {:optional true} :string] ;; REVIEW [:media-directory {:optional true} :string] ;; REVIEW
[:media-uri {:optional true} :string] [:media-uri {:optional true} :string]
[:assets-path {:optional true} :string] [:assets-path {:optional true} :string]
;; Legacy, will be removed in 2.5 [:netty-io-threads {:optional true} ::sm/int]
[:executor-threads {:optional true} ::sm/int]
;; DEPRECATED
[:assets-storage-backend {:optional true} :keyword] [:assets-storage-backend {:optional true} :keyword]
[:storage-assets-fs-directory {:optional true} :string] [:storage-assets-fs-directory {:optional true} :string]
[:storage-assets-s3-bucket {:optional true} :string] [:storage-assets-s3-bucket {:optional true} :string]
[:storage-assets-s3-region {:optional true} :keyword] [:storage-assets-s3-region {:optional true} :keyword]
[:storage-assets-s3-endpoint {:optional true} ::sm/uri] [:storage-assets-s3-endpoint {:optional true} ::sm/uri]
[:storage-assets-s3-io-threads {:optional true} ::sm/int]
[:objects-storage-backend {:optional true} :keyword] [:objects-storage-backend {:optional true} :keyword]
[:objects-storage-fs-directory {:optional true} :string] [:objects-storage-fs-directory {:optional true} :string]
[:objects-storage-s3-bucket {:optional true} :string] [:objects-storage-s3-bucket {:optional true} :string]
[:objects-storage-s3-region {:optional true} :keyword] [:objects-storage-s3-region {:optional true} :keyword]
[:objects-storage-s3-endpoint {:optional true} ::sm/uri] [:objects-storage-s3-endpoint {:optional true} ::sm/uri]]))
[:objects-storage-s3-io-threads {:optional true} ::sm/int]]))
(defn- parse-flags (defn- parse-flags
[config] [config]
@@ -298,7 +307,12 @@
(defn get-deletion-delay (defn get-deletion-delay
[] []
(or (c/get config :deletion-delay) (or (c/get config :deletion-delay)
(dt/duration {:days 7}))) (ct/duration {:days 7})))
(defn get-file-clean-delay
[]
(or (c/get config :file-clean-delay)
(ct/duration {:days 2})))
(defn get (defn get
"A configuration getter. Helps code be more testable." "A configuration getter. Helps code be more testable."
@@ -307,5 +321,9 @@
([key default] ([key default]
(c/get config key default))) (c/get config key default)))
(defn logging-context
[]
{:version/backend (:full version)})
;; Set value for all new threads bindings. ;; Set value for all new threads bindings.
(alter-var-root #'*assert* (constantly (contains? flags :backend-asserts))) (alter-var-root #'*assert* (constantly (contains? flags :backend-asserts)))
+39 -11
View File
@@ -10,19 +10,20 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.json :as json]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db.sql :as sql] [app.db.sql :as sql]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.util.json :as json]
[app.util.time :as dt]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.set :as set] [clojure.set :as set]
[integrant.core :as ig] [integrant.core :as ig]
[next.jdbc :as jdbc] [next.jdbc :as jdbc]
[next.jdbc.date-time :as jdbc-dt] [next.jdbc.date-time :as jdbc-dt]
[next.jdbc.prepare :as jdbc.prepare]
[next.jdbc.transaction]) [next.jdbc.transaction])
(:import (:import
com.zaxxer.hikari.HikariConfig com.zaxxer.hikari.HikariConfig
@@ -33,6 +34,7 @@
java.io.InputStream java.io.InputStream
java.io.OutputStream java.io.OutputStream
java.sql.Connection java.sql.Connection
java.sql.PreparedStatement
java.sql.Savepoint java.sql.Savepoint
org.postgresql.PGConnection org.postgresql.PGConnection
org.postgresql.geometric.PGpoint org.postgresql.geometric.PGpoint
@@ -296,7 +298,7 @@
(defn insert! (defn insert!
"A helper that builds an insert sql statement and executes it. By "A helper that builds an insert sql statement and executes it. By
default returns the inserted row with all the field; you can delimit default returns the inserted row with all the field; you can delimit
the returned columns with the `::columns` option." the returned columns with the `::sql/columns` option."
[ds table params & {:as opts}] [ds table params & {:as opts}]
(let [conn (get-connectable ds) (let [conn (get-connectable ds)
sql (sql/insert table params opts) sql (sql/insert table params opts)
@@ -377,9 +379,7 @@
(defn is-row-deleted? (defn is-row-deleted?
[{:keys [deleted-at]}] [{:keys [deleted-at]}]
(and (dt/instant? deleted-at) (some? deleted-at))
(< (inst-ms deleted-at)
(inst-ms (dt/now)))))
(defn get* (defn get*
"Retrieve a single row from database that matches a simple filters. Do "Retrieve a single row from database that matches a simple filters. Do
@@ -404,6 +404,23 @@
:hint "database object not found")) :hint "database object not found"))
row)) row))
(defn get-with-sql
[ds sql & {:as opts}]
(let [rows
(cond->> (exec! ds sql opts)
(::remove-deleted opts true)
(remove is-row-deleted?)
:always
(not-empty))]
(when (and (not rows) (::throw-if-not-exists opts true))
(ex/raise :type :not-found
:code :object-not-found
:hint "database object not found"))
(first rows)))
(def ^:private default-plan-opts (def ^:private default-plan-opts
(-> default-opts (-> default-opts
(assoc :fetch-size 1000) (assoc :fetch-size 1000)
@@ -558,10 +575,10 @@
[system f & params] [system f & params]
(cond (cond
(connection? system) (connection? system)
(run! {::conn system} f) (apply run! {::conn system} f params)
(pool? system) (pool? system)
(run! {::pool system} f) (apply run! {::pool system} f params)
(::conn system) (::conn system)
(apply f system params) (apply f system params)
@@ -585,7 +602,7 @@
(string? o) (string? o)
(pginterval o) (pginterval o)
(dt/duration? o) (ct/duration? o)
(interval (inst-ms o)) (interval (inst-ms o))
:else :else
@@ -599,7 +616,7 @@
val (.getValue o)] val (.getValue o)]
(if (or (= typ "json") (if (or (= typ "json")
(= typ "jsonb")) (= typ "jsonb"))
(json/decode val) (json/decode val :key-fn keyword)
val)))) val))))
(defn decode-transit-pgobject (defn decode-transit-pgobject
@@ -640,7 +657,7 @@
(when data (when data
(doto (org.postgresql.util.PGobject.) (doto (org.postgresql.util.PGobject.)
(.setType "jsonb") (.setType "jsonb")
(.setValue (json/encode-str data))))) (.setValue (json/encode data)))))
;; --- Locks ;; --- Locks
@@ -686,3 +703,14 @@
[cause] [cause]
(and (sql-exception? cause) (and (sql-exception? cause)
(= "40001" (.getSQLState ^java.sql.SQLException cause)))) (= "40001" (.getSQLState ^java.sql.SQLException cause))))
(defn duplicate-key-error?
[cause]
(and (sql-exception? cause)
(= "23505" (.getSQLState ^java.sql.SQLException cause))))
(extend-protocol jdbc.prepare/SettableParameter
clojure.lang.Keyword
(set-parameter [^clojure.lang.Keyword v ^PreparedStatement s ^long i]
(.setObject s i ^String (d/name v))))
+9 -2
View File
@@ -53,8 +53,15 @@
opts (cond-> opts opts (cond-> opts
(::order-by opts) (assoc :order-by (::order-by opts)) (::order-by opts) (assoc :order-by (::order-by opts))
(::columns opts) (assoc :columns (::columns opts)) (::columns opts) (assoc :columns (::columns opts))
(::for-update opts) (assoc :suffix "FOR UPDATE")
(::for-share opts) (assoc :suffix "FOR SHARE"))] (or (::db/for-update opts)
(::for-update opts))
(assoc :suffix "FOR UPDATE")
(or (::db/for-share opts)
(::for-share opts))
(assoc :suffix "FOR SHARE"))]
(sql/for-query table where-params opts)))) (sql/for-query table where-params opts))))
(defn update (defn update
+69 -53
View File
@@ -7,6 +7,7 @@
(ns app.email (ns app.email
"Main api for send emails." "Main api for send emails."
(:require (:require
[app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
@@ -93,36 +94,42 @@
headers))) headers)))
(defn- assign-body (defn- assign-body
[^MimeMessage mmsg {:keys [body charset] :or {charset "utf-8"}}] [^MimeMessage mmsg {:keys [body charset attachments] :or {charset "utf-8"}}]
(let [mpart (MimeMultipart. "mixed")] (let [mixed-mpart (MimeMultipart. "mixed")]
(cond (cond
(string? body) (string? body)
(let [bpart (MimeBodyPart.)] (let [text-part (MimeBodyPart.)]
(.setContent bpart ^String body (str "text/plain; charset=" charset)) (.setText text-part ^String body ^String charset)
(.addBodyPart mpart bpart)) (.addBodyPart mixed-mpart text-part))
(vector? body)
(let [mmp (MimeMultipart. "alternative")
mbp (MimeBodyPart.)]
(.addBodyPart mpart mbp)
(.setContent mbp mmp)
(doseq [item body]
(let [mbp (MimeBodyPart.)]
(.setContent mbp
^String (:content item)
^String (str (:type item "text/plain") "; charset=" charset))
(.addBodyPart mmp mbp))))
(map? body) (map? body)
(let [bpart (MimeBodyPart.)] (let [content-part (MimeBodyPart.)
(.setContent bpart alternative-mpart (MimeMultipart. "alternative")]
^String (:content body)
^String (str (:type body "text/plain") "; charset=" charset)) (when-let [content (get body "text/plain")]
(.addBodyPart mpart bpart)) (let [text-part (MimeBodyPart.)]
(.setText text-part ^String content ^String charset)
(.addBodyPart alternative-mpart text-part)))
(when-let [content (get body "text/html")]
(let [html-part (MimeBodyPart.)]
(.setContent html-part ^String content
(str "text/html; charset=" charset))
(.addBodyPart alternative-mpart html-part)))
(.setContent content-part alternative-mpart)
(.addBodyPart mixed-mpart content-part))
:else :else
(throw (ex-info "Unsupported type" {:body body}))) (throw (IllegalArgumentException. "invalid email body provided")))
(.setContent mmsg mpart)
(doseq [[name content] attachments]
(let [attachment-part (MimeBodyPart.)]
(.setFileName attachment-part ^String name)
(.setContent attachment-part ^String content (str "text/plain; charset=" charset))
(.addBodyPart mixed-mpart attachment-part)))
(.setContent mmsg mixed-mpart)
mmsg)) mmsg))
(defn- opts->props (defn- opts->props
@@ -210,24 +217,26 @@
(ex/raise :type :internal (ex/raise :type :internal
:code :missing-email-templates)) :code :missing-email-templates))
{:subject subj {:subject subj
:body (into :body (d/without-nils
[{:type "text/plain" {"text/plain" text
:content text}] "text/html" html})}))
(when html
[{:type "text/html"
:content html}]))}))
(def ^:private schema:context (def ^:private schema:params
[:map [:map {:title "Email Params"}
[:to [:or ::sm/email [::sm/vec ::sm/email]]] [:to [:or ::sm/email [::sm/vec ::sm/email]]]
[:reply-to {:optional true} ::sm/email] [:reply-to {:optional true} ::sm/email]
[:from {:optional true} ::sm/email] [:from {:optional true} ::sm/email]
[:lang {:optional true} ::sm/text] [:lang {:optional true} ::sm/text]
[:subject {:optional true} ::sm/text]
[:priority {:optional true} [:enum :high :low]] [:priority {:optional true} [:enum :high :low]]
[:extra-data {:optional true} ::sm/text]]) [:extra-data {:optional true} ::sm/text]
[:body {:optional true}
[:or :string [:map-of :string :string]]]
[:attachments {:optional true}
[:map-of :string :string]]])
(def ^:private check-context (def ^:private check-params
(sm/check-fn schema:context)) (sm/check-fn schema:params))
(defn template-factory (defn template-factory
[& {:keys [id schema]}] [& {:keys [id schema]}]
@@ -235,9 +244,9 @@
(let [check-fn (if schema (let [check-fn (if schema
(sm/check-fn schema) (sm/check-fn schema)
(constantly nil))] (constantly nil))]
(fn [context] (fn [params]
(let [context (-> context check-context check-fn) (let [params (-> params check-params check-fn)
email (build-email-template id context)] email (build-email-template id params)]
(when-not email (when-not email
(ex/raise :type :internal (ex/raise :type :internal
:code :email-template-does-not-exists :code :email-template-does-not-exists
@@ -245,35 +254,40 @@
:template-id id)) :template-id id))
(cond-> (assoc email :id (name id)) (cond-> (assoc email :id (name id))
(:extra-data context) (:extra-data params)
(assoc :extra-data (:extra-data context)) (assoc :extra-data (:extra-data params))
(:from context) (seq (:attachments params))
(assoc :from (:from context)) (assoc :attachments (:attachments params))
(:reply-to context) (:from params)
(assoc :reply-to (:reply-to context)) (assoc :from (:from params))
(:to context) (:reply-to params)
(assoc :to (:to context))))))) (assoc :reply-to (:reply-to params))
(:to params)
(assoc :to (:to params)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUBLIC HIGH-LEVEL API ;; PUBLIC HIGH-LEVEL API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn render (defn render
[email-factory context] [email-factory params]
(email-factory context)) (email-factory params))
(defn send! (defn send!
"Schedule an already defined email to be sent using asynchronously "Schedule an already defined email to be sent using asynchronously
using worker task." using worker task."
[{:keys [::conn ::factory] :as context}] [{:keys [::conn ::factory] :as params}]
(assert (db/connectable? conn) "expected a valid database connection or pool") (assert (db/connectable? conn) "expected a valid database connection or pool")
(let [email (if factory (let [email (if factory
(factory context) (factory params)
(dissoc context ::conn))] (-> params
(dissoc params)
(check-params)))]
(wrk/submit! {::wrk/task :sendmail (wrk/submit! {::wrk/task :sendmail
::wrk/delay 0 ::wrk/delay 0
::wrk/max-retries 4 ::wrk/max-retries 4
@@ -343,8 +357,10 @@
(def ^:private schema:feedback (def ^:private schema:feedback
[:map [:map
[:subject ::sm/text] [:feedback-subject ::sm/text]
[:content ::sm/text]]) [:feedback-type ::sm/text]
[:feedback-content ::sm/text]
[:profile :map]])
(def user-feedback (def user-feedback
"A profile feedback email." "A profile feedback email."
+253 -103
View File
@@ -9,46 +9,22 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.files.migrations :as fmg]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.types.path :as path] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.types.objects-map :as omap]
[app.config :as cf]
[app.db :as db] [app.db :as db]
[app.db.sql :as-alias sql] [app.db.sql :as-alias sql]
[app.storage :as sto] [app.storage :as sto]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.objects-map :as omap] [app.util.objects-map :as omap.legacy]
[app.util.pointer-map :as pmap])) [app.util.pointer-map :as pmap]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OFFLOAD
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn offloaded?
[file]
(= "objects-storage" (:data-backend file)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OBJECTS-MAP ;; OBJECTS-MAP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn enable-objects-map
[file & _opts]
(let [update-page
(fn [page]
(if (and (pmap/pointer-map? page)
(not (pmap/loaded? page)))
page
(update page :objects omap/wrap)))
update-data
(fn [fdata]
(update fdata :pages-index d/update-vals update-page))]
(-> file
(update :data update-data)
(update :features conj "fdata/objects-map"))))
(defn process-objects (defn process-objects
"Apply a function to all objects-map on the file. Usualy used for convert "Apply a function to all objects-map on the file. Usualy used for convert
the objects-map instances to plain maps" the objects-map instances to plain maps"
@@ -58,35 +34,237 @@
(fn [page] (fn [page]
(update page :objects (update page :objects
(fn [objects] (fn [objects]
(if (omap/objects-map? objects) (if (or (omap/objects-map? objects)
(omap.legacy/objects-map? objects))
(update-fn objects) (update-fn objects)
objects))))) objects)))))
fdata)) fdata))
(defn realize-objects
"Process a file and remove all instances of objects map realizing them
to a plain data. Used in operation where is more efficient have the
whole file loaded in memory or we going to persist it in an
alterantive storage."
[_cfg file]
(update file :data process-objects (partial into {})))
(defn enable-objects-map
[file & _opts]
(let [update-page
(fn [page]
(update page :objects omap/wrap))
update-data
(fn [fdata]
(update fdata :pages-index d/update-vals update-page))]
(-> file
(update :data update-data)
(update :features conj "fdata/objects-map"))))
(defn disable-objects-map
[file & _opts]
(let [update-page
(fn [page]
(update page :objects #(into {} %)))
update-data
(fn [fdata]
(update fdata :pages-index d/update-vals update-page))]
(-> file
(update :data update-data)
(update :features disj "fdata/objects-map"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; STORAGE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmulti resolve-file-data
(fn [_cfg file] (get file :backend "legacy-db")))
(defmethod resolve-file-data "legacy-db"
[_cfg {:keys [legacy-data] :as file}]
(-> file
(assoc :data legacy-data)
(dissoc :legacy-data)))
(defmethod resolve-file-data "db"
[_cfg file]
(dissoc file :legacy-data))
(defmethod resolve-file-data "storage"
[cfg {:keys [metadata] :as file}]
(let [storage (sto/resolve cfg ::db/reuse-conn true)
ref-id (:storage-ref-id metadata)
data (->> (sto/get-object storage ref-id)
(sto/get-object-bytes storage))]
(-> file
(assoc :data data)
(dissoc :legacy-data))))
(defn decode-file-data
[_cfg {:keys [data] :as file}]
(cond-> file
(bytes? data)
(assoc :data (blob/decode data))))
(def ^:private sql:insert-file-data
"INSERT INTO file_data (file_id, id, created_at, modified_at, deleted_at,
type, backend, metadata, data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
(def ^:private sql:upsert-file-data
(str sql:insert-file-data
" ON CONFLICT (file_id, id)
DO UPDATE SET modified_at=?,
deleted_at=?,
backend=?,
metadata=?,
data=?"))
(defn- upsert-in-database
[cfg {:keys [id file-id created-at modified-at deleted-at type backend data metadata]}]
(let [created-at (or created-at (ct/now))
metadata (some-> metadata db/json)
modified-at (or modified-at created-at)]
(db/exec-one! cfg [sql:upsert-file-data
file-id id
created-at
modified-at
deleted-at
type
backend
metadata
data
modified-at
deleted-at
backend
metadata
data])))
(defn- handle-persistence
[cfg {:keys [type backend id file-id data] :as params}]
(cond
(= backend "storage")
(let [storage (sto/resolve cfg)
content (sto/content data)
sobject (sto/put-object! storage
{::sto/content content
::sto/touch true
:bucket "file-data"
:content-type "application/octet-stream"
:file-id file-id
:id id})
metadata {:storage-ref-id (:id sobject)}
params (-> params
(assoc :metadata metadata)
(assoc :data nil))]
(upsert-in-database cfg params))
(= backend "db")
(->> (dissoc params :metadata)
(upsert-in-database cfg))
(= backend "legacy-db")
(cond
(= type "main")
(do
(db/delete! cfg :file-data
{:id id :file-id file-id :type "main"}
{::db/return-keys false})
(db/update! cfg :file
{:data data}
{:id file-id}
{::db/return-keys false}))
(= type "snapshot")
(do
(db/delete! cfg :file-data
{:id id :file-id file-id :type "snapshot"}
{::db/return-keys false})
(db/update! cfg :file-change
{:data data}
{:file-id file-id :id id}
{::db/return-keys false}))
(= type "fragment")
(upsert-in-database cfg
(-> (dissoc params :metadata)
(assoc :backend "db")))
:else
(throw (RuntimeException. "not implemented")))
:else
(throw (IllegalArgumentException.
(str "backend '" backend "' not supported")))))
(defn process-metadata
[cfg metadata]
(when-let [storage-id (:storage-ref-id metadata)]
(let [storage (sto/resolve cfg ::db/reuse-conn true)]
(sto/touch-object! storage storage-id))))
(defn- default-backend
[backend]
(or backend (cf/get :file-data-backend)))
(def ^:private schema:metadata
[:map {:title "Metadata"}
[:storage-ref-id {:optional true} ::sm/uuid]])
(def decode-metadata-with-schema
(sm/decoder schema:metadata sm/json-transformer))
(defn decode-metadata
[metadata]
(some-> metadata
(db/decode-json-pgobject)
(decode-metadata-with-schema)))
(def ^:private schema:update-params
[:map {:closed true}
[:id ::sm/uuid]
[:type [:enum "main" "snapshot" "fragment"]]
[:file-id ::sm/uuid]
[:backend {:optional true} [:enum "db" "legacy-db" "storage"]]
[:metadata {:optional true} [:maybe schema:metadata]]
[:data {:optional true} bytes?]
[:created-at {:optional true} ::ct/inst]
[:modified-at {:optional true} [:maybe ::ct/inst]]
[:deleted-at {:optional true} [:maybe ::ct/inst]]])
(def ^:private check-update-params
(sm/check-fn schema:update-params :hint "invalid params received for update"))
(defn upsert!
"Create or update file data"
[cfg params & {:as opts}]
(let [params (-> (check-update-params params)
(update :backend default-backend))]
(some->> (:metadata params)
(process-metadata cfg))
(-> (handle-persistence cfg params)
(db/get-update-count)
(pos?))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; POINTER-MAP ;; POINTER-MAP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-file-data
"Get file data given a file instance."
[system file]
(if (offloaded? file)
(let [storage (sto/resolve system ::db/reuse-conn true)]
(->> (sto/get-object storage (:data-ref-id file))
(sto/get-object-bytes storage)))
(:data file)))
(defn resolve-file-data
[system file]
(let [data (get-file-data system file)]
(assoc file :data data)))
(defn load-pointer (defn load-pointer
"A database loader pointer helper" "A database loader pointer helper"
[system file-id id] [cfg file-id id]
(let [fragment (db/get* system :file-data-fragment (let [fragment (some-> (db/get* cfg :file-data
{:id id :file-id file-id} {:id id :file-id file-id :type "fragment"}
{::sql/columns [:data :data-backend :data-ref-id :id]})] {::sql/columns [:data :backend :id :metadata]})
(update :metadata decode-metadata))]
(l/trc :hint "load pointer" (l/trc :hint "load pointer"
:file-id (str file-id) :file-id (str file-id)
@@ -100,22 +278,21 @@
:file-id file-id :file-id file-id
:fragment-id id)) :fragment-id id))
(let [data (get-file-data system fragment)] (-> (resolve-file-data cfg fragment)
;; FIXME: conditional thread scheduling for decoding big objects (get :data)
(blob/decode data)))) (blob/decode))))
(defn persist-pointers! (defn persist-pointers!
"Persist all currently tracked pointer objects" "Persist all currently tracked pointer objects"
[system file-id] [cfg file-id]
(let [conn (db/get-connection system)] (doseq [[id item] @pmap/*tracked*]
(doseq [[id item] @pmap/*tracked*] (when (pmap/modified? item)
(when (pmap/modified? item) (l/trc :hint "persist pointer" :file-id (str file-id) :id (str id))
(l/trc :hint "persist pointer" :file-id (str file-id) :id (str id)) (let [content (-> item deref blob/encode)]
(let [content (-> item deref blob/encode)] (upsert! cfg {:id id
(db/insert! conn :file-data-fragment :file-id file-id
{:id id :type "fragment"
:file-id file-id :data content})))))
:data content}))))))
(defn process-pointers (defn process-pointers
"Apply a function to all pointers on the file. Usuly used for "Apply a function to all pointers on the file. Usuly used for
@@ -129,6 +306,14 @@
(d/update-vals update-fn') (d/update-vals update-fn')
(update :pages-index d/update-vals update-fn')))) (update :pages-index d/update-vals update-fn'))))
(defn realize-pointers
"Process a file and remove all instances of pointers realizing them to
a plain data. Used in operation where is more efficient have the
whole file loaded in memory."
[cfg {:keys [id] :as file}]
(binding [pmap/*load-fn* (partial load-pointer cfg id)]
(update file :data process-pointers deref)))
(defn get-used-pointer-ids (defn get-used-pointer-ids
"Given a file, return all pointer ids used in the data." "Given a file, return all pointer ids used in the data."
[fdata] [fdata]
@@ -148,47 +333,12 @@
(update :features conj "fdata/pointer-map"))) (update :features conj "fdata/pointer-map")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PATH-DATA ;; GENERAL PURPOSE HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn enable-path-data (defn realize
"Enable the fdata/path-data feature on the file." "A helper that combines realize-pointers and realize-objects"
[file & _opts] [cfg file]
(letfn [(update-object [object] (->> file
(if (or (cfh/path-shape? object) (realize-pointers cfg)
(cfh/bool-shape? object)) (realize-objects cfg)))
(update object :content path/content)
object))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(-> file
(update :data (fn [data]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(update :features conj "fdata/path-data"))))
(defn disable-path-data
[file & _opts]
(letfn [(update-object [object]
(if (or (cfh/path-shape? object)
(cfh/bool-shape? object))
(update object :content vec)
object))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(when-let [conn db/*conn*]
(db/delete! conn :file-migration {:file-id (:id file)
:name "0003-convert-path-content"}))
(-> file
(update :data (fn [data]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(update :features disj "fdata/path-data")
(update :migrations disj "0003-convert-path-content")
(vary-meta update ::fmg/migrated disj "0003-convert-path-content"))))
+19 -5
View File
@@ -8,6 +8,7 @@
"Backend specific code for file migrations. Implemented as permanent feature of files." "Backend specific code for file migrations. Implemented as permanent feature of files."
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.files.migrations :as fmg :refer [xf:map-name]] [app.common.files.migrations :as fmg :refer [xf:map-name]]
[app.db :as db] [app.db :as db]
[app.db.sql :as-alias sql])) [app.db.sql :as-alias sql]))
@@ -26,14 +27,27 @@
(defn upsert-migrations! (defn upsert-migrations!
"Persist or update file migrations. Return the updated/inserted number "Persist or update file migrations. Return the updated/inserted number
of rows" of rows"
[conn {:keys [id] :as file}] [cfg {:keys [id] :as file}]
(let [migrations (or (-> file meta ::fmg/migrated) (let [conn (db/get-connection cfg)
(-> file :migrations not-empty) migrations (or (-> file meta ::fmg/migrated)
fmg/available-migrations) (-> file :migrations))
columns [:file-id :name] columns [:file-id :name]
rows (mapv (fn [name] [id name]) migrations)] rows (->> migrations
(mapv (fn [name] [id name]))
(not-empty))]
(when-not rows
(ex/raise :type :internal
:code :missing-migrations
:hint "no migrations available on file"))
(-> (db/insert-many! conn :file-migration columns rows (-> (db/insert-many! conn :file-migration columns rows
{::db/return-keys false {::db/return-keys false
::sql/on-conflict-do-nothing true}) ::sql/on-conflict-do-nothing true})
(db/get-update-count)))) (db/get-update-count))))
(defn reset-migrations!
"Replace file migrations"
[cfg {:keys [id] :as file}]
(db/delete! cfg :file-migration {:file-id id})
(upsert-migrations! cfg file))
+446
View File
@@ -0,0 +1,446 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.features.file-snapshots
(:require
[app.binfile.common :as bfc]
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.features :as-alias cfeat]
[app.common.files.migrations :as fmg]
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.fdata :as fdata]
[app.storage :as sto]
[app.util.blob :as blob]
[app.worker :as wrk]
[cuerdas.core :as str]))
(def sql:snapshots
"SELECT c.id,
c.label,
c.created_at,
c.updated_at AS modified_at,
c.deleted_at,
c.profile_id,
c.created_by,
c.locked_by,
c.revn,
c.features,
c.migrations,
c.version,
c.file_id,
c.data AS legacy_data,
fd.data AS data,
coalesce(fd.backend, 'legacy-db') AS backend,
fd.metadata AS metadata
FROM file_change AS c
LEFT JOIN file_data AS fd ON (fd.file_id = c.file_id
AND fd.id = c.id
AND fd.type = 'snapshot')
WHERE c.label IS NOT NULL")
(defn- decode-snapshot
[snapshot]
(some-> snapshot
(-> (d/update-when :metadata fdata/decode-metadata)
(d/update-when :migrations db/decode-pgarray [])
(d/update-when :features db/decode-pgarray #{}))))
(def ^:private sql:get-minimal-file
"SELECT f.id,
f.revn,
f.modified_at,
f.deleted_at,
fd.backend AS backend,
fd.metadata AS metadata
FROM file AS f
LEFT JOIN file_data AS fd ON (fd.file_id = f.id AND fd.id = f.id)
WHERE f.id = ?")
(defn- get-minimal-file
[cfg id & {:as opts}]
(-> (db/get-with-sql cfg [sql:get-minimal-file id] opts)
(d/update-when :metadata fdata/decode-metadata)))
(def ^:private sql:get-snapshot-without-data
(str "WITH snapshots AS (" sql:snapshots ")"
"SELECT c.id,
c.label,
c.revn,
c.created_at,
c.modified_at,
c.deleted_at,
c.profile_id,
c.created_by,
c.locked_by,
c.features,
c.metadata,
c.migrations,
c.version,
c.file_id
FROM snapshots AS c
WHERE c.id = ?
AND CASE WHEN c.created_by = 'user'
THEN c.deleted_at IS NULL
WHEN c.created_by = 'system'
THEN c.deleted_at IS NULL OR c.deleted_at >= ?::timestamptz
END"))
(defn get-minimal-snapshot
[cfg snapshot-id]
(let [now (ct/now)]
(-> (db/get-with-sql cfg [sql:get-snapshot-without-data snapshot-id now]
{::db/remove-deleted false})
(decode-snapshot))))
(def ^:private sql:get-snapshot
(str sql:snapshots
" AND c.file_id = ?
AND c.id = ?
AND CASE WHEN c.created_by = 'user'
THEN (c.deleted_at IS NULL)
WHEN c.created_by = 'system'
THEN (c.deleted_at IS NULL OR c.deleted_at >= ?::timestamptz)
END"))
(defn- get-snapshot
"Get snapshot with decoded data"
[cfg file-id snapshot-id]
(let [now (ct/now)]
(->> (db/get-with-sql cfg [sql:get-snapshot file-id snapshot-id now]
{::db/remove-deleted false})
(decode-snapshot)
(fdata/resolve-file-data cfg)
(fdata/decode-file-data cfg))))
(def ^:private sql:get-visible-snapshots
(str "WITH "
"snapshots1 AS ( " sql:snapshots "),"
"snapshots2 AS (
SELECT c.id,
c.label,
c.revn,
c.version,
c.created_at,
c.modified_at,
c.created_by,
c.locked_by,
c.profile_id,
c.deleted_at
FROM snapshots1 AS c
WHERE c.file_id = ?
), snapshots3 AS (
(SELECT * FROM snapshots2
WHERE created_by = 'system'
AND (deleted_at IS NULL OR
deleted_at >= ?::timestamptz)
LIMIT 500)
UNION ALL
(SELECT * FROM snapshots2
WHERE created_by = 'user'
AND deleted_at IS NULL
LIMIT 500)
)
SELECT * FROM snapshots3
ORDER BY created_at DESC"))
(defn get-visible-snapshots
"Return a list of snapshots fecheable from the API, it has a limited
set of fields and applies big but safe limits over all available
snapshots. It return a ordered vector by the snapshot date of
creation."
[cfg file-id]
(let [now (ct/now)]
(->> (db/exec! cfg [sql:get-visible-snapshots file-id now])
(mapv decode-snapshot))))
(def ^:private schema:decoded-file
[:map {:title "DecodedFile"}
[:id ::sm/uuid]
[:revn :int]
[:vern :int]
[:data :map]
[:version :int]
[:features ::cfeat/features]
[:migrations [::sm/set :string]]])
(def ^:private schema:snapshot
[:map {:title "Snapshot"}
[:id ::sm/uuid]
[:revn [::sm/int {:min 0}]]
[:version [::sm/int {:min 0}]]
[:features ::cfeat/features]
[:migrations [::sm/set ::sm/text]]
[:profile-id {:optional true} ::sm/uuid]
[:label ::sm/text]
[:file-id ::sm/uuid]
[:created-by [:enum "system" "user" "admin"]]
[:deleted-at {:optional true} ::ct/inst]
[:modified-at ::ct/inst]
[:created-at ::ct/inst]])
(def ^:private check-snapshot
(sm/check-fn schema:snapshot))
(def ^:private check-decoded-file
(sm/check-fn schema:decoded-file))
(defn- generate-snapshot-label
[]
(let [ts (-> (ct/now)
(ct/format-inst)
(str/replace #"[T:\.]" "-")
(str/rtrim "Z"))]
(str "snapshot-" ts)))
(def ^:private schema:create-params
[:map {:title "SnapshotCreateParams"}
[:profile-id ::sm/uuid]
[:created-by {:optional true} [:enum "user" "system"]]
[:label {:optional true} ::sm/text]
[:session-id {:optional true} ::sm/uuid]
[:modified-at {:optional true} ::ct/inst]
[:deleted-at {:optional true} ::ct/inst]])
(def ^:private check-create-params
(sm/check-fn schema:create-params))
(defn create!
"Create a file snapshot; expects a non-encoded file"
[cfg file & {:as params}]
(let [{:keys [label created-by deleted-at profile-id session-id]}
(check-create-params params)
file
(check-decoded-file file)
created-by
(or created-by "system")
snapshot-id
(uuid/next)
created-at
(ct/now)
deleted-at
(or deleted-at
(if (= created-by "system")
(ct/in-future (cf/get-deletion-delay))
nil))
label
(or label (generate-snapshot-label))
snapshot
(cond-> {:id snapshot-id
:revn (:revn file)
:version (:version file)
:file-id (:id file)
:features (:features file)
:migrations (:migrations file)
:label label
:created-at created-at
:modified-at created-at
:created-by created-by}
deleted-at
(assoc :deleted-at deleted-at)
:always
(check-snapshot))]
(db/insert! cfg :file-change
(-> snapshot
(update :features into-array)
(update :migrations into-array)
(assoc :updated-at created-at)
(assoc :profile-id profile-id)
(assoc :session-id session-id)
(dissoc :modified-at))
{::db/return-keys false})
(fdata/upsert! cfg
{:id snapshot-id
:file-id (:id file)
:type "snapshot"
:data (blob/encode (:data file))
:created-at created-at
:deleted-at deleted-at})
snapshot))
(def ^:private schema:update-params
[:map {:title "SnapshotUpdateParams"}
[:id ::sm/uuid]
[:file-id ::sm/uuid]
[:label ::sm/text]
[:modified-at {:optional true} ::ct/inst]])
(def ^:private check-update-params
(sm/check-fn schema:update-params))
(defn update!
[cfg params]
(let [{:keys [id file-id label modified-at]}
(check-update-params params)
modified-at
(or modified-at (ct/now))]
(db/update! cfg :file-data
{:deleted-at nil
:modified-at modified-at}
{:file-id file-id
:id id
:type "snapshot"}
{::db/return-keys false})
(-> (db/update! cfg :file-change
{:label label
:created-by "user"
:updated-at modified-at
:deleted-at nil}
{:file-id file-id
:id id}
{::db/return-keys false})
(db/get-update-count)
(pos?))))
(defn restore!
[{:keys [::db/conn] :as cfg} file-id snapshot-id]
(let [file (get-minimal-file conn file-id {::db/for-update true})
vern (rand-int Integer/MAX_VALUE)
storage
(sto/resolve cfg {::db/reuse-conn true})
snapshot
(get-snapshot cfg file-id snapshot-id)]
(when-not snapshot
(ex/raise :type :not-found
:code :snapshot-not-found
:hint "unable to find snapshot with the provided label"
:snapshot-id snapshot-id
:file-id file-id))
(when-not (:data snapshot)
(ex/raise :type :internal
:code :snapshot-without-data
:hint "snapshot has no data"
:label (:label snapshot)
:file-id file-id))
(let [;; If the snapshot has applied migrations stored, we reuse
;; them, if not, we take a safest set of migrations as
;; starting point. This is because, at the time of
;; implementing snapshots, migrations were not taken into
;; account so we need to make this backward compatible in
;; some way.
migrations
(or (:migrations snapshot)
(fmg/generate-migrations-from-version 67))
file
(-> file
(update :revn inc)
(assoc :migrations migrations)
(assoc :data (:data snapshot))
(assoc :vern vern)
(assoc :version (:version snapshot))
(assoc :has-media-trimmed false)
(assoc :modified-at (:modified-at snapshot))
(assoc :features (:features snapshot)))]
(l/dbg :hint "restoring snapshot"
:file-id (str file-id)
:label (:label snapshot)
:snapshot-id (str (:id snapshot)))
;; In the same way, on reseting the file data, we need to restore
;; the applied migrations on the moment of taking the snapshot
(bfc/update-file! cfg file ::bfc/reset-migrations? true)
;; FIXME: this should be separated functions, we should not have
;; inline sql here.
;; clean object thumbnails
(let [sql (str "update file_tagged_object_thumbnail "
" set deleted_at = now() "
" where file_id=? returning media_id")
res (db/exec! conn [sql file-id])]
(doseq [media-id (into #{} (keep :media-id) res)]
(sto/touch-object! storage media-id)))
;; clean file thumbnails
(let [sql (str "update file_thumbnail "
" set deleted_at = now() "
" where file_id=? returning media_id")
res (db/exec! conn [sql file-id])]
(doseq [media-id (into #{} (keep :media-id) res)]
(sto/touch-object! storage media-id)))
vern)))
(defn delete!
[cfg & {:keys [id file-id deleted-at]}]
(assert (uuid? id) "missing id")
(assert (uuid? file-id) "missing file-id")
(assert (ct/inst? deleted-at) "missing deleted-at")
(wrk/submit! {::db/conn (db/get-connection cfg)
::wrk/task :delete-object
::wrk/params {:object :snapshot
:deleted-at deleted-at
:file-id file-id
:id id}})
(db/update! cfg :file-change
{:deleted-at deleted-at}
{:id id :file-id file-id}
{::db/return-keys false})
true)
(def ^:private sql:get-snapshots
(str sql:snapshots " AND c.file_id = ?"))
(defn lock-by!
[conn id profile-id]
(-> (db/update! conn :file-change
{:locked-by profile-id}
{:id id}
{::db/return-keys false})
(db/get-update-count)
(pos?)))
(defn unlock!
[conn id]
(-> (db/update! conn :file-change
{:locked-by nil}
{:id id}
{::db/return-keys false})
(db/get-update-count)
(pos?)))
(defn reduce-snapshots
"Process the file snapshots using efficient reduction; the file
reduction comes with all snapshots, including maked as deleted"
[cfg file-id xform f init]
(let [conn (db/get-connection cfg)
xform (comp
(map (partial fdata/resolve-file-data cfg))
(map (partial fdata/decode-file-data cfg))
xform)]
(->> (db/plan conn [sql:get-snapshots file-id] {:fetch-size 1})
(transduce xform f init))))
+10 -9
View File
@@ -7,22 +7,23 @@
(ns app.features.logical-deletion (ns app.features.logical-deletion
"A code related to handle logical deletion mechanism" "A code related to handle logical deletion mechanism"
(:require (:require
[app.config :as cf] [app.common.time :as ct]
[app.util.time :as dt])) [app.config :as cf]))
(def ^:private canceled-status
#{"canceled" "unpaid"})
(defn get-deletion-delay (defn get-deletion-delay
"Calculate the next deleted-at for a resource (file, team, etc) in function "Calculate the next deleted-at for a resource (file, team, etc) in function
of team settings" of team settings"
[team] [team]
(if-let [subscription (get team :subscription)] (if-let [{:keys [type status]} (get team :subscription)]
(cond (cond
(and (= (:type subscription) "unlimited") (and (= "unlimited" type) (not (contains? canceled-status status)))
(= (:status subscription) "active")) (ct/duration {:days 30})
(dt/duration {:days 30})
(and (= (:type subscription) "enterprise") (and (= "enterprise" type) (not (contains? canceled-status status)))
(= (:status subscription) "active")) (ct/duration {:days 90})
(dt/duration {:days 90})
:else :else
(cf/get-deletion-delay)) (cf/get-deletion-delay))
+44 -29
View File
@@ -17,16 +17,16 @@
[app.http.awsns :as-alias awsns] [app.http.awsns :as-alias awsns]
[app.http.debug :as-alias debug] [app.http.debug :as-alias debug]
[app.http.errors :as errors] [app.http.errors :as errors]
[app.http.management :as mgmt]
[app.http.middleware :as mw] [app.http.middleware :as mw]
[app.http.security :as sec]
[app.http.session :as session] [app.http.session :as session]
[app.http.websocket :as-alias ws] [app.http.websocket :as-alias ws]
[app.main :as-alias main] [app.main :as-alias main]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.doc :as-alias rpc.doc]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.exec :as px]
[reitit.core :as r] [reitit.core :as r]
[reitit.middleware :as rr] [reitit.middleware :as rr]
[yetti.adapter :as yt] [yetti.adapter :as yt]
@@ -53,6 +53,8 @@
[:map [:map
[::port ::sm/int] [::port ::sm/int]
[::host ::sm/text] [::host ::sm/text]
[::io-threads {:optional true} ::sm/int]
[::max-worker-threads {:optional true} ::sm/int]
[::max-body-size {:optional true} ::sm/int] [::max-body-size {:optional true} ::sm/int]
[::max-multipart-body-size {:optional true} ::sm/int] [::max-multipart-body-size {:optional true} ::sm/int]
[::router {:optional true} [:fn r/router?]] [::router {:optional true} [:fn r/router?]]
@@ -63,30 +65,41 @@
(assert (sm/check schema:server-params params))) (assert (sm/check schema:server-params params)))
(defmethod ig/init-key ::server (defmethod ig/init-key ::server
[_ {:keys [::handler ::router ::host ::port] :as cfg}] [_ {:keys [::handler ::router ::host ::port ::mtx/metrics] :as cfg}]
(l/info :hint "starting http server" :port port :host host) (l/info :hint "starting http server" :port port :host host)
(let [options {:http/port port (let [on-dispatch
:http/host host (fn [_ start-at-ns]
:http/max-body-size (::max-body-size cfg) (let [timing (- (System/nanoTime) start-at-ns)
:http/max-multipart-body-size (::max-multipart-body-size cfg) timing (int (/ timing 1000000))]
:xnio/io-threads (or (::io-threads cfg) (mtx/run! metrics
(max 3 (px/get-available-processors))) :id :http-server-dispatch-timing
:xnio/dispatch :virtual :val timing)))
:ring/compat :ring2
:socket/backlog 4069}
handler (cond options
(some? router) {:http/port port
(router-handler router) :http/host host
:http/max-body-size (::max-body-size cfg)
:http/max-multipart-body-size (::max-multipart-body-size cfg)
:xnio/direct-buffers false
:xnio/io-threads (::io-threads cfg)
:xnio/max-worker-threads (::max-worker-threads cfg)
:ring/compat :ring2
:events/on-dispatch on-dispatch
:socket/backlog 4069}
(some? handler) handler
handler (cond
(some? router)
(router-handler router)
:else (some? handler)
(throw (UnsupportedOperationException. "handler or router are required"))) handler
options (d/without-nils options) :else
server (yt/server handler options)] (throw (UnsupportedOperationException. "handler or router are required")))
server
(yt/server handler (d/without-nils options))]
(assoc cfg ::server (yt/start! server)))) (assoc cfg ::server (yt/start! server))))
@@ -135,12 +148,12 @@
[:map [:map
[::ws/routes schema:routes] [::ws/routes schema:routes]
[::rpc/routes schema:routes] [::rpc/routes schema:routes]
[::rpc.doc/routes schema:routes]
[::oidc/routes schema:routes] [::oidc/routes schema:routes]
[::assets/routes schema:routes] [::assets/routes schema:routes]
[::debug/routes schema:routes] [::debug/routes schema:routes]
[::mtx/routes schema:routes] [::mtx/routes schema:routes]
[::awsns/routes schema:routes] [::awsns/routes schema:routes]
[::mgmt/routes schema:routes]
::session/manager ::session/manager
::setup/props ::setup/props
::db/pool]) ::db/pool])
@@ -153,10 +166,12 @@
[_ cfg] [_ cfg]
(rr/router (rr/router
[["" {:middleware [[mw/server-timing] [["" {:middleware [[mw/server-timing]
[sec/sec-fetch-metadata]
[mw/params] [mw/params]
[mw/format-response] [mw/format-response]
[session/soft-auth cfg] [mw/auth {:bearer (partial session/decode-token cfg)
[actoken/soft-auth cfg] :cookie (partial session/decode-token cfg)
:token (partial actoken/decode-token cfg)}]
[mw/parse-request] [mw/parse-request]
[mw/errors errors/handle] [mw/errors errors/handle]
[mw/restrict-methods]]} [mw/restrict-methods]]}
@@ -168,9 +183,9 @@
["/webhooks" ["/webhooks"
(::awsns/routes cfg)] (::awsns/routes cfg)]
(::ws/routes cfg) ["/management"
(::mgmt/routes cfg)]
["/api" {:middleware [[mw/cors]]} (::ws/routes cfg)
(::oidc/routes cfg) (::oidc/routes cfg)
(::rpc.doc/routes cfg) (::rpc/routes cfg)]]))
(::rpc/routes cfg)]]]))
+26 -49
View File
@@ -9,23 +9,19 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.http :as-alias http]
[app.main :as-alias main] [app.main :as-alias main]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.tokens :as tokens] [app.tokens :as tokens]))
[yetti.request :as yreq]))
(def header-re #"^Token\s+(.*)") (defn decode-token
[cfg token]
(defn- get-token (try
[request] (tokens/verify cfg {:token token :iss "access-token"})
(some->> (yreq/get-header request "authorization") (catch Throwable cause
(re-matches header-re) (l/trc :hint "exception on decoding token"
(second))) :token token
:cause cause))))
(defn- decode-token
[props token]
(when token
(tokens/verify props {:token token :iss "access-token"})))
(def sql:get-token-data (def sql:get-token-data
"SELECT perms, profile_id, expires_at "SELECT perms, profile_id, expires_at
@@ -35,47 +31,28 @@
OR (expires_at > now()));") OR (expires_at > now()));")
(defn- get-token-data (defn- get-token-data
[pool token-id] [pool claims]
(when-not (db/read-only? pool) (when-not (db/read-only? pool)
(some-> (db/exec-one! pool [sql:get-token-data token-id]) (when-let [token-id (get claims :tid)]
(update :perms db/decode-pgarray #{})))) (some-> (db/exec-one! pool [sql:get-token-data token-id])
(update :perms db/decode-pgarray #{})))))
(defn- wrap-soft-auth
"Soft Authentication, will be executed synchronously on the undertow
worker thread."
[handler {:keys [::setup/props]}]
(letfn [(handle-request [request]
(try
(let [token (get-token request)
claims (decode-token props token)]
(cond-> request
(map? claims)
(assoc ::id (:tid claims))))
(catch Throwable cause
(l/trace :hint "exception on decoding malformed token" :cause cause)
request)))]
(fn [request]
(handler (handle-request request)))))
(defn- wrap-authz (defn- wrap-authz
"Authorization middleware, will be executed synchronously on vthread."
[handler {:keys [::db/pool]}] [handler {:keys [::db/pool]}]
(fn [request] (fn [request]
(let [{:keys [perms profile-id expires-at]} (some->> (::id request) (get-token-data pool))] (let [{:keys [type claims]} (get request ::http/auth-data)]
(handler (cond-> request (if (= :token type)
(some? perms) (let [{:keys [perms profile-id expires-at]} (some->> claims (get-token-data pool))]
(assoc ::perms perms) ;; FIXME: revisit this, this data looks unused
(some? profile-id) (handler (cond-> request
(assoc ::profile-id profile-id) (some? perms)
(some? expires-at) (assoc ::perms perms)
(assoc ::expires-at expires-at)))))) (some? profile-id)
(assoc ::profile-id profile-id)
(some? expires-at)
(assoc ::expires-at expires-at))))
(def soft-auth (handler request)))))
{:name ::soft-auth
:compile (fn [& _]
(when (contains? cf/flags :access-tokens)
wrap-soft-auth))})
(def authz (def authz
{:name ::authz {:name ::authz
+4 -4
View File
@@ -9,18 +9,18 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.time :as ct]
[app.common.uri :as u] [app.common.uri :as u]
[app.db :as db] [app.db :as db]
[app.storage :as sto] [app.storage :as sto]
[app.util.time :as dt]
[integrant.core :as ig] [integrant.core :as ig]
[yetti.response :as-alias yres])) [yetti.response :as-alias yres]))
(def ^:private cache-max-age (def ^:private cache-max-age
(dt/duration {:hours 24})) (ct/duration {:hours 24}))
(def ^:private signature-max-age (def ^:private signature-max-age
(dt/duration {:hours 24 :minutes 15})) (ct/duration {:hours 24 :minutes 15}))
(defn get-id (defn get-id
[{:keys [path-params]}] [{:keys [path-params]}]
@@ -30,7 +30,7 @@
(defn- get-file-media-object (defn- get-file-media-object
[pool id] [pool id]
(db/get pool :file-media-object {:id id})) (db/get pool :file-media-object {:id id} {::db/remove-deleted false}))
(defn- serve-object-from-s3 (defn- serve-object-from-s3
[{:keys [::sto/storage] :as cfg} obj] [{:keys [::sto/storage] :as cfg} obj]
+3 -5
View File
@@ -17,11 +17,9 @@
[app.main :as-alias main] [app.main :as-alias main]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.worker :as-alias wrk]
[clojure.data.json :as j] [clojure.data.json :as j]
[cuerdas.core :as str] [cuerdas.core :as str]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.exec :as px]
[yetti.request :as yreq] [yetti.request :as yreq]
[yetti.response :as-alias yres])) [yetti.response :as-alias yres]))
@@ -40,8 +38,8 @@
[_ cfg] [_ cfg]
(letfn [(handler [request] (letfn [(handler [request]
(let [data (-> request yreq/body slurp)] (let [data (-> request yreq/body slurp)]
(px/run! :vthread (partial handle-request cfg data))) (handle-request cfg data)
{::yres/status 200})] {::yres/status 200}))]
["/sns" {:handler handler ["/sns" {:handler handler
:allowed-methods #{:post}}])) :allowed-methods #{:post}}]))
@@ -109,7 +107,7 @@
[cfg headers] [cfg headers]
(let [tdata (get headers "x-penpot-data")] (let [tdata (get headers "x-penpot-data")]
(when-not (str/empty? tdata) (when-not (str/empty? tdata)
(let [result (tokens/verify (::setup/props cfg) {:token tdata :iss :profile-identity})] (let [result (tokens/verify cfg {:token tdata :iss :profile-identity})]
(:profile-id result))))) (:profile-id result)))))
(defn- parse-notification (defn- parse-notification
+5 -11
View File
@@ -9,8 +9,7 @@
(:require (:require
[app.common.schema :as sm] [app.common.schema :as sm]
[integrant.core :as ig] [integrant.core :as ig]
[java-http-clj.core :as http] [java-http-clj.core :as http])
[promesa.core :as p])
(:import (:import
java.net.http.HttpClient)) java.net.http.HttpClient))
@@ -29,14 +28,9 @@
(defn send! (defn send!
([client req] (send! client req {})) ([client req] (send! client req {}))
([client req {:keys [response-type sync?] :or {response-type :string sync? false}}] ([client req {:keys [response-type] :or {response-type :string}}]
(assert (client? client) "expected valid http client") (assert (client? client) "expected valid http client")
(if sync? (http/send req {:client client :as response-type})))
(http/send req {:client client :as response-type})
(try
(http/send-async req {:client client :as response-type})
(catch Throwable cause
(p/rejected cause))))))
(defn- resolve-client (defn- resolve-client
[params] [params]
@@ -56,8 +50,8 @@
([cfg-or-client request] ([cfg-or-client request]
(let [client (resolve-client cfg-or-client) (let [client (resolve-client cfg-or-client)
request (update request :uri str)] request (update request :uri str)]
(send! client request {:sync? true}))) (send! client request {})))
([cfg-or-client request options] ([cfg-or-client request options]
(let [client (resolve-client cfg-or-client) (let [client (resolve-client cfg-or-client)
request (update request :uri str)] request (update request :uri str)]
(send! client request (merge {:sync? true} options))))) (send! client request options))))
+172 -169
View File
@@ -15,21 +15,24 @@
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.pprint :as pp] [app.common.pprint :as pp]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.features.file-migrations :as feat.fmig]
[app.http.session :as session] [app.http.session :as session]
[app.rpc.commands.auth :as auth] [app.rpc.commands.auth :as auth]
[app.rpc.commands.files-create :refer [create-file]] [app.rpc.commands.files-create :refer [create-file]]
[app.rpc.commands.profile :as profile] [app.rpc.commands.profile :as profile]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.setup.clock :as clock]
[app.srepl.main :as srepl] [app.srepl.main :as srepl]
[app.storage :as-alias sto] [app.storage :as-alias sto]
[app.storage.tmp :as tmp] [app.storage.tmp :as tmp]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.template :as tmpl] [app.util.template :as tmpl]
[app.util.time :as dt]
[cuerdas.core :as str] [cuerdas.core :as str]
[datoteka.io :as io] [datoteka.io :as io]
[emoji.core :as emj] [emoji.core :as emj]
@@ -47,29 +50,35 @@
(defn index-handler (defn index-handler
[_cfg _request] [_cfg _request]
{::yres/status 200 (let [{:keys [clock offset]} @clock/current]
::yres/headers {"content-type" "text/html"} {::yres/status 200
::yres/body (-> (io/resource "app/templates/debug.tmpl") ::yres/headers {"content-type" "text/html"}
(tmpl/render {:version (:full cf/version)}))}) ::yres/body (-> (io/resource "app/templates/debug.tmpl")
(tmpl/render {:version (:full cf/version)
:current-clock (str clock)
:current-offset (if offset
(ct/format-duration offset)
"NO OFFSET")
:current-time (ct/format-inst (ct/now) :http)
:supported-features cfeat/supported-features}))}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FILE CHANGES ;; FILE CHANGES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn prepare-response (defn- get-resolved-file
[body] [cfg file-id]
(let [headers {"content-type" "application/transit+json"}] (some-> (bfc/get-file cfg file-id :migrate? false)
{::yres/status 200 (update :data blob/encode)))
::yres/body body
::yres/headers headers}))
(defn prepare-download-response (defn prepare-download
[body filename] [file filename]
(let [headers {"content-disposition" (str "attachment; filename=" filename) {::yres/status 200
"content-type" "application/octet-stream"}] ::yres/headers
{::yres/status 200 {"content-disposition" (str "attachment; filename=" filename ".json")
::yres/body body "content-type" "application/octet-stream"}
::yres/headers headers})) ::yres/body
(t/encode file {:type :json-verbose})})
(def sql:retrieve-range-of-changes (def sql:retrieve-range-of-changes
"select revn, changes from file_change where file_id=? and revn >= ? and revn <= ? order by revn") "select revn, changes from file_change where file_id=? and revn >= ? and revn <= ? order by revn")
@@ -77,45 +86,51 @@
(def sql:retrieve-single-change (def sql:retrieve-single-change
"select revn, changes, data from file_change where file_id=? and revn = ?") "select revn, changes, data from file_change where file_id=? and revn = ?")
(defn- retrieve-file-data (defn- download-file-data
[{:keys [::db/pool]} {:keys [params ::session/profile-id] :as request}] [cfg {:keys [params ::session/profile-id] :as request}]
(let [file-id (some-> params :file-id parse-uuid) (let [file-id (some-> params :file-id parse-uuid)
revn (some-> params :revn parse-long)
filename (str file-id)] filename (str file-id)]
(when-not file-id (when-not file-id
(ex/raise :type :validation (ex/raise :type :validation
:code :missing-arguments)) :code :missing-arguments))
(let [data (if (integer? revn) (if-let [file (get-resolved-file cfg file-id)]
(some-> (db/exec-one! pool [sql:retrieve-single-change file-id revn]) :data)
(some-> (db/get-by-id pool :file file-id) :data))]
(when-not data
(ex/raise :type :not-found
:code :enpty-data
:hint "empty response"))
(cond (cond
(contains? params :download) (contains? params :download)
(prepare-download-response data filename) (prepare-download file filename)
(contains? params :clone) (contains? params :clone)
(let [profile (profile/get-profile pool profile-id) (db/tx-run! cfg
project-id (:default-project-id profile)] (fn [{:keys [::db/conn] :as cfg}]
(let [profile (profile/get-profile conn profile-id)
project-id (:default-project-id profile)
file (-> (create-file cfg {:id (uuid/next)
:name (str "Cloned: " (:name file))
:features (:features file)
:project-id project-id
:profile-id profile-id})
(assoc :data (:data file))
(assoc :migrations (:migrations file)))]
(db/run! pool (fn [{:keys [::db/conn] :as cfg}] (feat.fmig/reset-migrations! conn file)
(create-file cfg {:id file-id (db/update! conn :file
:name (str "Cloned file: " filename) {:data (:data file)}
:project-id project-id {:id (:id file)}
:profile-id profile-id}) {::db/return-keys false})
(db/update! conn :file
{:data data}
{:id file-id}) {::yres/status 201
{::yres/status 201 ::yres/body "OK CLONED"})))
::yres/body "OK CREATED"})))
:else :else
(prepare-response (blob/decode data)))))) (ex/raise :type :validation
:code :invalid-params
:hint "invalid button"))
(ex/raise :type :not-found
:code :enpty-data
:hint "empty response"))))
(defn- is-file-exists? (defn- is-file-exists?
[pool id] [pool id]
@@ -123,81 +138,61 @@
(-> (db/exec-one! pool [sql id]) :exists))) (-> (db/exec-one! pool [sql id]) :exists)))
(defn- upload-file-data (defn- upload-file-data
[{:keys [::db/pool]} {:keys [::session/profile-id params] :as request}] [{:keys [::db/pool] :as cfg} {:keys [::session/profile-id params] :as request}]
(let [profile (profile/get-profile pool profile-id) (let [profile (profile/get-profile pool profile-id)
project-id (:default-project-id profile) project-id (:default-project-id profile)
data (some-> params :file :path io/read*)] file (some-> params :file :path io/read* t/decode)]
(if (and data project-id) (if (and file project-id)
(let [fname (str "Imported file *: " (dt/now)) (let [fname (str "Imported: " (:name file) "(" (ct/now) ")")
reuse-id? (contains? params :reuseid) reuse-id? (contains? params :reuseid)
file-id (or (and reuse-id? (ex/ignoring (-> params :file :filename parse-uuid))) file-id (or (and reuse-id? (ex/ignoring (-> params :file :filename parse-uuid)))
(uuid/next))] (uuid/next))]
(if (and reuse-id? file-id (if (and reuse-id? file-id
(is-file-exists? pool file-id)) (is-file-exists? pool file-id))
(do (db/tx-run! cfg
(db/update! pool :file (fn [{:keys [::db/conn] :as cfg}]
{:data data (db/update! conn :file
:deleted-at nil} {:data (:data file)
{:id file-id}) :features (into-array (:features file))
{::yres/status 200 :deleted-at nil}
::yres/body "OK UPDATED"}) {:id file-id}
{::db/return-keys false})
(feat.fmig/reset-migrations! conn file)
{::yres/status 200
::yres/body "OK UPDATED"}))
(db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(let [file (-> (create-file cfg {:id file-id
:name fname
:features (:features file)
:project-id project-id
:profile-id profile-id})
(assoc :data (:data file))
(assoc :migrations (:migrations file)))]
(db/run! pool (fn [{:keys [::db/conn] :as cfg}]
(create-file cfg {:id file-id
:name fname
:project-id project-id
:profile-id profile-id})
(db/update! conn :file (db/update! conn :file
{:data data} {:data (:data file)}
{:id file-id}) {:id file-id}
{::db/return-keys false})
(feat.fmig/reset-migrations! conn file)
{::yres/status 201 {::yres/status 201
::yres/body "OK CREATED"})))) ::yres/body "OK CREATED"})))))
{::yres/status 500 (ex/raise :type :validation
::yres/body "ERROR"}))) :code :invalid-params
:hint "invalid file uploaded"))))
(defn file-data-handler (defn raw-export-import-handler
[cfg request] [cfg request]
(case (yreq/method request) (case (yreq/method request)
:get (retrieve-file-data cfg request) :get (download-file-data cfg request)
:post (upload-file-data cfg request) :post (upload-file-data cfg request)
(ex/raise :type :http (ex/raise :type :http
:code :method-not-found))) :code :method-not-found)))
(defn file-changes-handler
[{:keys [::db/pool]} {:keys [params] :as request}]
(letfn [(retrieve-changes [file-id revn]
(if (str/includes? revn ":")
(let [[start end] (->> (str/split revn #":")
(map str/trim)
(map parse-long))]
(some->> (db/exec! pool [sql:retrieve-range-of-changes file-id start end])
(map :changes)
(map blob/decode)
(mapcat identity)
(vec)))
(if-let [revn (parse-long revn)]
(let [item (db/exec-one! pool [sql:retrieve-single-change file-id revn])]
(some-> item :changes blob/decode vec))
(ex/raise :type :validation :code :invalid-arguments))))]
(let [file-id (some-> params :id parse-uuid)
revn (or (some-> params :revn parse-long) "latest")
filename (str file-id)]
(when (or (not file-id) (not revn))
(ex/raise :type :validation
:code :invalid-arguments
:hint "missing arguments"))
(let [data (retrieve-changes file-id revn)]
(if (contains? params :download)
(prepare-download-response data filename)
(prepare-response data))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ERROR BROWSER ;; ERROR BROWSER
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -234,7 +229,7 @@
(-> (io/resource "app/templates/error-report.v3.tmpl") (-> (io/resource "app/templates/error-report.v3.tmpl")
(tmpl/render (-> content (tmpl/render (-> content
(assoc :id id) (assoc :id id)
(assoc :created-at (dt/format-instant created-at :rfc1123))))))] (assoc :created-at (ct/format-inst created-at :rfc1123))))))]
(if-let [report (get-report request)] (if-let [report (get-report request)]
(let [result (case (:version report) (let [result (case (:version report)
@@ -258,7 +253,7 @@
(defn error-list-handler (defn error-list-handler
[{:keys [::db/pool]} _request] [{:keys [::db/pool]} _request]
(let [items (->> (db/exec! pool [sql:error-reports]) (let [items (->> (db/exec! pool [sql:error-reports])
(map #(update % :created-at dt/format-instant :rfc1123)))] (map #(update % :created-at ct/format-inst :rfc1123)))]
{::yres/status 200 {::yres/status 200
::yres/body (-> (io/resource "app/templates/error-list.tmpl") ::yres/body (-> (io/resource "app/templates/error-list.tmpl")
(tmpl/render {:items items})) (tmpl/render {:items items}))
@@ -402,77 +397,67 @@
::yres/headers {"content-type" "text/plain"} ::yres/headers {"content-type" "text/plain"}
::yres/body (str/ffmt "PROFILE '%' ACTIVATED" (:email profile))})))))) ::yres/body (str/ffmt "PROFILE '%' ACTIVATED" (:email profile))}))))))
(defn- handle-team-features
(defn- reset-file-version
[cfg {:keys [params] :as request}] [cfg {:keys [params] :as request}]
(let [file-id (some-> params :file-id d/parse-uuid) (let [team-id (some-> params :team-id d/parse-uuid)
version (some-> params :version d/parse-integer)] feature (some-> params :feature str)
action (some-> params :action)
(when-not (contains? params :force)
(ex/raise :type :validation
:code :missing-force
:hint "missing force checkbox"))
(when (nil? file-id)
(ex/raise :type :validation
:code :invalid-file-id
:hint "provided invalid file id"))
(when (nil? version)
(ex/raise :type :validation
:code :invalid-version
:hint "provided invalid version"))
(db/tx-run! cfg srepl/process-file! file-id #(assoc % :version version))
{::yres/status 200
::yres/headers {"content-type" "text/plain"}
::yres/body "OK"}))
(defn- add-team-feature
[{:keys [params] :as request}]
(let [team-id (some-> params :team-id d/parse-uuid)
feature (some-> params :feature str)
skip-check (contains? params :skip-check)] skip-check (contains? params :skip-check)]
(when-not (contains? params :force)
(ex/raise :type :validation
:code :missing-force
:hint "missing force checkbox"))
(when (nil? team-id) (when (nil? team-id)
(ex/raise :type :validation (ex/raise :type :validation
:code :invalid-team-id :code :invalid-team-id
:hint "provided invalid team id")) :hint "provided invalid team id"))
(srepl/enable-team-feature! team-id feature :skip-check skip-check) (if (= action "show")
(let [team (db/run! cfg teams/get-team-info {:id team-id})]
{::yres/status 200
::yres/headers {"content-type" "text/plain"}
::yres/body (apply str "Team features:\n"
(->> (:features team)
(map (fn [feature]
(str "- " feature "\n")))))})
{::yres/status 200 (do
::yres/headers {"content-type" "text/plain"} (when-not (contains? params :force)
::yres/body "OK"})) (ex/raise :type :validation
:code :missing-force
:hint "missing force checkbox"))
(defn- remove-team-feature (cond
[{:keys [params] :as request}] (= action "enable")
(let [team-id (some-> params :team-id d/parse-uuid) (srepl/enable-team-feature! team-id feature :skip-check skip-check)
feature (some-> params :feature str)
skip-check (contains? params :skip-check)]
(when-not (contains? params :force) (= action "disable")
(ex/raise :type :validation (srepl/disable-team-feature! team-id feature :skip-check skip-check)
:code :missing-force
:hint "missing force checkbox"))
(when (nil? team-id) :else
(ex/raise :type :validation (ex/raise :type :validation
:code :invalid-team-id :code :invalid-action
:hint "provided invalid team id")) :hint (str "invalid action: " action)))
(srepl/disable-team-feature! team-id feature :skip-check skip-check)
{::yres/status 200 {::yres/status 200
::yres/headers {"content-type" "text/plain"} ::yres/headers {"content-type" "text/plain"}
::yres/body "OK"})) ::yres/body "OK"}))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; VIRTUAL CLOCK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- set-virtual-clock
[_ {:keys [params] :as request}]
(let [offset (some-> params :offset str/trim not-empty ct/duration)
reset? (contains? params :reset)]
(if (= "production" (cf/get :tenant))
{::yres/status 501
::yres/body "OPERATION NOT ALLOWED"}
(do
(if (or reset? (zero? (inst-ms offset)))
(clock/set-offset! nil)
(clock/set-offset! offset))
{::yres/status 302
::yres/headers {"location" "/dbg"}}))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; OTHER SMALL VIEWS/HANDLERS ;; OTHER SMALL VIEWS/HANDLERS
@@ -525,6 +510,25 @@
(ex/raise :type :authentication (ex/raise :type :authentication
:code :only-admins-allowed)))))}) :code :only-admins-allowed)))))})
(def errors
(letfn [(handle-error [cause]
(when-let [data (ex-data cause)]
(when (= :validation (:type data))
(str "Error: " (or (:hint data) (ex-message cause)) "\n"))))]
{:name ::errors
:compile
(fn [& _params]
(fn [handler]
(fn [request]
(try
(handler request)
(catch Throwable cause
(let [body (or (handle-error cause)
(ex/format-throwable cause))]
{::yres/status 400
::yres/headers {"content-type" "text/plain"}
::yres/body body}))))))}))
(defmethod ig/assert-key ::routes (defmethod ig/assert-key ::routes
[_ params] [_ params]
(assert (db/pool? (::db/pool params)) "expected a valid database pool") (assert (db/pool? (::db/pool params)) "expected a valid database pool")
@@ -540,15 +544,14 @@
["/changelog" {:handler (partial changelog-handler cfg)}] ["/changelog" {:handler (partial changelog-handler cfg)}]
["/error/:id" {:handler (partial error-handler cfg)}] ["/error/:id" {:handler (partial error-handler cfg)}]
["/error" {:handler (partial error-list-handler cfg)}] ["/error" {:handler (partial error-list-handler cfg)}]
["/actions/resend-email-verification" ["/actions" {:middleware [[errors]]}
{:handler (partial resend-email-notification cfg)}] ["/set-virtual-clock"
["/actions/reset-file-version" {:handler (partial set-virtual-clock cfg)}]
{:handler (partial reset-file-version cfg)}] ["/resend-email-verification"
["/actions/add-team-feature" {:handler (partial resend-email-notification cfg)}]
{:handler (partial add-team-feature)}] ["/handle-team-features"
["/actions/remove-team-feature" {:handler (partial handle-team-features cfg)}]
{:handler (partial remove-team-feature)}] ["/file-export" {:handler (partial export-handler cfg)}]
["/file/export" {:handler (partial export-handler cfg)}] ["/file-import" {:handler (partial import-handler cfg)}]
["/file/import" {:handler (partial import-handler cfg)}] ["/file-raw-export-import" {:handler (partial raw-export-import-handler cfg)}]]]])
["/file/data" {:handler (partial file-data-handler cfg)}]
["/file/changes" {:handler (partial file-changes-handler cfg)}]]])
+11 -14
View File
@@ -13,6 +13,7 @@
[app.config :as cf] [app.config :as cf]
[app.http :as-alias http] [app.http :as-alias http]
[app.http.access-token :as-alias actoken] [app.http.access-token :as-alias actoken]
[app.http.auth :as-alias auth]
[app.http.session :as-alias session] [app.http.session :as-alias session]
[app.util.inet :as inet] [app.util.inet :as inet]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
@@ -22,18 +23,16 @@
(defn request->context (defn request->context
"Extracts error report relevant context data from request." "Extracts error report relevant context data from request."
[request] [request]
(let [claims (-> {} (let [{:keys [claims] :as auth} (get request ::http/auth-data)]
(into (::session/token-claims request)) (-> (cf/logging-context)
(into (::actoken/token-claims request)))] (assoc :request/path (:path request))
{:request/path (:path request) (assoc :request/method (:method request))
:request/method (:method request) (assoc :request/params (:params request))
:request/params (:params request) (assoc :request/user-agent (yreq/get-header request "user-agent"))
:request/user-agent (yreq/get-header request "user-agent") (assoc :request/ip-addr (inet/parse-request request))
:request/ip-addr (inet/parse-request request) (assoc :request/profile-id (get claims :uid))
:request/profile-id (:uid claims) (assoc :request/auth-data auth)
:version/frontend (or (yreq/get-header request "x-frontend-version") "unknown") (assoc :version/frontend (or (yreq/get-header request "x-frontend-version") "unknown")))))
:version/backend (:full cf/version)}))
(defmulti handle-error (defmulti handle-error
(fn [cause _ _] (fn [cause _ _]
@@ -61,8 +60,6 @@
::yres/body data} ::yres/body data}
(binding [l/*context* (request->context request)] (binding [l/*context* (request->context request)]
(l/err :hint "restriction error"
:cause err)
{::yres/status 400 {::yres/status 400
::yres/body data})))) ::yres/body data}))))
+240
View File
@@ -0,0 +1,240 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.http.management
"Internal mangement HTTP API"
(:require
[app.common.logging :as l]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.time :as ct]
[app.config :as cf]
[app.db :as db]
[app.http.middleware :as mw]
[app.main :as-alias main]
[app.rpc.commands.profile :as cmd.profile]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.worker :as-alias wrk]
[integrant.core :as ig]
[yetti.response :as-alias yres]))
;; ---- ROUTES
(declare ^:private authenticate)
(declare ^:private get-customer)
(declare ^:private update-customer)
(defmethod ig/assert-key ::routes
[_ params]
(assert (db/pool? (::db/pool params)) "expect valid database pool"))
(def ^:private default-system
{:name ::default-system
:compile
(fn [_ _]
(fn [handler cfg]
(fn [request]
(handler cfg request))))})
(def ^:private transaction
{:name ::transaction
:compile
(fn [data _]
(when (:transaction data)
(fn [handler]
(fn [cfg request]
(db/tx-run! cfg handler request)))))})
(defmethod ig/init-key ::routes
[_ {:keys [::setup/props] :as cfg}]
(let [management-key (or (cf/get :management-api-key)
(get props :management-key))]
["" {:middleware [[mw/shared-key-auth management-key]
[default-system cfg]
[transaction]]}
["/authenticate"
{:handler authenticate
:allowed-methods #{:post}}]
["/get-customer"
{:handler get-customer
:transaction true
:allowed-methods #{:post}}]
["/update-customer"
{:handler update-customer
:allowed-methods #{:post}
:transaction true}]]))
;; ---- HELPERS
(defn- coercer
[schema & {:as opts}]
(let [decode-fn (sm/decoder schema sm/json-transformer)
check-fn (sm/check-fn schema opts)]
(fn [data]
(-> data decode-fn check-fn))))
;; ---- API: AUTHENTICATE
(defn- authenticate
[cfg request]
(let [token (-> request :params :token)
result (tokens/verify cfg {:token token :iss "authentication"})]
{::yres/status 200
::yres/body result}))
;; ---- API: GET-CUSTOMER
(def ^:private schema:get-customer
[:map [:id ::sm/uuid]])
(def ^:private coerce-get-customer-params
(coercer schema:get-customer
:type :validation
:hint "invalid data provided for `get-customer` rpc call"))
(def ^:private sql:get-customer-slots
"WITH teams AS (
SELECT tpr.team_id AS id,
tpr.profile_id AS profile_id
FROM team_profile_rel AS tpr
WHERE tpr.is_owner IS true
AND tpr.profile_id = ?
), teams_with_slots AS (
SELECT tpr.team_id AS id,
count(*) AS total
FROM team_profile_rel AS tpr
WHERE tpr.team_id IN (SELECT id FROM teams)
AND tpr.can_edit IS true
GROUP BY 1
ORDER BY 2
)
SELECT max(total) AS total FROM teams_with_slots;")
(defn- get-customer-slots
[cfg profile-id]
(let [result (db/exec-one! cfg [sql:get-customer-slots profile-id])]
(:total result)))
(defn- get-customer
[cfg request]
(let [profile-id (-> request :params coerce-get-customer-params :id)
profile (cmd.profile/get-profile cfg profile-id)
result {:id (get profile :id)
:name (get profile :fullname)
:email (get profile :email)
:num-editors (get-customer-slots cfg profile-id)
:subscription (-> profile :props :subscription)}]
{::yres/status 200
::yres/body result}))
;; ---- API: UPDATE-CUSTOMER
(def ^:private schema:timestamp
(sm/type-schema
{:type ::timestamp
:pred ct/inst?
:type-properties
{:title "inst"
:description "The same as :app.common.time/inst but encodes to epoch"
:error/message "should be an instant"
:gen/gen (->> (sg/small-int)
(sg/fmap (fn [v] (ct/inst v))))
:decode/string ct/inst
:encode/string inst-ms
:decode/json ct/inst
:encode/json inst-ms}}))
(def ^:private schema:subscription
[:map {:title "Subscription"}
[:id ::sm/text]
[:customer-id ::sm/text]
[:type [:enum
"unlimited"
"professional"
"enterprise"]]
[:status [:enum
"active"
"canceled"
"incomplete"
"incomplete_expired"
"past_due"
"paused"
"trialing"
"unpaid"]]
[:billing-period [:enum
"month"
"day"
"week"
"year"]]
[:quantity :int]
[:description [:maybe ::sm/text]]
[:created-at schema:timestamp]
[:start-date [:maybe schema:timestamp]]
[:ended-at [:maybe schema:timestamp]]
[:trial-end [:maybe schema:timestamp]]
[:trial-start [:maybe schema:timestamp]]
[:cancel-at [:maybe schema:timestamp]]
[:canceled-at [:maybe schema:timestamp]]
[:current-period-end [:maybe schema:timestamp]]
[:current-period-start [:maybe schema:timestamp]]
[:cancel-at-period-end :boolean]
[:cancellation-details
[:map {:title "CancellationDetails"}
[:comment [:maybe ::sm/text]]
[:reason [:maybe ::sm/text]]
[:feedback [:maybe
[:enum
"customer_service"
"low_quality"
"missing_feature"
"other"
"switched_service"
"too_complex"
"too_expensive"
"unused"]]]]]])
(def ^:private schema:update-customer
[:map
[:id ::sm/uuid]
[:subscription [:maybe schema:subscription]]])
(def ^:private coerce-update-customer-params
(coercer schema:update-customer
:type :validation
:hint "invalid data provided for `update-customer` rpc call"))
(defn- update-customer
[cfg request]
(let [{:keys [id subscription]}
(-> request :params coerce-update-customer-params)
{:keys [props] :as profile}
(cmd.profile/get-profile cfg id ::db/for-update true)
props
(assoc props :subscription subscription)]
(l/dbg :hint "update customer"
:profile-id (str id)
:subscription-type (get subscription :type)
:subscription-status (get subscription :status)
:subscription-quantity (get subscription :quantity))
(db/update! cfg :profile
{:props (db/tjson props)}
{:id id}
{::db/return-keys false})
{::yres/status 201
::yres/body nil}))
+77
View File
@@ -12,8 +12,11 @@
[app.common.schema :as-alias sm] [app.common.schema :as-alias sm]
[app.common.transit :as t] [app.common.transit :as t]
[app.config :as cf] [app.config :as cf]
[app.http :as-alias http]
[app.http.errors :as errors] [app.http.errors :as errors]
[app.tokens :as tokens]
[app.util.pointer-map :as pmap] [app.util.pointer-map :as pmap]
[buddy.core.codecs :as bc]
[cuerdas.core :as str] [cuerdas.core :as str]
[yetti.adapter :as yt] [yetti.adapter :as yt]
[yetti.middleware :as ymw] [yetti.middleware :as ymw]
@@ -240,3 +243,77 @@
(if (contains? allowed method) (if (contains? allowed method)
(handler request) (handler request)
{::yres/status 405}))))))}) {::yres/status 405}))))))})
(defn- wrap-auth
[handler decoders]
(let [token-re
#"(?i)^(Token|Bearer)\s+(.*)"
get-token-from-authorization
(fn [request]
(when-let [[_ token-type token] (some->> (yreq/get-header request "authorization")
(re-matches token-re))]
(if (= "token" (str/lower token-type))
{:type :token
:token token}
{:type :bearer
:token token})))
get-token-from-cookie
(fn [request]
(let [cname (cf/get :auth-token-cookie-name)
token (some-> (yreq/get-cookie request cname) :value)]
(when-not (str/empty? token)
{:type :cookie
:token token})))
get-token
(some-fn get-token-from-cookie get-token-from-authorization)
process-request
(fn [request]
(if-let [{:keys [type token] :as auth} (get-token request)]
(let [decode-fn (get decoders type)]
(if (or (= type :cookie) (= type :bearer))
(let [metadata (tokens/decode-header token)]
;; NOTE: we only proceed to decode claims on new
;; cookie tokens. The old cookies dont need to be
;; decoded because they use the token string as ID
(if (and (= (:kid metadata) 1)
(= (:ver metadata) 1)
(some? decode-fn))
(assoc request ::http/auth-data (assoc auth
:claims (decode-fn token)
:metadata metadata))
(assoc request ::http/auth-data (assoc auth :metadata {:ver 0}))))
(if decode-fn
(assoc request ::http/auth-data (assoc auth :claims (decode-fn token)))
(assoc request ::http/auth-data auth))))
request))]
(fn [request]
(-> request process-request handler))))
(def auth
{:name ::auth
:compile (constantly wrap-auth)})
(defn- wrap-shared-key-auth
[handler shared-key]
(if shared-key
(let [shared-key (if (string? shared-key)
shared-key
(bc/bytes->b64-str shared-key true))]
(fn [request]
(let [key (yreq/get-header request "x-shared-key")]
(if (= key shared-key)
(handler (assoc request ::http/auth-with-shared-key true))
{::yres/status 403}))))
(fn [_ _]
{::yres/status 403})))
(def shared-key-auth
{:name ::shared-key-auth
:compile (constantly wrap-shared-key-auth)})
+55
View File
@@ -0,0 +1,55 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.http.security
"Additional security layer middlewares"
(:require
[app.config :as cf]
[yetti.request :as yreq]
[yetti.response :as yres]))
(def ^:private safe-methods
#{:get :head :options})
(defn- wrap-sec-fetch-metadata
"Sec-Fetch metadata security layer middleware"
[handler]
(fn [request]
(let [site (yreq/get-header request "sec-fetch-site")]
(cond
(= site "same-origin")
(handler request)
(or (= site "same-site")
(= site "cross-site"))
(if (contains? safe-methods (yreq/method request))
(handler request)
{::yres/status 403})
:else
(handler request)))))
(def sec-fetch-metadata
{:name ::sec-fetch-metadata
:compile (fn [_ _]
(when (contains? cf/flags :sec-fetch-metadata-middleware)
wrap-sec-fetch-metadata))})
(defn- wrap-client-header-check
"Check for a penpot custom header to be present as additional CSRF
protection"
[handler]
(fn [request]
(let [client (yreq/get-header request "x-client")]
(if (some? client)
(handler request)
{::yres/status 403}))))
(def client-header-check
{:name ::client-header-check
:compile (fn [_ _]
(when (contains? cf/flags :client-header-check-middleware)
wrap-client-header-check))})
+178 -198
View File
@@ -10,45 +10,40 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.uri :as u] [app.common.time :as ct]
[app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.db.sql :as sql] [app.db.sql :as sql]
[app.http :as-alias http]
[app.http.auth :as-alias http.auth]
[app.http.session.tasks :as-alias tasks] [app.http.session.tasks :as-alias tasks]
[app.main :as-alias main] [app.main :as-alias main]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.time :as dt]
[cuerdas.core :as str]
[integrant.core :as ig] [integrant.core :as ig]
[yetti.request :as yreq])) [yetti.request :as yreq]
[yetti.response :as yres]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DEFAULTS ;; DEFAULTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A default cookie name for storing the session.
(def default-auth-token-cookie-name "auth-token")
;; A cookie that we can use to check from other sites of the same
;; domain if a user is authenticated.
(def default-auth-data-cookie-name "auth-data")
;; Default value for cookie max-age ;; Default value for cookie max-age
(def default-cookie-max-age (dt/duration {:days 7})) (def default-cookie-max-age (ct/duration {:days 7}))
;; Default age for automatic session renewal ;; Default age for automatic session renewal
(def default-renewal-max-age (dt/duration {:hours 6})) (def default-renewal-max-age (ct/duration {:hours 6}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PROTOCOLS ;; PROTOCOLS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defprotocol ISessionManager (defprotocol ISessionManager
(read [_ key]) (read-session [_ id])
(write! [_ key data]) (create-session [_ params])
(update! [_ data]) (update-session [_ session])
(delete! [_ key])) (delete-session [_ id]))
(defn manager? (defn manager?
[o] [o]
@@ -63,67 +58,82 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private schema:params (def ^:private schema:params
[:map {:title "session-params"} [:map {:title "SessionParams" :closed true}
[:user-agent ::sm/text]
[:profile-id ::sm/uuid] [:profile-id ::sm/uuid]
[:created-at ::sm/inst]]) [:user-agent {:optional true} ::sm/text]
[:sso-provider-id {:optional true} ::sm/uuid]
[:sso-session-id {:optional true} :string]])
(def ^:private valid-params? (def ^:private valid-params?
(sm/validator schema:params)) (sm/validator schema:params))
(defn- prepare-session-params
[key params]
(assert (string? key) "expected key to be a string")
(assert (not (str/blank? key)) "expected key to be not empty")
(assert (valid-params? params) "expected valid params")
{:user-agent (:user-agent params)
:profile-id (:profile-id params)
:created-at (:created-at params)
:updated-at (:created-at params)
:id key})
(defn- database-manager (defn- database-manager
[pool] [pool]
(reify ISessionManager (reify ISessionManager
(read [_ token] (read-session [_ id]
(db/exec-one! pool (sql/select :http-session {:id token}))) (if (string? id)
;; Backward compatibility
(let [session (db/exec-one! pool (sql/select :http-session {:id id}))]
(-> session
(assoc :modified-at (:updated-at session))
(dissoc :updated-at)))
(db/exec-one! pool (sql/select :http-session-v2 {:id id}))))
(write! [_ key params] (create-session [_ params]
(let [params (prepare-session-params key params)] (assert (valid-params? params) "expect valid session params")
(db/insert! pool :http-session params)
params))
(update! [_ params] (let [now (ct/now)
(let [updated-at (dt/now)] params (-> params
(db/update! pool :http-session (assoc :id (uuid/next))
{:updated-at updated-at} (assoc :created-at now)
{:id (:id params)}) (assoc :modified-at now))]
(assoc params :updated-at updated-at))) (db/insert! pool :http-session-v2 params
{::db/return-keys true})))
(delete! [_ token] (update-session [_ session]
(db/delete! pool :http-session {:id token}) (let [modified-at (ct/now)]
(if (string? (:id session))
(db/insert! pool :http-session-v2
(-> session
(assoc :id (uuid/next))
(assoc :created-at modified-at)
(assoc :modified-at modified-at)))
(db/update! pool :http-session-v2
{:modified-at modified-at}
{:id (:id session)}
{::db/return-keys true}))))
(delete-session [_ id]
(if (string? id)
(db/delete! pool :http-session {:id id} {::db/return-keys false})
(db/delete! pool :http-session-v2 {:id id} {::db/return-keys false}))
nil))) nil)))
(defn inmemory-manager (defn inmemory-manager
[] []
(let [cache (atom {})] (let [cache (atom {})]
(reify ISessionManager (reify ISessionManager
(read [_ token] (read-session [_ id]
(get @cache token)) (get @cache id))
(write! [_ key params] (create-session [_ params]
(let [params (prepare-session-params key params)] (assert (valid-params? params) "expect valid session params")
(swap! cache assoc key params)
params))
(update! [_ params] (let [now (ct/now)
(let [updated-at (dt/now)] session (-> params
(swap! cache update (:id params) assoc :updated-at updated-at) (assoc :id (uuid/next))
(assoc params :updated-at updated-at))) (assoc :created-at now)
(assoc :modified-at now))]
(swap! cache assoc (:id session) session)
session))
(delete! [_ token] (update-session [_ session]
(swap! cache dissoc token) (let [modified-at (ct/now)]
(swap! cache update (:id session) assoc :modified-at modified-at)
(assoc session :modified-at modified-at)))
(delete-session [_ id]
(swap! cache dissoc id)
nil)))) nil))))
(defmethod ig/assert-key ::manager (defmethod ig/assert-key ::manager
@@ -143,109 +153,116 @@
;; MANAGER IMPL ;; MANAGER IMPL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare ^:private assign-auth-token-cookie) (declare ^:private assign-session-cookie)
(declare ^:private assign-auth-data-cookie) (declare ^:private clear-session-cookie)
(declare ^:private clear-auth-token-cookie)
(declare ^:private clear-auth-data-cookie) (defn- assign-token
(declare ^:private gen-token) [cfg session]
(let [claims {:iss "authentication"
:aud "penpot"
:sid (:id session)
:iat (:modified-at session)
:uid (:profile-id session)
:sso-provider-id (:sso-provider-id session)
:sso-session-id (:sso-session-id session)}
header {:kid 1 :ver 1}
token (tokens/generate cfg claims header)]
(assoc session :token token)))
(defn create-fn (defn create-fn
[{:keys [::manager ::setup/props]} profile-id] [{:keys [::manager] :as cfg} {profile-id :id :as profile}
& {:keys [sso-provider-id sso-session-id]}]
(assert (manager? manager) "expected valid session manager") (assert (manager? manager) "expected valid session manager")
(assert (uuid? profile-id) "expected valid uuid for profile-id") (assert (uuid? profile-id) "expected valid uuid for profile-id")
(fn [request response] (fn [request response]
(let [uagent (yreq/get-header request "user-agent") (let [uagent (yreq/get-header request "user-agent")
params {:profile-id profile-id session (->> {:user-agent uagent
:user-agent uagent :profile-id profile-id
:created-at (dt/now)} :sso-provider-id sso-provider-id
token (gen-token props params) :sso-session-id sso-session-id}
session (write! manager token params)] (d/without-nils)
(l/trace :hint "create" :profile-id (str profile-id)) (create-session manager)
(-> response (assign-token cfg))]
(assign-auth-token-cookie session)
(assign-auth-data-cookie session))))) (l/trc :hint "create" :id (str (:id session)) :profile-id (str profile-id))
(assign-session-cookie response session))))
(defn delete-fn (defn delete-fn
[{:keys [::manager]}] [{:keys [::manager]}]
(assert (manager? manager) "expected valid session manager") (assert (manager? manager) "expected valid session manager")
(fn [request response] (fn [request response]
(let [cname (cf/get :auth-token-cookie-name default-auth-token-cookie-name) (some->> (get request ::id) (delete-session manager))
cookie (yreq/get-cookie request cname)] (clear-session-cookie response)))
(l/trace :hint "delete" :profile-id (:profile-id request))
(some->> (:value cookie) (delete! manager))
(-> response
(assoc :status 204)
(assoc :body nil)
(clear-auth-token-cookie)
(clear-auth-data-cookie)))))
(defn- gen-token (defn decode-token
[props {:keys [profile-id created-at]}] [cfg token]
(tokens/generate props {:iss "authentication" (try
:iat created-at (tokens/verify cfg {:token token :iss "authentication"})
:uid profile-id})) (catch Throwable cause
(defn- decode-token (l/trc :hint "exception on decoding token"
[props token] :token token
(when token :cause cause))))
(tokens/verify props {:token token :iss "authentication"})))
(defn- get-token (defn get-session
[request] [request]
(let [cname (cf/get :auth-token-cookie-name default-auth-token-cookie-name) (get request ::session))
cookie (some-> (yreq/get-cookie request cname) :value)]
(when-not (str/empty? cookie)
cookie)))
(defn- get-session (defn invalidate-others
[manager token] [cfg session]
(some->> token (read manager))) (let [sql "delete from http_session_v2 where profile_id = ? and id != ?"]
(-> (db/exec-one! cfg [sql (:profile-id session) (:id session)])
(db/get-update-count))))
(defn- renew-session? (defn- renew-session?
[{:keys [updated-at] :as session}] [{:keys [id modified-at] :as session}]
(and (dt/instant? updated-at) (or (string? id)
(let [elapsed (dt/diff updated-at (dt/now))] (and (ct/inst? modified-at)
(neg? (compare default-renewal-max-age elapsed))))) (let [elapsed (ct/diff modified-at (ct/now))]
(neg? (compare default-renewal-max-age elapsed))))))
(defn- wrap-soft-auth
[handler {:keys [::manager ::setup/props]}]
(assert (manager? manager) "expected valid session manager")
(letfn [(handle-request [request]
(try
(let [token (get-token request)
claims (decode-token props token)]
(cond-> request
(map? claims)
(-> (assoc ::token-claims claims)
(assoc ::token token))))
(catch Throwable cause
(l/trace :hint "exception on decoding malformed token" :cause cause)
request)))]
(fn [request]
(handler (handle-request request)))))
(defn- wrap-authz (defn- wrap-authz
[handler {:keys [::manager]}] [handler {:keys [::manager] :as cfg}]
(assert (manager? manager) "expected valid session manager") (assert (manager? manager) "expected valid session manager")
(fn [request] (fn [request]
(let [session (get-session manager (::token request)) (let [{:keys [type token claims metadata]} (get request ::http/auth-data)]
request (cond-> request (cond
(some? session) (= type :cookie)
(assoc ::profile-id (:profile-id session) (let [session (case (:ver metadata)
::id (:id session))) ;; BACKWARD COMPATIBILITY WITH OLD TOKENS
response (handler request)] 0 (read-session manager token)
1 (some->> (:sid claims) (read-session manager))
nil)
(if (renew-session? session) request (cond-> request
(let [session (update! manager session)] (some? session)
(-> response (-> (assoc ::profile-id (:profile-id session))
(assign-auth-token-cookie session) (assoc ::session session)))
(assign-auth-data-cookie session)))
response))))
(def soft-auth response (handler request)]
{:name ::soft-auth
:compile (constantly wrap-soft-auth)}) (if (and session (renew-session? session))
(let [session (->> session
(update-session manager)
(assign-token cfg))]
(assign-session-cookie response session))
response))
(= type :bearer)
(let [session (case (:ver metadata)
;; BACKWARD COMPATIBILITY WITH OLD TOKENS
0 (read-session manager token)
1 (some->> (:sid claims) (read-session manager))
nil)
request (cond-> request
(some? session)
(-> (assoc ::profile-id (:profile-id session))
(assoc ::session session)))]
(handler request))
:else
(handler request)))))
(def authz (def authz
{:name ::authz {:name ::authz
@@ -253,17 +270,17 @@
;; --- IMPL ;; --- IMPL
(defn- assign-auth-token-cookie (defn- assign-session-cookie
[response {token :id updated-at :updated-at}] [response {token :token modified-at :modified-at}]
(let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age) (let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age)
created-at (or updated-at (dt/now)) created-at modified-at
renewal (dt/plus created-at default-renewal-max-age) renewal (ct/plus created-at default-renewal-max-age)
expires (dt/plus created-at max-age) expires (ct/plus created-at max-age)
secure? (contains? cf/flags :secure-session-cookies) secure? (contains? cf/flags :secure-session-cookies)
strict? (contains? cf/flags :strict-session-cookies) strict? (contains? cf/flags :strict-session-cookies)
cors? (contains? cf/flags :cors) cors? (contains? cf/flags :cors)
name (cf/get :auth-token-cookie-name default-auth-token-cookie-name) name (cf/get :auth-token-cookie-name)
comment (str "Renewal at: " (dt/format-instant renewal :rfc1123)) comment (str "Renewal at: " (ct/format-inst renewal :rfc1123))
cookie {:path "/" cookie {:path "/"
:http-only true :http-only true
:expires expires :expires expires
@@ -271,59 +288,21 @@
:comment comment :comment comment
:same-site (if cors? :none (if strict? :strict :lax)) :same-site (if cors? :none (if strict? :strict :lax))
:secure secure?}] :secure secure?}]
(update response :cookies assoc name cookie))) (update response ::yres/cookies assoc name cookie)))
(defn- assign-auth-data-cookie (defn- clear-session-cookie
[response {profile-id :profile-id updated-at :updated-at}]
(let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age)
domain (cf/get :auth-data-cookie-domain)
cname default-auth-data-cookie-name
created-at (or updated-at (dt/now))
renewal (dt/plus created-at default-renewal-max-age)
expires (dt/plus created-at max-age)
comment (str "Renewal at: " (dt/format-instant renewal :rfc1123))
secure? (contains? cf/flags :secure-session-cookies)
strict? (contains? cf/flags :strict-session-cookies)
cors? (contains? cf/flags :cors)
cookie {:domain domain
:expires expires
:path "/"
:comment comment
:value (u/map->query-string {:profile-id profile-id})
:same-site (if cors? :none (if strict? :strict :lax))
:secure secure?}]
(cond-> response
(string? domain)
(update :cookies assoc cname cookie))))
(defn- clear-auth-token-cookie
[response] [response]
(let [cname (cf/get :auth-token-cookie-name default-auth-token-cookie-name)] (let [cname (cf/get :auth-token-cookie-name)]
(update response :cookies assoc cname {:path "/" :value "" :max-age 0}))) (update response ::yres/cookies assoc cname {:path "/" :value "" :max-age 0})))
(defn- clear-auth-data-cookie
[response]
(let [cname default-auth-data-cookie-name
domain (cf/get :auth-data-cookie-domain)]
(cond-> response
(string? domain)
(update :cookies assoc cname {:domain domain :path "/" :value "" :max-age 0}))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TASK: SESSION GC ;; TASK: SESSION GC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FIXME: MOVE
(defmethod ig/assert-key ::tasks/gc (defmethod ig/assert-key ::tasks/gc
[_ params] [_ params]
(assert (db/pool? (::db/pool params)) "expected valid database pool") (assert (db/pool? (::db/pool params)) "expected valid database pool")
(assert (dt/duration? (::tasks/max-age params)))) (assert (ct/duration? (::tasks/max-age params))))
(defmethod ig/expand-key ::tasks/gc (defmethod ig/expand-key ::tasks/gc
[k v] [k v]
@@ -332,22 +311,23 @@
(def ^:private (def ^:private
sql:delete-expired sql:delete-expired
"delete from http_session "DELETE FROM http_session
where updated_at < now() - ?::interval WHERE updated_at < ?::timestamptz
or (updated_at is null and or (updated_at is null and
created_at < now() - ?::interval)") created_at < ?::timestamptz)")
(defn- collect-expired-tasks (defn- collect-expired-tasks
[{:keys [::db/conn ::tasks/max-age]}] [{:keys [::db/conn ::tasks/max-age]}]
(let [interval (db/interval max-age) (let [threshold (ct/minus (ct/now) max-age)
result (db/exec-one! conn [sql:delete-expired interval interval]) result (-> (db/exec-one! conn [sql:delete-expired threshold threshold])
result (:next.jdbc/update-count result)] (db/get-update-count))]
(l/debug :task "gc" (l/dbg :task "gc"
:hint "clean http sessions" :hint "clean http sessions"
:deleted result) :deleted result)
result)) result))
(defmethod ig/init-key ::tasks/gc (defmethod ig/init-key ::tasks/gc
[_ {:keys [::tasks/max-age] :as cfg}] [_ {:keys [::tasks/max-age] :as cfg}]
(l/debug :hint "initializing session gc task" :max-age max-age) (l/dbg :hint "initializing session gc task" :max-age max-age)
(fn [_] (db/tx-run! cfg collect-expired-tasks))) (fn [_]
(db/tx-run! cfg collect-expired-tasks)))
+4 -3
View File
@@ -33,7 +33,7 @@
(println "event:" (d/name name)) (println "event:" (d/name name))
(println "data:" (t/encode-str data {:type :json-verbose})) (println "data:" (t/encode-str data {:type :json-verbose}))
(println))] (println))]
(.getBytes data "UTF-8")) (.getBytes ^String data "UTF-8"))
(catch Throwable cause (catch Throwable cause
(l/err :hint "unexpected error on encoding value on sse stream" (l/err :hint "unexpected error on encoding value on sse stream"
:cause cause) :cause cause)
@@ -44,7 +44,8 @@
(def default-headers (def default-headers
{"Content-Type" "text/event-stream;charset=UTF-8" {"Content-Type" "text/event-stream;charset=UTF-8"
"Cache-Control" "no-cache, no-store, max-age=0, must-revalidate" "Cache-Control" "no-cache, no-store, max-age=0, must-revalidate"
"Pragma" "no-cache"}) "Pragma" "no-cache"
"X-Accel-Buffering" "no"})
(defn response (defn response
[handler & {:keys [buf] :or {buf 32} :as opts}] [handler & {:keys [buf] :or {buf 32} :as opts}]
@@ -54,7 +55,7 @@
::yres/body (yres/stream-body ::yres/body (yres/stream-body
(fn [_ output] (fn [_ output]
(let [channel (sp/chan :buf buf :xf (keep encode)) (let [channel (sp/chan :buf buf :xf (keep encode))
listener (events/start-listener listener (events/spawn-listener
channel channel
(partial write! output) (partial write! output)
(partial pu/close! output))] (partial pu/close! output))]
+3 -3
View File
@@ -11,12 +11,12 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.common.pprint :as pp] [app.common.pprint :as pp]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.http.session :as session] [app.http.session :as session]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.msgbus :as mbus] [app.msgbus :as mbus]
[app.util.time :as dt]
[app.util.websocket :as ws] [app.util.websocket :as ws]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.exec.csp :as sp] [promesa.exec.csp :as sp]
@@ -239,7 +239,7 @@
(defn- on-connect (defn- on-connect
[{:keys [::mtx/metrics]} {:keys [::ws/id] :as wsp}] [{:keys [::mtx/metrics]} {:keys [::ws/id] :as wsp}]
(let [created-at (dt/now)] (let [created-at (ct/now)]
(l/trace :fn "on-connect" :conn-id id) (l/trace :fn "on-connect" :conn-id id)
(swap! state assoc id wsp) (swap! state assoc id wsp)
(mtx/run! metrics (mtx/run! metrics
@@ -253,7 +253,7 @@
(mtx/run! metrics :id :websocket-active-connections :dec 1) (mtx/run! metrics :id :websocket-active-connections :dec 1)
(mtx/run! metrics (mtx/run! metrics
:id :websocket-session-timing :id :websocket-session-timing
:val (/ (inst-ms (dt/diff created-at (dt/now))) 1000.0)))))) :val (/ (inst-ms (ct/diff created-at (ct/now))) 1000.0))))))
(defn- on-rcv-message (defn- on-rcv-message
[{:keys [::mtx/metrics ::profile-id ::session-id]} message] [{:keys [::mtx/metrics ::profile-id ::session-id]} message]
+62 -27
View File
@@ -11,6 +11,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
@@ -23,9 +24,9 @@
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.util.inet :as inet] [app.util.inet :as inet]
[app.util.services :as-alias sv] [app.util.services :as-alias sv]
[app.util.time :as dt]
[app.worker :as wrk] [app.worker :as wrk]
[cuerdas.core :as str])) [cuerdas.core :as str]
[yetti.request :as yreq]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS ;; HELPERS
@@ -78,17 +79,32 @@
(remove #(contains? reserved-props (key %)))) (remove #(contains? reserved-props (key %))))
props)) props))
(defn event-from-rpc-params (defn get-external-session-id
"Create a base event skeleton with pre-filled some important [request]
data that can be extracted from RPC params object" (when-let [session-id (yreq/get-header request "x-external-session-id")]
[params] (when-not (or (> (count session-id) 256)
(let [context {:external-session-id (::rpc/external-session-id params) (= session-id "null")
:external-event-origin (::rpc/external-event-origin params) (str/blank? session-id))
:triggered-by (::rpc/handler-name params)}] session-id)))
{::type "action"
::profile-id (::rpc/profile-id params) (defn- get-client-event-origin
::ip-addr (::rpc/ip-addr params) [request]
::context (d/without-nils context)})) (when-let [origin (yreq/get-header request "x-event-origin")]
(when-not (or (= origin "null")
(str/blank? origin))
(str/prune origin 200))))
(defn get-client-user-agent
[request]
(when-let [user-agent (yreq/get-header request "user-agent")]
(str/prune user-agent 500)))
(defn- get-client-version
[request]
(when-let [origin (yreq/get-header request "x-frontend-version")]
(when-not (or (= origin "null")
(str/blank? origin))
(str/prune origin 100))))
;; --- SPECS ;; --- SPECS
@@ -108,15 +124,42 @@
[::ip-addr {:optional true} ::sm/text] [::ip-addr {:optional true} ::sm/text]
[::props {:optional true} [:map-of :keyword :any]] [::props {:optional true} [:map-of :keyword :any]]
[::context {:optional true} [:map-of :keyword :any]] [::context {:optional true} [:map-of :keyword :any]]
[::tracked-at {:optional true} ::sm/inst] [::tracked-at {:optional true} ::ct/inst]
[::webhooks/event? {:optional true} ::sm/boolean] [::webhooks/event? {:optional true} ::sm/boolean]
[::webhooks/batch-timeout {:optional true} ::dt/duration] [::webhooks/batch-timeout {:optional true} ::ct/duration]
[::webhooks/batch-key {:optional true} [::webhooks/batch-key {:optional true}
[:or ::sm/fn ::sm/text :keyword]]]) [:or ::sm/fn ::sm/text :keyword]]])
(def ^:private check-event (def ^:private check-event
(sm/check-fn schema:event)) (sm/check-fn schema:event))
(defn- prepare-context-from-request
[request]
(let [client-event-origin (get-client-event-origin request)
client-version (get-client-version request)
client-user-agent (get-client-user-agent request)
session-id (get-external-session-id request)
token-id (::actoken/id request)]
(d/without-nils
{:external-session-id session-id
:access-token-id (some-> token-id str)
:client-event-origin client-event-origin
:client-user-agent client-user-agent
:client-version client-version
:version (:full cf/version)})))
(defn event-from-rpc-params
"Create a base event skeleton with pre-filled some important
data that can be extracted from RPC params object"
[params]
(let [context (some-> params meta ::http/request prepare-context-from-request)
event {::type "action"
::profile-id (or (::rpc/profile-id params) uuid/zero)
::ip-addr (::rpc/ip-addr params)}]
(cond-> event
(some? context)
(assoc ::context context))))
(defn prepare-event (defn prepare-event
[cfg mdata params result] [cfg mdata params result]
(let [resultm (meta result) (let [resultm (meta result)
@@ -126,23 +169,15 @@
(::rpc/profile-id params) (::rpc/profile-id params)
uuid/zero) uuid/zero)
session-id (get params ::rpc/external-session-id)
event-origin (get params ::rpc/external-event-origin)
props (-> (or (::replace-props resultm) props (-> (or (::replace-props resultm)
(-> params (-> params
(merge (::props resultm)) (merge (::props resultm))
(dissoc :profile-id) (dissoc :profile-id)
(dissoc :type))) (dissoc :type)))
(clean-props)) (clean-props))
token-id (::actoken/id request) context (merge (::context resultm)
context (-> (::context resultm) (prepare-context-from-request request))
(assoc :external-session-id session-id)
(assoc :external-event-origin event-origin)
(assoc :access-token-id (some-> token-id str))
(d/without-nils))
ip-addr (inet/parse-request request)] ip-addr (inet/parse-request request)]
{::type (or (::type resultm) {::type (or (::type resultm)
@@ -199,7 +234,7 @@
(defn- handle-event! (defn- handle-event!
[cfg event] [cfg event]
(let [params (event->params event) (let [params (event->params event)
tnow (dt/now)] tnow (ct/now)]
(when (contains? cf/flags :audit-log) (when (contains? cf/flags :audit-log)
;; NOTE: this operation may cause primary key conflicts on inserts ;; NOTE: this operation may cause primary key conflicts on inserts
@@ -273,7 +308,7 @@
(let [event (-> (d/without-nils event) (let [event (-> (d/without-nils event)
(check-event))] (check-event))]
(db/run! cfg (fn [cfg] (db/run! cfg (fn [cfg]
(let [tnow (dt/now) (let [tnow (ct/now)
params (-> (event->params event) params (-> (event->params event)
(assoc :created-at tnow) (assoc :created-at tnow)
(update :tracked-at #(or % tnow)))] (update :tracked-at #(or % tnow)))]
@@ -16,7 +16,6 @@
[app.http.client :as http] [app.http.client :as http]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.time :as dt]
[integrant.core :as ig] [integrant.core :as ig]
[lambdaisland.uri :as u] [lambdaisland.uri :as u]
[promesa.exec :as px])) [promesa.exec :as px]))
@@ -53,13 +52,12 @@
(defn- send! (defn- send!
[{:keys [::uri] :as cfg} events] [{:keys [::uri] :as cfg} events]
(let [token (tokens/generate (::setup/props cfg) (let [token (tokens/generate cfg
{:iss "authentication" {:iss "authentication"
:iat (dt/now)
:uid uuid/zero}) :uid uuid/zero})
body (t/encode {:events events}) body (t/encode {:events events})
headers {"content-type" "application/transit+json" headers {"content-type" "application/transit+json"
"origin" (cf/get :public-uri) "origin" (str (cf/get :public-uri))
"cookie" (u/map->query-string {:auth-token token})} "cookie" (u/map->query-string {:auth-token token})}
params {:uri uri params {:uri uri
:timeout 12000 :timeout 12000
+7 -7
View File
@@ -41,7 +41,7 @@
(if (or (instance? java.util.concurrent.CompletionException cause) (if (or (instance? java.util.concurrent.CompletionException cause)
(instance? java.util.concurrent.ExecutionException cause)) (instance? java.util.concurrent.ExecutionException cause))
(-> record (-> record
(assoc ::trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false)) (assoc ::trace (ex/format-throwable cause :data? true :explain? false :header? false :summary? false))
(assoc ::l/cause (ex-cause cause)) (assoc ::l/cause (ex-cause cause))
(record->report)) (record->report))
@@ -49,7 +49,7 @@
ctx (-> context ctx (-> context
(assoc :tenant (cf/get :tenant)) (assoc :tenant (cf/get :tenant))
(assoc :host (cf/get :host)) (assoc :host (cf/get :host))
(assoc :public-uri (cf/get :public-uri)) (assoc :public-uri (str (cf/get :public-uri)))
(assoc :logger/name logger) (assoc :logger/name logger)
(assoc :logger/level level) (assoc :logger/level level)
(dissoc :request/params :value :params :data))] (dissoc :request/params :value :params :data))]
@@ -64,18 +64,18 @@
message)) message))
@message) @message)
:trace (or (::trace record) :trace (or (::trace record)
(some-> cause (ex/format-throwable :data? false :explain? false :header? false :summary? false)))} (some-> cause (ex/format-throwable :data? true :explain? false :header? false :summary? false)))}
(when-let [params (or (:request/params context) (:params context))] (when-let [params (or (:request/params context) (:params context))]
{:params (pp/pprint-str params :length 30 :level 13)}) {:params (pp/pprint-str params :length 20 :level 20)})
(when-let [value (:value context)] (when-let [value (:value context)]
{:value (pp/pprint-str value :length 30 :level 12)}) {:value (pp/pprint-str value :length 30 :level 13)})
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))] (when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
{:data (pp/pprint-str data :length 30 :level 12)}) {:data (pp/pprint-str data :length 30 :level 13)})
(when-let [explain (ex/explain data :length 30 :level 12)] (when-let [explain (ex/explain data :length 30 :level 13)]
{:explain explain}))))) {:explain explain})))))
(defn error-record? (defn error-record?
+4 -4
View File
@@ -10,13 +10,13 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.time :as ct]
[app.common.transit :as t] [app.common.transit :as t]
[app.common.uri :as uri] [app.common.uri :as uri]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.http.client :as http] [app.http.client :as http]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.util.time :as dt]
[app.worker :as wrk] [app.worker :as wrk]
[clojure.data.json :as json] [clojure.data.json :as json]
[cuerdas.core :as str] [cuerdas.core :as str]
@@ -124,7 +124,7 @@
{:id (:id whook)}))) {:id (:id whook)})))
(db/update! pool :webhook (db/update! pool :webhook
{:updated-at (dt/now) {:updated-at (ct/now)
:error-code nil :error-code nil
:error-count 0} :error-count 0}
{:id (:id whook)}))) {:id (:id whook)})))
@@ -132,7 +132,7 @@
(report-delivery! [whook req rsp err] (report-delivery! [whook req rsp err]
(db/insert! pool :webhook-delivery (db/insert! pool :webhook-delivery
{:webhook-id (:id whook) {:webhook-id (:id whook)
:created-at (dt/now) :created-at (ct/now)
:error-code err :error-code err
:req-data (db/tjson req) :req-data (db/tjson req)
:rsp-data (db/tjson rsp)}))] :rsp-data (db/tjson rsp)}))]
@@ -155,7 +155,7 @@
(let [req {:uri (:uri whook) (let [req {:uri (:uri whook)
:headers {"content-type" (:mtype whook) :headers {"content-type" (:mtype whook)
"user-agent" (str/ffmt "penpot/%" (:main cf/version))} "user-agent" (str/ffmt "penpot/%" (:main cf/version))}
:timeout (dt/duration "4s") :timeout (ct/duration "4s")
:method :post :method :post
:body body}] :body body}]
(try (try
+92 -67
View File
@@ -11,6 +11,7 @@
[app.auth.oidc.providers :as-alias oidc.providers] [app.auth.oidc.providers :as-alias oidc.providers]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.time :as ct]
[app.config :as cf] [app.config :as cf]
[app.db :as-alias db] [app.db :as-alias db]
[app.email :as-alias email] [app.email :as-alias email]
@@ -19,7 +20,8 @@
[app.http.awsns :as http.awsns] [app.http.awsns :as http.awsns]
[app.http.client :as-alias http.client] [app.http.client :as-alias http.client]
[app.http.debug :as-alias http.debug] [app.http.debug :as-alias http.debug]
[app.http.session :as-alias session] [app.http.management :as mgmt]
[app.http.session :as session]
[app.http.session.tasks :as-alias session.tasks] [app.http.session.tasks :as-alias session.tasks]
[app.http.websocket :as http.ws] [app.http.websocket :as http.ws]
[app.loggers.webhooks :as-alias webhooks] [app.loggers.webhooks :as-alias webhooks]
@@ -29,7 +31,6 @@
[app.redis :as-alias rds] [app.redis :as-alias rds]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit] [app.rpc.climit :as-alias climit]
[app.rpc.doc :as-alias rpc.doc]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.srepl :as-alias srepl] [app.srepl :as-alias srepl]
[app.storage :as-alias sto] [app.storage :as-alias sto]
@@ -38,8 +39,9 @@
[app.storage.gc-touched :as-alias sto.gc-touched] [app.storage.gc-touched :as-alias sto.gc-touched]
[app.storage.s3 :as-alias sto.s3] [app.storage.s3 :as-alias sto.s3]
[app.svgo :as-alias svgo] [app.svgo :as-alias svgo]
[app.util.time :as dt] [app.util.cron]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[app.worker.executor]
[clojure.test :as test] [clojure.test :as test]
[clojure.tools.namespace.repl :as repl] [clojure.tools.namespace.repl :as repl]
[cuerdas.core :as str] [cuerdas.core :as str]
@@ -146,23 +148,11 @@
::mdef/labels [] ::mdef/labels []
::mdef/type :histogram} ::mdef/type :histogram}
:executors-active-threads :http-server-dispatch-timing
{::mdef/name "penpot_executors_active_threads" {::mdef/name "penpot_http_server_dispatch_timing"
::mdef/help "Current number of threads available in the executor service." ::mdef/help "Histogram of dispatch handler"
::mdef/labels ["name"] ::mdef/labels []
::mdef/type :gauge} ::mdef/type :histogram}})
:executors-completed-tasks
{::mdef/name "penpot_executors_completed_tasks_total"
::mdef/help "Approximate number of completed tasks by the executor."
::mdef/labels ["name"]
::mdef/type :counter}
:executors-running-threads
{::mdef/name "penpot_executors_running_threads"
::mdef/help "Current number of threads with state RUNNING."
::mdef/labels ["name"]
::mdef/type :gauge}})
(def system-config (def system-config
{::db/pool {::db/pool
@@ -174,14 +164,12 @@
::db/max-size (cf/get :database-max-pool-size 60) ::db/max-size (cf/get :database-max-pool-size 60)
::mtx/metrics (ig/ref ::mtx/metrics)} ::mtx/metrics (ig/ref ::mtx/metrics)}
;; Default thread pool for IO operations ;; Default netty IO pool (shared between several services)
::wrk/executor ::wrk/netty-io-executor
{} {:threads (cf/get :netty-io-threads)}
::wrk/monitor ::wrk/netty-executor
{::mtx/metrics (ig/ref ::mtx/metrics) {:threads (cf/get :executor-threads)}
::wrk/executor (ig/ref ::wrk/executor)
::wrk/name "default"}
:app.migrations/migrations :app.migrations/migrations
{::db/pool (ig/ref ::db/pool)} {::db/pool (ig/ref ::db/pool)}
@@ -192,17 +180,27 @@
::mtx/routes ::mtx/routes
{::mtx/metrics (ig/ref ::mtx/metrics)} {::mtx/metrics (ig/ref ::mtx/metrics)}
::rds/redis ::rds/client
{::rds/uri (cf/get :redis-uri) {::rds/uri
::mtx/metrics (ig/ref ::mtx/metrics) (cf/get :redis-uri)
::wrk/executor (ig/ref ::wrk/executor)}
::wrk/netty-executor
(ig/ref ::wrk/netty-executor)
::wrk/netty-io-executor
(ig/ref ::wrk/netty-io-executor)}
::rds/pool
{::rds/client (ig/ref ::rds/client)
::mtx/metrics (ig/ref ::mtx/metrics)}
::mbus/msgbus ::mbus/msgbus
{::wrk/executor (ig/ref ::wrk/executor) {::wrk/executor (ig/ref ::wrk/netty-executor)
::rds/redis (ig/ref ::rds/redis)} ::rds/client (ig/ref ::rds/client)
::mtx/metrics (ig/ref ::mtx/metrics)}
:app.storage.tmp/cleaner :app.storage.tmp/cleaner
{::wrk/executor (ig/ref ::wrk/executor)} {::wrk/executor (ig/ref ::wrk/netty-executor)}
::sto.gc-deleted/handler ::sto.gc-deleted/handler
{::db/pool (ig/ref ::db/pool) {::db/pool (ig/ref ::db/pool)
@@ -230,8 +228,10 @@
::http/host (cf/get :http-server-host) ::http/host (cf/get :http-server-host)
::http/router (ig/ref ::http/router) ::http/router (ig/ref ::http/router)
::http/io-threads (cf/get :http-server-io-threads) ::http/io-threads (cf/get :http-server-io-threads)
::http/max-worker-threads (cf/get :http-server-max-worker-threads)
::http/max-body-size (cf/get :http-server-max-body-size) ::http/max-body-size (cf/get :http-server-max-body-size)
::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)} ::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)
::mtx/metrics (ig/ref ::mtx/metrics)}
::ldap/provider ::ldap/provider
{:host (cf/get :ldap-host) {:host (cf/get :ldap-host)
@@ -259,26 +259,33 @@
::oidc.providers/generic ::oidc.providers/generic
{::http.client/client (ig/ref ::http.client/client)} {::http.client/client (ig/ref ::http.client/client)}
::oidc/providers
[(ig/ref ::oidc.providers/google)
(ig/ref ::oidc.providers/github)
(ig/ref ::oidc.providers/gitlab)
(ig/ref ::oidc.providers/generic)]
::oidc/routes ::oidc/routes
{::http.client/client (ig/ref ::http.client/client) {::http.client/client (ig/ref ::http.client/client)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::setup/props (ig/ref ::setup/props) ::setup/props (ig/ref ::setup/props)
::oidc/providers {:google (ig/ref ::oidc.providers/google) ::oidc/providers (ig/ref ::oidc/providers)
:github (ig/ref ::oidc.providers/github)
:gitlab (ig/ref ::oidc.providers/gitlab)
:oidc (ig/ref ::oidc.providers/generic)}
::session/manager (ig/ref ::session/manager) ::session/manager (ig/ref ::session/manager)
::email/blacklist (ig/ref ::email/blacklist) ::email/blacklist (ig/ref ::email/blacklist)
::email/whitelist (ig/ref ::email/whitelist)} ::email/whitelist (ig/ref ::email/whitelist)}
::mgmt/routes
{::db/pool (ig/ref ::db/pool)
::setup/props (ig/ref ::setup/props)}
:app.http/router :app.http/router
{::session/manager (ig/ref ::session/manager) {::session/manager (ig/ref ::session/manager)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::rpc/routes (ig/ref ::rpc/routes) ::rpc/routes (ig/ref ::rpc/routes)
::rpc.doc/routes (ig/ref ::rpc.doc/routes)
::setup/props (ig/ref ::setup/props) ::setup/props (ig/ref ::setup/props)
::mtx/routes (ig/ref ::mtx/routes) ::mtx/routes (ig/ref ::mtx/routes)
::oidc/routes (ig/ref ::oidc/routes) ::oidc/routes (ig/ref ::oidc/routes)
::mgmt/routes (ig/ref ::mgmt/routes)
::http.debug/routes (ig/ref ::http.debug/routes) ::http.debug/routes (ig/ref ::http.debug/routes)
::http.assets/routes (ig/ref ::http.assets/routes) ::http.assets/routes (ig/ref ::http.assets/routes)
::http.ws/routes (ig/ref ::http.ws/routes) ::http.ws/routes (ig/ref ::http.ws/routes)
@@ -294,33 +301,35 @@
{::db/pool (ig/ref ::db/pool) {::db/pool (ig/ref ::db/pool)
::mtx/metrics (ig/ref ::mtx/metrics) ::mtx/metrics (ig/ref ::mtx/metrics)
::mbus/msgbus (ig/ref ::mbus/msgbus) ::mbus/msgbus (ig/ref ::mbus/msgbus)
::setup/props (ig/ref ::setup/props)
::session/manager (ig/ref ::session/manager)} ::session/manager (ig/ref ::session/manager)}
:app.http.assets/routes :app.http.assets/routes
{::http.assets/path (cf/get :assets-path) {::http.assets/path (cf/get :assets-path)
::http.assets/cache-max-age (dt/duration {:hours 24}) ::http.assets/cache-max-age (ct/duration {:hours 24})
::http.assets/cache-max-agesignature-max-age (dt/duration {:hours 24 :minutes 5}) ::http.assets/cache-max-agesignature-max-age (ct/duration {:hours 24 :minutes 5})
::sto/storage (ig/ref ::sto/storage)} ::sto/storage (ig/ref ::sto/storage)}
::rpc/climit ::rpc/climit
{::mtx/metrics (ig/ref ::mtx/metrics) {::mtx/metrics (ig/ref ::mtx/metrics)
::wrk/executor (ig/ref ::wrk/executor) ::wrk/executor (ig/ref ::wrk/netty-executor)
::climit/config (cf/get :rpc-climit-config) ::climit/config (cf/get :rpc-climit-config)
::climit/enabled (contains? cf/flags :rpc-climit)} ::climit/enabled (contains? cf/flags :rpc-climit)}
:app.rpc/rlimit :app.rpc/rlimit
{::wrk/executor (ig/ref ::wrk/executor)} {::wrk/executor (ig/ref ::wrk/netty-executor)}
:app.rpc/methods :app.rpc/methods
{::http.client/client (ig/ref ::http.client/client) {::http.client/client (ig/ref ::http.client/client)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::wrk/executor (ig/ref ::wrk/executor) ::rds/pool (ig/ref ::rds/pool)
::wrk/executor (ig/ref ::wrk/netty-executor)
::session/manager (ig/ref ::session/manager) ::session/manager (ig/ref ::session/manager)
::ldap/provider (ig/ref ::ldap/provider) ::ldap/provider (ig/ref ::ldap/provider)
::sto/storage (ig/ref ::sto/storage) ::sto/storage (ig/ref ::sto/storage)
::mtx/metrics (ig/ref ::mtx/metrics) ::mtx/metrics (ig/ref ::mtx/metrics)
::mbus/msgbus (ig/ref ::mbus/msgbus) ::mbus/msgbus (ig/ref ::mbus/msgbus)
::rds/redis (ig/ref ::rds/redis) ::rds/client (ig/ref ::rds/client)
::rpc/climit (ig/ref ::rpc/climit) ::rpc/climit (ig/ref ::rpc/climit)
::rpc/rlimit (ig/ref ::rpc/rlimit) ::rpc/rlimit (ig/ref ::rpc/rlimit)
@@ -330,14 +339,26 @@
::email/blacklist (ig/ref ::email/blacklist) ::email/blacklist (ig/ref ::email/blacklist)
::email/whitelist (ig/ref ::email/whitelist)} ::email/whitelist (ig/ref ::email/whitelist)}
:app.rpc.doc/routes :app.rpc/management-methods
{:app.rpc/methods (ig/ref :app.rpc/methods)} {::http.client/client (ig/ref ::http.client/client)
::db/pool (ig/ref ::db/pool)
::rds/pool (ig/ref ::rds/pool)
::wrk/executor (ig/ref ::wrk/netty-executor)
::session/manager (ig/ref ::session/manager)
::sto/storage (ig/ref ::sto/storage)
::mtx/metrics (ig/ref ::mtx/metrics)
::mbus/msgbus (ig/ref ::mbus/msgbus)
::rds/client (ig/ref ::rds/client)
::setup/props (ig/ref ::setup/props)}
:app.rpc/routes ::rpc/routes
{::rpc/methods (ig/ref :app.rpc/methods) {::rpc/methods (ig/ref :app.rpc/methods)
::db/pool (ig/ref ::db/pool) ::rpc/management-methods (ig/ref :app.rpc/management-methods)
::session/manager (ig/ref ::session/manager)
::setup/props (ig/ref ::setup/props)} ;; FIXME: revisit if db/pool is necessary here
::db/pool (ig/ref ::db/pool)
::session/manager (ig/ref ::session/manager)
::setup/props (ig/ref ::setup/props)}
::wrk/registry ::wrk/registry
{::mtx/metrics (ig/ref ::mtx/metrics) {::mtx/metrics (ig/ref ::mtx/metrics)
@@ -425,6 +446,9 @@
;; module requires the migrations to run before initialize. ;; module requires the migrations to run before initialize.
::migrations (ig/ref :app.migrations/migrations)} ::migrations (ig/ref :app.migrations/migrations)}
::setup/clock
{}
:app.loggers.audit.archive-task/handler :app.loggers.audit.archive-task/handler
{::setup/props (ig/ref ::setup/props) {::setup/props (ig/ref ::setup/props)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
@@ -468,49 +492,50 @@
(cf/get :objects-storage-s3-bucket)) (cf/get :objects-storage-s3-bucket))
::sto.s3/io-threads (or (cf/get :storage-assets-s3-io-threads) ::sto.s3/io-threads (or (cf/get :storage-assets-s3-io-threads)
(cf/get :objects-storage-s3-io-threads)) (cf/get :objects-storage-s3-io-threads))
::wrk/executor (ig/ref ::wrk/executor)}
::wrk/netty-io-executor
(ig/ref ::wrk/netty-io-executor)}
:app.storage.fs/backend :app.storage.fs/backend
{::sto.fs/directory (or (cf/get :storage-assets-fs-directory) {::sto.fs/directory (or (cf/get :storage-assets-fs-directory)
(cf/get :objects-storage-fs-directory))}}) (cf/get :objects-storage-fs-directory))}})
(def worker-config (def worker-config
{::wrk/cron {::wrk/cron
{::wrk/registry (ig/ref ::wrk/registry) {::wrk/registry (ig/ref ::wrk/registry)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::wrk/entries ::wrk/entries
[{:cron #app/cron "0 0 0 * * ?" ;; daily [{:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :session-gc} :task :session-gc}
{:cron #app/cron "0 0 0 * * ?" ;; daily {:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :objects-gc} :task :objects-gc}
{:cron #app/cron "0 0 0 * * ?" ;; daily {:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :storage-gc-deleted} :task :storage-gc-deleted}
{:cron #app/cron "0 0 0 * * ?" ;; daily {:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :storage-gc-touched} :task :storage-gc-touched}
{:cron #app/cron "0 0 0 * * ?" ;; daily {:cron #penpot/cron "0 0 0 * * ?" ;; daily
:task :tasks-gc} :task :tasks-gc}
{:cron #app/cron "0 0 2 * * ?" ;; daily {:cron #penpot/cron "0 0 2 * * ?" ;; daily
:task :file-gc-scheduler} :task :file-gc-scheduler}
{:cron #app/cron "0 30 */3,23 * * ?" {:cron #penpot/cron "0 30 */3,23 * * ?"
:task :telemetry} :task :telemetry}
(when (contains? cf/flags :audit-log-archive) (when (contains? cf/flags :audit-log-archive)
{:cron #app/cron "0 */5 * * * ?" ;; every 5m {:cron #penpot/cron "0 */5 * * * ?" ;; every 5m
:task :audit-log-archive}) :task :audit-log-archive})
(when (contains? cf/flags :audit-log-gc) (when (contains? cf/flags :audit-log-gc)
{:cron #app/cron "30 */5 * * * ?" ;; every 5m {:cron #penpot/cron "30 */5 * * * ?" ;; every 5m
:task :audit-log-gc})]} :task :audit-log-gc})]}
::wrk/dispatcher ::wrk/dispatcher
{::rds/redis (ig/ref ::rds/redis) {::rds/client (ig/ref ::rds/client)
::mtx/metrics (ig/ref ::mtx/metrics) ::mtx/metrics (ig/ref ::mtx/metrics)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::wrk/tenant (cf/get :tenant)} ::wrk/tenant (cf/get :tenant)}
@@ -519,7 +544,7 @@
{::wrk/parallelism (cf/get ::worker-default-parallelism 1) {::wrk/parallelism (cf/get ::worker-default-parallelism 1)
::wrk/queue :default ::wrk/queue :default
::wrk/tenant (cf/get :tenant) ::wrk/tenant (cf/get :tenant)
::rds/redis (ig/ref ::rds/redis) ::rds/client (ig/ref ::rds/client)
::wrk/registry (ig/ref ::wrk/registry) ::wrk/registry (ig/ref ::wrk/registry)
::mtx/metrics (ig/ref ::mtx/metrics) ::mtx/metrics (ig/ref ::mtx/metrics)
::db/pool (ig/ref ::db/pool)} ::db/pool (ig/ref ::db/pool)}
@@ -528,7 +553,7 @@
{::wrk/parallelism (cf/get ::worker-webhook-parallelism 1) {::wrk/parallelism (cf/get ::worker-webhook-parallelism 1)
::wrk/queue :webhooks ::wrk/queue :webhooks
::wrk/tenant (cf/get :tenant) ::wrk/tenant (cf/get :tenant)
::rds/redis (ig/ref ::rds/redis) ::rds/client (ig/ref ::rds/client)
::wrk/registry (ig/ref ::wrk/registry) ::wrk/registry (ig/ref ::wrk/registry)
::mtx/metrics (ig/ref ::mtx/metrics) ::mtx/metrics (ig/ref ::mtx/metrics)
::db/pool (ig/ref ::db/pool)}}) ::db/pool (ig/ref ::db/pool)}})
+108 -23
View File
@@ -10,17 +10,20 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.media :as cm] [app.common.media :as cm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.schema.openapi :as-alias oapi] [app.common.schema.openapi :as-alias oapi]
[app.common.time :as ct]
[app.config :as cf] [app.config :as cf]
[app.db :as-alias db] [app.db :as-alias db]
[app.http.client :as http]
[app.storage :as-alias sto] [app.storage :as-alias sto]
[app.storage.tmp :as tmp] [app.storage.tmp :as tmp]
[app.util.time :as dt]
[buddy.core.bytes :as bb] [buddy.core.bytes :as bb]
[buddy.core.codecs :as bc] [buddy.core.codecs :as bc]
[clojure.java.shell :as sh] [clojure.java.shell :as sh]
[clojure.string]
[clojure.xml :as xml] [clojure.xml :as xml]
[cuerdas.core :as str] [cuerdas.core :as str]
[datoteka.fs :as fs] [datoteka.fs :as fs]
@@ -32,19 +35,19 @@
javax.xml.parsers.SAXParserFactory javax.xml.parsers.SAXParserFactory
org.apache.commons.io.IOUtils org.apache.commons.io.IOUtils
org.im4java.core.ConvertCmd org.im4java.core.ConvertCmd
org.im4java.core.IMOperation org.im4java.core.IMOperation))
org.im4java.core.Info))
(def default-max-file-size
(* 1024 1024 10)) ; 10 MiB
(def schema:upload (def schema:upload
(sm/register! [:map {:title "Upload"}
^{::sm/type ::upload} [:filename :string]
[:map {:title "Upload"} [:size ::sm/int]
[:filename :string] [:path ::fs/path]
[:size ::sm/int] [:mtype {:optional true} :string]
[:path ::fs/path] [:headers {:optional true}
[:mtype {:optional true} :string] [:map-of :string :string]]])
[:headers {:optional true}
[:map-of :string :string]]]))
(def ^:private schema:input (def ^:private schema:input
[:map {:title "Input"} [:map {:title "Input"}
@@ -116,7 +119,7 @@
(defn- parse-svg (defn- parse-svg
[text] [text]
(let [text (strip-doctype text)] (let [text (strip-doctype text)]
(dm/with-open [istream (IOUtils/toInputStream text "UTF-8")] (dm/with-open [istream (IOUtils/toInputStream ^String text "UTF-8")]
(xml/parse istream secure-parser-factory)))) (xml/parse istream secure-parser-factory))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -215,6 +218,24 @@
{:width (int width) {:width (int width)
:height (int height)})))])) :height (int height)})))]))
(defn- get-dimensions-with-orientation [^String path]
;; Image magick doesn't give info about exif rotation so we use the identify command
;; If we are processing an animated gif we use the first frame with -scene 0
(let [dim-result (sh/sh "identify" "-format" "%w %h\n" path)
orient-result (sh/sh "identify" "-format" "%[EXIF:Orientation]\n" path)]
(when (= 0 (:exit dim-result))
(let [[w h] (-> (:out dim-result)
str/trim
(clojure.string/split #"\s+")
(->> (mapv #(Integer/parseInt %))))
orientation-exit (:exit orient-result)
orientation (-> orient-result :out str/trim)]
(if (= 0 orientation-exit)
(case orientation
("6" "8") {:width h :height w} ; Rotated 90 or 270 degrees
{:width w :height h}) ; Normal or unknown orientation
{:width w :height h}))))) ; If orientation can't be read, use dimensions as-is
(defmethod process :info (defmethod process :info
[{:keys [input] :as params}] [{:keys [input] :as params}]
(let [{:keys [path mtype] :as input} (check-input input)] (let [{:keys [path mtype] :as input} (check-input input)]
@@ -224,23 +245,39 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :invalid-svg-file :code :invalid-svg-file
:hint "uploaded svg does not provides dimensions")) :hint "uploaded svg does not provides dimensions"))
(merge input info {:ts (dt/now)})) (merge input info {:ts (ct/now) :size (fs/size path)}))
(let [instance (Info. (str path)) (let [path-str (str path)
mtype' (.getProperty instance "Mime type")] identify-res (sh/sh "identify" "-format" "image/%[magick]\n" path-str)
;; identify prints one line per frame (animated GIFs, etc.); we take the first one
mtype' (if (zero? (:exit identify-res))
(-> identify-res
:out
str/trim
(str/split #"\s+" 2)
first
str/lower)
(ex/raise :type :validation
:code :invalid-image
:hint "invalid image"))
{:keys [width height]}
(or (get-dimensions-with-orientation path-str)
(do
(l/warn "Failed to read image dimensions with orientation" {:path path})
(ex/raise :type :validation
:code :invalid-image
:hint "invalid image")))]
(when (and (string? mtype) (when (and (string? mtype)
(not= mtype mtype')) (not= (str/lower mtype) mtype'))
(ex/raise :type :validation (ex/raise :type :validation
:code :media-type-mismatch :code :media-type-mismatch
:hint (str "Seems like you are uploading a file whose content does not match the extension." :hint (str "Seems like you are uploading a file whose content does not match the extension."
"Expected: " mtype ". Got: " mtype'))) "Expected: " mtype ". Got: " mtype')))
;; For an animated GIF, getImageWidth/Height returns the delta size of one frame (if no frame given
;; it returns size of the last one), whereas getPageWidth/Height always return the full size of
;; any frame.
(assoc input (assoc input
:width (.getPageWidth instance) :width width
:height (.getPageHeight instance) :height height
:ts (dt/now)))))) :size (fs/size path)
:ts (ct/now))))))
(defmethod process-error org.im4java.core.InfoException (defmethod process-error org.im4java.core.InfoException
[error] [error]
@@ -249,6 +286,54 @@
:hint "invalid image" :hint "invalid image"
:cause error)) :cause error))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; IMAGE HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn download-image
"Download an image from the provided URI and return the media input object"
[{:keys [::http/client]} uri]
(letfn [(parse-and-validate [{:keys [headers] :as response}]
(let [size (some-> (get headers "content-length") d/parse-integer)
mtype (get headers "content-type")
format (cm/mtype->format mtype)
max-size (cf/get :media-max-file-size default-max-file-size)]
(when-not size
(ex/raise :type :validation
:code :unknown-size
:hint "seems like the url points to resource with unknown size"))
(when (> size max-size)
(ex/raise :type :validation
:code :file-too-large
:hint (str/ffmt "the file size % is greater than the maximum %"
size
default-max-file-size)))
(when (nil? format)
(ex/raise :type :validation
:code :media-type-not-allowed
:hint "seems like the url points to an invalid media object"))
{:size size :mtype mtype :format format}))]
(let [{:keys [body] :as response} (http/req! client
{:method :get :uri uri}
{:response-type :input-stream})
{:keys [size mtype]} (parse-and-validate response)
path (tmp/tempfile :prefix "penpot.media.download.")
written (io/write* path body :size size)]
(when (not= written size)
(ex/raise :type :internal
:code :mismatch-write-size
:hint "unexpected state: unable to write to file"))
{;; :size size
:path path
:mtype mtype})))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FONTS ;; FONTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+19 -1
View File
@@ -438,7 +438,25 @@
:fn (mg/resource "app/migrations/sql/0138-mod-file-data-fragment-table.sql")} :fn (mg/resource "app/migrations/sql/0138-mod-file-data-fragment-table.sql")}
{:name "0139-mod-file-change-table.sql" {:name "0139-mod-file-change-table.sql"
:fn (mg/resource "app/migrations/sql/0139-mod-file-change-table.sql")}]) :fn (mg/resource "app/migrations/sql/0139-mod-file-change-table.sql")}
{:name "0140-mod-file-change-table.sql"
:fn (mg/resource "app/migrations/sql/0140-mod-file-change-table.sql")}
{:name "0140-add-locked-by-column-to-file-change-table"
:fn (mg/resource "app/migrations/sql/0140-add-locked-by-column-to-file-change-table.sql")}
{:name "0141-add-idx-to-file-library-rel"
:fn (mg/resource "app/migrations/sql/0141-add-idx-to-file-library-rel.sql")}
{:name "0141-add-file-data-table.sql"
:fn (mg/resource "app/migrations/sql/0141-add-file-data-table.sql")}
{:name "0142-add-sso-provider-table"
:fn (mg/resource "app/migrations/sql/0142-add-sso-provider-table.sql")}
{:name "0143-http-session-v2-table"
:fn (mg/resource "app/migrations/sql/0143-add-http-session-v2-table.sql")}])
(defn apply-migrations! (defn apply-migrations!
[pool name migrations] [pool name migrations]
+14 -13
View File
@@ -10,8 +10,8 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.pprint] [app.common.pprint]
[app.srepl.fixes.media-refs :refer [process-file]]
[app.srepl.main :as srepl] [app.srepl.main :as srepl]
[app.srepl.procs.media-refs]
[clojure.edn :as edn])) [clojure.edn :as edn]))
(def ^:private required-services (def ^:private required-services
@@ -20,7 +20,10 @@
:app.storage/storage :app.storage/storage
:app.metrics/metrics :app.metrics/metrics
:app.db/pool :app.db/pool
:app.worker/executor]) :app.worker/netty-io-executor])
(def default-options
{:rollback? false})
(defn -main (defn -main
[& [options]] [& [options]]
@@ -28,22 +31,20 @@
(let [config-var (requiring-resolve 'app.main/system-config) (let [config-var (requiring-resolve 'app.main/system-config)
start-var (requiring-resolve 'app.main/start-custom) start-var (requiring-resolve 'app.main/start-custom)
stop-var (requiring-resolve 'app.main/stop) stop-var (requiring-resolve 'app.main/stop)
config (select-keys @config-var required-services)]
config (select-keys @config-var required-services)
options (if (string? options)
(ex/ignoring (edn/read-string options))
{})
options (-> (merge default-options options)
(assoc :proc-fn #'app.srepl.procs.media-refs/fix-media-refs))]
(start-var config) (start-var config)
(l/inf :hint "executing media-refs migration" :options options)
(let [options (if (string? options) (srepl/process! options)
(ex/ignoring (edn/read-string options))
{})]
(l/inf :hint "executing media-refs migration" :options options)
(srepl/process-files! process-file options))
(stop-var) (stop-var)
(System/exit 0)) (System/exit 0))
(catch Throwable cause (catch Throwable cause
(ex/print-throwable cause) (ex/print-throwable cause)
(flush) (flush)
(System/exit -1)))) (System/exit -1))))
@@ -0,0 +1,11 @@
-- Add locked_by column to file_change table for version locking feature
-- This allows users to lock their own saved versions to prevent deletion by others
ALTER TABLE file_change
ADD COLUMN locked_by uuid NULL REFERENCES profile(id) ON DELETE SET NULL DEFERRABLE;
-- Create index for locked versions queries
CREATE INDEX file_change__locked_by__idx ON file_change (locked_by) WHERE locked_by IS NOT NULL;
-- Add comment for documentation
COMMENT ON COLUMN file_change.locked_by IS 'Profile ID of user who has locked this version. Only the creator can lock/unlock their own versions. Locked versions cannot be deleted by others.';
@@ -0,0 +1,2 @@
ALTER TABLE file_change
ADD COLUMN migrations text[];
@@ -0,0 +1,38 @@
CREATE TABLE file_data (
file_id uuid NOT NULL REFERENCES file(id) DEFERRABLE,
id uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
modified_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL,
type text NOT NULL,
backend text NULL,
metadata jsonb NULL,
data bytea NULL,
PRIMARY KEY (file_id, id)
) PARTITION BY HASH (file_id);
CREATE TABLE file_data_00 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 0);
CREATE TABLE file_data_01 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 1);
CREATE TABLE file_data_02 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 2);
CREATE TABLE file_data_03 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 3);
CREATE TABLE file_data_04 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 4);
CREATE TABLE file_data_05 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 5);
CREATE TABLE file_data_06 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 6);
CREATE TABLE file_data_07 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 7);
CREATE TABLE file_data_08 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 8);
CREATE TABLE file_data_09 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 9);
CREATE TABLE file_data_10 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 10);
CREATE TABLE file_data_11 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 11);
CREATE TABLE file_data_12 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 12);
CREATE TABLE file_data_13 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 13);
CREATE TABLE file_data_14 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 14);
CREATE TABLE file_data_15 PARTITION OF file_data FOR VALUES WITH (MODULUS 16, REMAINDER 15);
CREATE INDEX file_data__deleted_at__idx
ON file_data (deleted_at, file_id, id)
WHERE deleted_at IS NOT NULL;
@@ -0,0 +1,2 @@
CREATE INDEX IF NOT EXISTS file_library_rel__library_file_id__idx
ON file_library_rel (library_file_id);
@@ -0,0 +1,33 @@
CREATE TABLE sso_provider (
id uuid PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now(),
modified_at timestamptz NOT NULL DEFAULT now(),
is_enabled boolean NOT NULL DEFAULT true,
type text NOT NULL CHECK (type IN ('oidc')),
domain text NOT NULL,
client_id text NOT NULL,
client_secret text NOT NULL,
base_uri text NOT NULL,
token_uri text NULL,
auth_uri text NULL,
user_uri text NULL,
jwks_uri text NULL,
logout_uri text NULL,
roles_attr text NULL,
email_attr text NULL,
name_attr text NULL,
user_info_source text NOT NULL DEFAULT 'token'
CHECK (user_info_source IN ('token', 'userinfo', 'auto')),
scopes text[] NULL,
roles text[] NULL
);
CREATE UNIQUE INDEX sso_provider__domain__idx
ON sso_provider(domain);
@@ -0,0 +1,23 @@
CREATE TABLE http_session_v2 (
id uuid PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now(),
modified_at timestamptz NOT NULL DEFAULT now(),
profile_id uuid REFERENCES profile(id) ON DELETE CASCADE,
user_agent text NULL,
sso_provider_id uuid NULL REFERENCES sso_provider(id) ON DELETE CASCADE,
sso_session_id text NULL
);
CREATE INDEX http_session_v2__profile_id__idx
ON http_session_v2(profile_id);
CREATE INDEX http_session_v2__sso_provider_id__idx
ON http_session_v2(sso_provider_id)
WHERE sso_provider_id IS NOT NULL;
CREATE INDEX http_session_v2__sso_session_id__idx
ON http_session_v2(sso_session_id)
WHERE sso_session_id IS NOT NULL;
+14 -14
View File
@@ -10,13 +10,12 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.transit :as t] [app.common.transit :as t]
[app.config :as cfg] [app.config :as cfg]
[app.redis :as rds] [app.redis :as rds]
[app.util.time :as dt]
[app.worker :as wrk] [app.worker :as wrk]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.core :as p]
[promesa.exec :as px] [promesa.exec :as px]
[promesa.exec.csp :as sp])) [promesa.exec.csp :as sp]))
@@ -56,17 +55,19 @@
[k v] [k v]
{k (-> (d/without-nils v) {k (-> (d/without-nils v)
(assoc ::buffer-size 128) (assoc ::buffer-size 128)
(assoc ::timeout (dt/duration {:seconds 30})))}) (assoc ::timeout (ct/duration {:seconds 30})))})
(def ^:private schema:params (def ^:private schema:params
[:map ::rds/redis ::wrk/executor]) [:map
::rds/client
::wrk/executor])
(defmethod ig/assert-key ::msgbus (defmethod ig/assert-key ::msgbus
[_ params] [_ params]
(assert (sm/check schema:params params))) (assert (sm/check schema:params params)))
(defmethod ig/init-key ::msgbus (defmethod ig/init-key ::msgbus
[_ {:keys [::buffer-size ::wrk/executor ::timeout ::rds/redis] :as cfg}] [_ {:keys [::buffer-size ::wrk/executor ::timeout] :as cfg}]
(l/info :hint "initialize msgbus" :buffer-size buffer-size) (l/info :hint "initialize msgbus" :buffer-size buffer-size)
(let [cmd-ch (sp/chan :buf buffer-size) (let [cmd-ch (sp/chan :buf buffer-size)
rcv-ch (sp/chan :buf (sp/dropping-buffer buffer-size)) rcv-ch (sp/chan :buf (sp/dropping-buffer buffer-size))
@@ -74,8 +75,9 @@
:xf xform-prefix-topic) :xf xform-prefix-topic)
state (agent {}) state (agent {})
pconn (rds/connect redis :type :default :timeout timeout) ;; Open persistent connections to redis
sconn (rds/connect redis :type :pubsub :timeout timeout) pconn (rds/connect cfg :timeout timeout)
sconn (rds/connect-pubsub cfg :timeout timeout)
_ (set-error-handler! state #(l/error :cause % :hint "unexpected error on agent" ::l/sync? true)) _ (set-error-handler! state #(l/error :cause % :hint "unexpected error on agent" ::l/sync? true))
_ (set-error-mode! state :continue) _ (set-error-mode! state :continue)
@@ -189,14 +191,13 @@
(defn- create-listener (defn- create-listener
[rcv-ch] [rcv-ch]
(rds/pubsub-listener {:on-message (fn [_ topic message]
:on-message (fn [_ topic message]
;; There are no back pressure, so we use a slidding ;; There are no back pressure, so we use a slidding
;; buffer for cases when the pubsub broker sends ;; buffer for cases when the pubsub broker sends
;; more messages that we can process. ;; more messages that we can process.
(let [val {:topic topic :message (t/decode message)}] (let [val {:topic topic :message (t/decode-str message)}]
(when-not (sp/offer! rcv-ch val) (when-not (sp/offer! rcv-ch val)
(l/warn :msg "dropping message on subscription loop")))))) (l/warn :msg "dropping message on subscription loop"))))})
(defn- process-input (defn- process-input
[{:keys [::state ::wrk/executor] :as cfg} topic message] [{:keys [::state ::wrk/executor] :as cfg} topic message]
@@ -216,8 +217,7 @@
(rds/add-listener sconn (create-listener rcv-ch)) (rds/add-listener sconn (create-listener rcv-ch))
(px/thread (px/thread
{:name "penpot/msgbus/io-loop" {:name "penpot/msgbus"}
:virtual true}
(try (try
(loop [] (loop []
(let [timeout-ch (sp/timeout-chan 1000) (let [timeout-ch (sp/timeout-chan 1000)
@@ -263,7 +263,7 @@
intended to be used in core.async go blocks." intended to be used in core.async go blocks."
[{:keys [::pconn] :as cfg} {:keys [topic message]}] [{:keys [::pconn] :as cfg} {:keys [topic message]}]
(try (try
(p/await! (rds/publish pconn topic (t/encode message))) (rds/publish pconn topic (t/encode-str message))
(catch InterruptedException cause (catch InterruptedException cause
(throw cause)) (throw cause))
(catch Throwable cause (catch Throwable cause
+471 -251
View File
@@ -6,23 +6,22 @@
(ns app.redis (ns app.redis
"The msgbus abstraction implemented using redis as underlying backend." "The msgbus abstraction implemented using redis as underlying backend."
(:refer-clojure :exclude [eval]) (:refer-clojure :exclude [eval get set run!])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.generic-pool :as gpool]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.redis.script :as-alias rscript] [app.redis.script :as-alias rscript]
[app.util.cache :as cache] [app.worker :as wrk]
[app.util.time :as dt] [app.worker.executor]
[app.worker :as-alias wrk]
[clojure.core :as c] [clojure.core :as c]
[clojure.java.io :as io] [clojure.java.io :as io]
[cuerdas.core :as str] [cuerdas.core :as str]
[integrant.core :as ig] [integrant.core :as ig])
[promesa.core :as p]
[promesa.exec :as px])
(:import (:import
clojure.lang.MapEntry clojure.lang.MapEntry
io.lettuce.core.KeyValue io.lettuce.core.KeyValue
@@ -32,12 +31,10 @@
io.lettuce.core.RedisException io.lettuce.core.RedisException
io.lettuce.core.RedisURI io.lettuce.core.RedisURI
io.lettuce.core.ScriptOutputType io.lettuce.core.ScriptOutputType
io.lettuce.core.api.StatefulConnection io.lettuce.core.SetArgs
io.lettuce.core.api.StatefulRedisConnection io.lettuce.core.api.StatefulRedisConnection
io.lettuce.core.api.async.RedisAsyncCommands
io.lettuce.core.api.async.RedisScriptingAsyncCommands
io.lettuce.core.api.sync.RedisCommands io.lettuce.core.api.sync.RedisCommands
io.lettuce.core.codec.ByteArrayCodec io.lettuce.core.api.sync.RedisScriptingCommands
io.lettuce.core.codec.RedisCodec io.lettuce.core.codec.RedisCodec
io.lettuce.core.codec.StringCodec io.lettuce.core.codec.StringCodec
io.lettuce.core.pubsub.RedisPubSubListener io.lettuce.core.pubsub.RedisPubSubListener
@@ -45,244 +42,238 @@
io.lettuce.core.pubsub.api.sync.RedisPubSubCommands io.lettuce.core.pubsub.api.sync.RedisPubSubCommands
io.lettuce.core.resource.ClientResources io.lettuce.core.resource.ClientResources
io.lettuce.core.resource.DefaultClientResources io.lettuce.core.resource.DefaultClientResources
io.netty.channel.nio.NioEventLoopGroup
io.netty.util.HashedWheelTimer io.netty.util.HashedWheelTimer
io.netty.util.Timer io.netty.util.Timer
io.netty.util.concurrent.EventExecutorGroup
java.lang.AutoCloseable java.lang.AutoCloseable
java.time.Duration)) java.time.Duration))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(declare ^:private initialize-resources) (def ^:const MAX-EVAL-RETRIES 18)
(declare ^:private shutdown-resources)
(declare ^:private impl-eval)
(defprotocol IRedis (def default-timeout
(-connect [_ options]) (ct/duration "10s"))
(-get-or-connect [_ key options]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; IMPL & PRIVATE API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defprotocol IConnection (defprotocol IConnection
(publish [_ topic message]) (-set-timeout [_ timeout] "set connection timeout")
(rpush [_ key payload]) (-get-timeout [_] "get current timeout")
(blpop [_ timeout keys]) (-reset-timeout [_] "reset to default timeout"))
(eval [_ script]))
(defprotocol IDefaultConnection
"Public API of default redis connection"
(-publish [_ topic message])
(-rpush [_ key payload])
(-blpop [_ timeout keys])
(-eval [_ script])
(-get [_ key])
(-set [_ key val args])
(-del [_ key-or-keys])
(-ping [_]))
(defprotocol IPubSubConnection (defprotocol IPubSubConnection
(add-listener [_ listener]) (-add-listener [_ listener])
(subscribe [_ topics]) (-subscribe [_ topics])
(unsubscribe [_ topics])) (-unsubscribe [_ topics]))
(def default-codec (def ^:private default-codec
(RedisCodec/of StringCodec/UTF8 ByteArrayCodec/INSTANCE))
(def string-codec
(RedisCodec/of StringCodec/UTF8 StringCodec/UTF8)) (RedisCodec/of StringCodec/UTF8 StringCodec/UTF8))
(sm/register! (defn- impl-eval
{:type ::connection [cmd cache metrics script]
:pred #(satisfies? IConnection %) (let [keys (into-array String (map str (::rscript/keys script)))
:type-properties vals (into-array String (map str (::rscript/vals script)))
{:title "connection" sname (::rscript/name script)
:description "redis connection instance"}})
(sm/register! read-script
{:type ::pubsub-connection (fn []
:pred #(satisfies? IPubSubConnection %) (-> script ::rscript/path io/resource slurp))
:type-properties
{:title "connection"
:description "redis connection instance"}})
(defn redis? load-script
[o] (fn []
(satisfies? IRedis o)) (let [id (.scriptLoad ^RedisScriptingCommands cmd
^String (read-script))]
(swap! cache assoc sname id)
(l/trc :hint "load script" :name sname :id id)
(sm/register! id))
{:type ::redis
:pred redis?})
(def ^:private schema:script eval-script
[:map {:title "script"} (fn [id]
[::rscript/name qualified-keyword?] (try
[::rscript/path ::sm/text] (let [tpoint (ct/tpoint)
[::rscript/keys {:optional true} [:vector :any]] result (.evalsha ^RedisScriptingCommands cmd
[::rscript/vals {:optional true} [:vector :any]]]) ^String id
^ScriptOutputType ScriptOutputType/MULTI
^"[Ljava.lang.String;" keys
^"[Ljava.lang.String;" vals)
elapsed (tpoint)]
(def valid-script? (mtx/run! metrics {:id :redis-eval-timing
(sm/lazy-validator schema:script)) :labels [(name sname)]
:val (inst-ms elapsed)})
(defmethod ig/expand-key ::redis (l/trc :hint "eval script"
[k v] :name (name sname)
(let [cpus (px/get-available-processors) :id id
threads (max 1 (int (* cpus 0.2)))] :params (str/join "," (::rscript/vals script))
{k (-> (d/without-nils v) :elapsed (ct/format-duration elapsed))
(assoc ::timeout (dt/duration "10s"))
(assoc ::io-threads (max 3 threads))
(assoc ::worker-threads (max 3 threads)))}))
(def ^:private schema:redis-params result)
[:map {:title "redis-params"}
::wrk/executor
::mtx/metrics
[::uri ::sm/uri]
[::worker-threads ::sm/int]
[::io-threads ::sm/int]
[::timeout ::dt/duration]])
(defmethod ig/assert-key ::redis (catch io.lettuce.core.RedisNoScriptException _cause
[_ params] ::load)
(assert (sm/check schema:redis-params params)))
(defmethod ig/init-key ::redis (catch Throwable cause
[_ params] (when-let [on-error (::rscript/on-error script)]
(initialize-resources params)) (on-error cause))
(throw cause))))
(defmethod ig/halt-key! ::redis eval-script'
[_ instance] (fn [id]
(d/close! instance)) (loop [id id
retries 0]
(if (> retries MAX-EVAL-RETRIES)
(ex/raise :type :internal
:code ::max-eval-retries-reached
:hint (str "unable to eval redis script " sname))
(let [result (eval-script id)]
(if (= result ::load)
(recur (load-script)
(inc retries))
result)))))]
(defn- initialize-resources (if-let [id (c/get @cache sname)]
"Initialize redis connection resources" (eval-script' id)
[{:keys [::uri ::io-threads ::worker-threads ::wrk/executor ::mtx/metrics] :as params}] (-> (load-script)
(eval-script')))))
(l/inf :hint "initialize redis resources" (deftype Connection [^StatefulRedisConnection conn
:uri (str uri) ^RedisCommands cmd
:io-threads io-threads ^Duration timeout
:worker-threads worker-threads) cache metrics]
AutoCloseable
(close [_]
(ex/ignoring (.close conn)))
(let [timer (HashedWheelTimer.) IConnection
resources (.. (DefaultClientResources/builder) (-set-timeout [_ timeout]
(ioThreadPoolSize ^long io-threads) (.setTimeout conn ^Duration timeout))
(computationThreadPoolSize ^long worker-threads)
(timer ^Timer timer)
(build))
redis-uri (RedisURI/create ^String (str uri)) (-reset-timeout [_]
(.setTimeout conn timeout))
shutdown (fn [client conn] (-get-timeout [_]
(ex/ignoring (.close ^StatefulConnection conn)) (.getTimeout conn))
(ex/ignoring (.close ^RedisClient client))
(l/trc :hint "disconnect" :hid (hash client)))
on-remove (fn [key val cause] IDefaultConnection
(l/trace :hint "evict connection (cache)" :key key :reason cause) (-publish [_ topic message]
(some-> val d/close!)) (.publish cmd ^String topic ^String message))
cache (cache/create :executor executor (-rpush [_ key elements]
:on-remove on-remove (try
:keepalive "5m")] (let [vals (make-array String (count elements))]
(reify (loop [i 0 xs (seq elements)]
java.lang.AutoCloseable (when xs
(close [_] (aset ^"[[Ljava.lang.String;" vals i ^String (first xs))
(ex/ignoring (cache/invalidate! cache)) (recur (inc i) (next xs))))
(ex/ignoring (.shutdown ^ClientResources resources))
(ex/ignoring (.stop ^Timer timer)))
IRedis (.rpush cmd
(-get-or-connect [this key options] ^String key
(let [create (fn [_] (-connect this options))] ^"[[Ljava.lang.String;" vals))
(cache/get cache key create)))
(-connect [_ options] (catch RedisCommandInterruptedException cause
(let [timeout (or (:timeout options) (::timeout params)) (throw (InterruptedException. (ex-message cause))))))
codec (get options :codec default-codec)
type (get options :type :default)
client (RedisClient/create ^ClientResources resources
^RedisURI redis-uri)]
(l/trc :hint "connect" :hid (hash client)) (-blpop [_ keys timeout]
(if (= type :pubsub) (try
(let [conn (.connectPubSub ^RedisClient client (let [keys (into-array String keys)]
^RedisCodec codec)] (when-let [res (.blpop cmd
(.setTimeout ^StatefulConnection conn ^double timeout
^Duration timeout) ^"[Ljava.lang.String;" keys)]
(reify (MapEntry/create
IPubSubConnection (.getKey ^KeyValue res)
(add-listener [_ listener] (.getValue ^KeyValue res))))
(assert (instance? RedisPubSubListener listener) "expected listener instance") (catch RedisCommandInterruptedException cause
(.addListener ^StatefulRedisPubSubConnection conn (throw (InterruptedException. (ex-message cause))))))
^RedisPubSubListener listener))
(subscribe [_ topics] (-get [_ key]
(try (assert (string? key) "key expected to be string")
(let [topics (into-array String (map str topics)) (.get cmd ^String key))
cmd (.sync ^StatefulRedisPubSubConnection conn)]
(.subscribe ^RedisPubSubCommands cmd topics))
(catch RedisCommandInterruptedException cause
(throw (InterruptedException. (ex-message cause))))))
(unsubscribe [_ topics] (-set [_ key val args]
(try (.set cmd
(let [topics (into-array String (map str topics)) ^String key
cmd (.sync ^StatefulRedisPubSubConnection conn)] ^bytes val
(.unsubscribe ^RedisPubSubCommands cmd topics)) ^SetArgs args))
(catch RedisCommandInterruptedException cause
(throw (InterruptedException. (ex-message cause)))))) (-del [_ keys]
(let [keys (into-array String keys)]
(.del cmd ^String/1 keys)))
(-ping [_]
(.ping cmd))
(-eval [_ script]
(impl-eval cmd cache metrics script)))
AutoCloseable (deftype SubscriptionConnection [^StatefulRedisPubSubConnection conn
(close [_] (shutdown client conn)))) ^RedisPubSubCommands cmd
^Duration timeout]
AutoCloseable
(close [_]
(ex/ignoring (.close conn)))
(let [conn (.connect ^RedisClient client ^RedisCodec codec)] IConnection
(.setTimeout ^StatefulConnection conn ^Duration timeout) (-set-timeout [_ timeout]
(reify (.setTimeout conn ^Duration timeout))
IConnection
(publish [_ topic message]
(assert (string? topic) "expected topic to be string")
(assert (bytes? message) "expected message to be a byte array")
(let [pcomm (.async ^StatefulRedisConnection conn)] (-reset-timeout [_]
(.publish ^RedisAsyncCommands pcomm ^String topic ^bytes message))) (.setTimeout conn timeout))
(rpush [_ key payload] (-get-timeout [_]
(assert (or (and (vector? payload) (.getTimeout conn))
(every? bytes? payload))
(bytes? payload)))
(try
(let [cmd (.sync ^StatefulRedisConnection conn)
data (if (vector? payload) payload [payload])
vals (make-array (. Class (forName "[B")) (count data))]
(loop [i 0 xs (seq data)] IPubSubConnection
(when xs (-add-listener [_ listener]
(aset ^"[[B" vals i ^bytes (first xs)) (.addListener conn ^RedisPubSubListener listener))
(recur (inc i) (next xs))))
(.rpush ^RedisCommands cmd (-subscribe [_ topics]
^String key (try
^"[[B" vals)) (let [topics (into-array String topics)]
(.subscribe cmd topics))
(catch RedisCommandInterruptedException cause
(throw (InterruptedException. (ex-message cause))))))
(catch RedisCommandInterruptedException cause (-unsubscribe [_ topics]
(throw (InterruptedException. (ex-message cause)))))) (try
(let [topics (into-array String topics)]
(.unsubscribe cmd topics))
(catch RedisCommandInterruptedException cause
(throw (InterruptedException. (ex-message cause)))))))
(blpop [_ timeout keys] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(try ;; PUBLIC API
(let [keys (into-array Object (map str keys)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cmd (.sync ^StatefulRedisConnection conn)
timeout (/ (double (inst-ms timeout)) 1000.0)]
(when-let [res (.blpop ^RedisCommands cmd
^double timeout
^"[Ljava.lang.String;" keys)]
(MapEntry/create
(.getKey ^KeyValue res)
(.getValue ^KeyValue res))))
(catch RedisCommandInterruptedException cause
(throw (InterruptedException. (ex-message cause))))))
(eval [_ script] (defn build-set-args
(assert (valid-script? script) "expected valid script") [options]
(impl-eval conn metrics script)) (reduce-kv (fn [^SetArgs args k v]
(case k
AutoCloseable :ex (if (instance? Duration v)
(close [_] (shutdown client conn)))))))))) (.ex args ^Duration v)
(.ex args (long v)))
(defn connect :px (.px args (long v))
[instance & {:as opts}] :nx (if v (.nx args) args)
(assert (satisfies? IRedis instance) "expected valid redis instance") :keep-ttl (if v (.keepttl args) args)))
(-connect instance opts)) (SetArgs.)
options))
(defn get-or-connect
[instance key & {:as opts}]
(assert (satisfies? IRedis instance) "expected valid redis instance")
(-get-or-connect instance key opts))
(defn pubsub-listener (defn pubsub-listener
[& {:keys [on-message on-subscribe on-unsubscribe]}] [& {:keys [on-message on-subscribe on-unsubscribe]}]
@@ -311,61 +302,172 @@
(when on-unsubscribe (when on-unsubscribe
(on-unsubscribe nil topic count))))) (on-unsubscribe nil topic count)))))
(def ^:private scripts-cache (atom {})) (defn connect
[cfg & {:as options}]
(assert (contains? cfg ::mtx/metrics) "missing ::mtx/metrics on provided system")
(assert (contains? cfg ::client) "missing ::rds/client on provided system")
(defn- impl-eval (let [state (::client cfg)
[^StatefulRedisConnection connection metrics script]
(let [cmd (.async ^StatefulRedisConnection connection)
keys (into-array String (map str (::rscript/keys script)))
vals (into-array String (map str (::rscript/vals script)))
sname (::rscript/name script)]
(letfn [(on-error [cause] cache (::cache state)
(if (instance? io.lettuce.core.RedisNoScriptException cause) client (::client state)
(do timeout (or (some-> (:timeout options) ct/duration)
(l/error :hint "no script found" :name sname :cause cause) (::timeout state))
(->> (load-script)
(p/mcat eval-script)))
(if-let [on-error (::rscript/on-error script)]
(on-error cause)
(p/rejected cause))))
(eval-script [sha] conn (.connect ^RedisClient client
(let [tpoint (dt/tpoint)] ^RedisCodec default-codec)
(->> (.evalsha ^RedisScriptingAsyncCommands cmd cmd (.sync ^StatefulRedisConnection conn)]
^String sha
^ScriptOutputType ScriptOutputType/MULTI
^"[Ljava.lang.String;" keys
^"[Ljava.lang.String;" vals)
(p/fmap (fn [result]
(let [elapsed (tpoint)]
(mtx/run! metrics {:id :redis-eval-timing
:labels [(name sname)]
:val (inst-ms elapsed)})
(l/trace :hint "eval script"
:name (name sname)
:sha sha
:params (str/join "," (::rscript/vals script))
:elapsed (dt/format-duration elapsed))
result)))
(p/merr on-error))))
(read-script [] (.setTimeout ^StatefulRedisConnection conn ^Duration timeout)
(-> script ::rscript/path io/resource slurp)) (->Connection conn cmd timeout cache (::mtx/metrics cfg))))
(load-script [] (defn connect-pubsub
(l/trace :hint "load script" :name sname) [cfg & {:as options}]
(->> (.scriptLoad ^RedisScriptingAsyncCommands cmd (let [state (::client cfg)
^String (read-script)) client (::client state)
(p/fmap (fn [sha]
(swap! scripts-cache assoc sname sha)
sha))))]
(p/await! timeout (or (some-> (:timeout options) ct/duration)
(if-let [sha (get @scripts-cache sname)] (::timeout state))
(eval-script sha) conn (.connectPubSub ^RedisClient client
(->> (load-script) ^RedisCodec default-codec)
(p/mapcat eval-script))))))) cmd (.sync ^StatefulRedisPubSubConnection conn)]
(.setTimeout ^StatefulRedisPubSubConnection conn
^Duration timeout)
(->SubscriptionConnection conn cmd timeout)))
(defn get
[conn key]
(assert (string? key) "key must be string instance")
(try
(-get conn key)
(catch RedisCommandTimeoutException cause
(l/err :hint "timeout on get redis key" :key key :cause cause)
nil)))
(defn set
([conn key val]
(set conn key val nil))
([conn key val args]
(assert (string? key) "key must be string instance")
(assert (string? val) "val must be string instance")
(let [args (cond
(or (instance? SetArgs args)
(nil? args))
args
(map? args)
(build-set-args args)
:else
(throw (IllegalArgumentException. "invalid args")))]
(try
(-set conn key val args)
(catch RedisCommandTimeoutException cause
(l/err :hint "timeout on set redis key" :key key :cause cause)
nil)))))
(defn del
[conn key-or-keys]
(let [keys (if (vector? key-or-keys) key-or-keys [key-or-keys])]
(assert (every? string? keys) "only string keys allowed")
(try
(-del conn keys)
(catch RedisCommandTimeoutException cause
(l/err :hint "timeout on del redis key" :key key :cause cause)
nil))))
(defn ping
[conn]
(-ping conn))
(defn blpop
[conn key-or-keys timeout]
(let [keys (if (vector? key-or-keys) key-or-keys [key-or-keys])
timeout (cond
(ct/duration? timeout)
(/ (double (inst-ms timeout)) 1000.0)
(double? timeout)
timeout
(int? timeout)
(/ (double timeout) 1000.0)
:else
0)]
(assert (every? string? keys) "only string keys allowed")
(-blpop conn keys timeout)))
(defn rpush
[conn key elements]
(assert (string? key) "key must be string instance")
(assert (every? string? elements) "elements should be all strings")
(let [elements (vec elements)]
(-rpush conn key elements)))
(defn publish
[conn topic payload]
(assert (string? topic) "expected topic to be string")
(assert (string? payload) "expected message to be a byte array")
(-publish conn topic payload))
(def ^:private schema:script
[:map {:title "script"}
[::rscript/name qualified-keyword?]
[::rscript/path ::sm/text]
[::rscript/keys {:optional true} [:vector :any]]
[::rscript/vals {:optional true} [:vector :any]]])
(def ^:private valid-script?
(sm/lazy-validator schema:script))
(defn eval
[conn script]
(assert (valid-script? script) "expected valid script")
(-eval conn script))
(defn add-listener
[conn listener]
(let [listener (cond
(map? listener)
(pubsub-listener listener)
(instance? RedisPubSubListener listener)
listener
:else
(throw (IllegalArgumentException. "invalid listener provided")))]
(-add-listener conn listener)))
(defn subscribe
[conn topic-or-topics]
(let [topics (if (vector? topic-or-topics) topic-or-topics [topic-or-topics])]
(assert (every? string? topics))
(-subscribe conn topics)))
(defn unsubscribe
[conn topic-or-topics]
(let [topics (if (vector? topic-or-topics) topic-or-topics [topic-or-topics])]
(assert (every? string? topics))
(-unsubscribe conn topics)))
(defn set-timeout
[conn timeout]
(let [timeout (ct/duration timeout)]
(-set-timeout conn timeout)))
(defn get-timeout
[conn]
(-get-timeout conn))
(defn reset-timeout
[conn]
(-reset-timeout conn))
(defn timeout-exception? (defn timeout-exception?
[cause] [cause]
@@ -374,3 +476,121 @@
(defn exception? (defn exception?
[cause] [cause]
(instance? RedisException cause)) (instance? RedisException cause))
(defn get-pooled
[cfg]
(let [pool (::pool cfg)]
(gpool/get pool)))
(defn close
[o]
(.close ^AutoCloseable o))
(defn pool
[cfg & {:as options}]
(gpool/create :create-fn (partial connect cfg options)
:destroy-fn close
:dispose-fn -reset-timeout))
(defn run!
[cfg f & args]
(if (gpool/pool? cfg)
(apply f {::pool cfg} f args)
(let [pool (::pool cfg)]
(with-open [^AutoCloseable conn (gpool/get pool)]
(apply f (assoc cfg ::conn @conn) args)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZATION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod ig/expand-key ::client
[k v]
{k (-> (d/without-nils v)
(assoc ::timeout (ct/duration "10s")))})
(def ^:private schema:client
[:map {:title "RedisClient"}
[::timer [:fn #(instance? HashedWheelTimer %)]]
[::cache ::sm/atom]
[::timeout ::ct/duration]
[::resources [:fn #(instance? DefaultClientResources %)]]])
(def check-client
(sm/check-fn schema:client))
(sm/register! ::client schema:client)
(sm/register!
{:type ::pool
:pred gpool/pool?})
(def ^:private schema:client-params
[:map {:title "redis-params"}
::wrk/netty-io-executor
::wrk/netty-executor
[::uri ::sm/uri]
[::timeout ::ct/duration]])
(def ^:private check-client-params
(sm/check-fn schema:client-params))
(defmethod ig/assert-key ::client
[_ params]
(check-client-params params))
(defmethod ig/init-key ::client
[_ {:keys [::uri ::wrk/netty-io-executor ::wrk/netty-executor] :as params}]
(l/inf :hint "initialize redis client" :uri (str uri))
(let [timer (HashedWheelTimer.)
cache (atom {})
resources (.. (DefaultClientResources/builder)
(eventExecutorGroup ^EventExecutorGroup netty-executor)
;; We provide lettuce with a shared event loop
;; group instance instead of letting lettuce to
;; create its own
(eventLoopGroupProvider
(reify io.lettuce.core.resource.EventLoopGroupProvider
(allocate [_ _] netty-io-executor)
(threadPoolSize [_]
(.executorCount ^NioEventLoopGroup netty-io-executor))
(release [_ _ _ _ _]
;; Do nothing
)
(shutdown [_ _ _ _]
;; Do nothing
)))
(timer ^Timer timer)
(build))
redis-uri (RedisURI/create ^String (str uri))
client (RedisClient/create ^ClientResources resources
^RedisURI redis-uri)]
{::client client
::cache cache
::timer timer
::timeout default-timeout
::resources resources}))
(defmethod ig/halt-key! ::client
[_ {:keys [::client ::timer ::resources]}]
(ex/ignoring (.shutdown ^RedisClient client))
(ex/ignoring (.shutdown ^ClientResources resources))
(ex/ignoring (.stop ^Timer timer)))
(defmethod ig/assert-key ::pool
[_ {:keys [::client]}]
(check-client client))
(defmethod ig/init-key ::pool
[_ cfg]
(pool cfg {:timeout (ct/duration 2000)}))
(defmethod ig/halt-key! ::pool
[_ instance]
(.close ^java.lang.AutoCloseable instance))
+165 -70
View File
@@ -12,18 +12,25 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.time :as ct]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.http :as-alias http] [app.http :as-alias http]
[app.http.access-token :as actoken] [app.http.access-token :as actoken]
[app.http.client :as-alias http.client] [app.http.client :as-alias http.client]
[app.http.middleware :as mw]
[app.http.security :as sec]
[app.http.session :as session] [app.http.session :as session]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.main :as-alias main] [app.main :as-alias main]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.msgbus :as-alias mbus] [app.msgbus :as-alias mbus]
[app.redis :as rds]
[app.rpc.climit :as climit] [app.rpc.climit :as climit]
[app.rpc.cond :as cond] [app.rpc.cond :as cond]
[app.rpc.doc :as doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
[app.rpc.retry :as retry] [app.rpc.retry :as retry]
[app.rpc.rlimit :as rlimit] [app.rpc.rlimit :as rlimit]
@@ -31,11 +38,9 @@
[app.storage :as-alias sto] [app.storage :as-alias sto]
[app.util.inet :as inet] [app.util.inet :as inet]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.core :as p]
[yetti.request :as yreq] [yetti.request :as yreq]
[yetti.response :as yres])) [yetti.response :as yres]))
@@ -43,7 +48,7 @@
(defn- default-handler (defn- default-handler
[_] [_]
(p/rejected (ex/error :type :not-found))) (ex/raise :type :not-found))
(defn- handle-response-transformation (defn- handle-response-transformation
[response request mdata] [response request mdata]
@@ -63,74 +68,69 @@
(let [mdata (meta result) (let [mdata (meta result)
response (if (fn? result) response (if (fn? result)
(result request) (result request)
(let [result (rph/unwrap result)] (let [result (rph/unwrap result)
{::yres/status (::http/status mdata 200) status (or (::http/status mdata)
::yres/headers (::http/headers mdata {}) (if (nil? result)
204
200))
headers (cond-> (::http/headers mdata {})
(yres/stream-body? result)
(assoc "content-type" "application/octet-stream"))]
{::yres/status status
::yres/headers headers
::yres/body result}))] ::yres/body result}))]
(-> response (-> response
(handle-response-transformation request mdata) (handle-response-transformation request mdata)
(handle-before-comple-hook mdata)))) (handle-before-comple-hook mdata))))
(defn get-external-session-id (defn- make-rpc-handler
[request]
(when-let [session-id (yreq/get-header request "x-external-session-id")]
(when-not (or (> (count session-id) 256)
(= session-id "null")
(str/blank? session-id))
session-id)))
(defn- get-external-event-origin
[request]
(when-let [origin (yreq/get-header request "x-event-origin")]
(when-not (or (> (count origin) 256)
(= origin "null")
(str/blank? origin))
origin)))
(defn- rpc-handler
"Ring handler that dispatches cmd requests and convert between "Ring handler that dispatches cmd requests and convert between
internal async flow into ring async flow." internal async flow into ring async flow."
[methods {:keys [params path-params method] :as request}] [methods]
(let [handler-name (:type path-params) (let [methods (update-vals methods peek)]
etag (yreq/get-header request "if-none-match") (fn [{:keys [params path-params method] :as request}]
profile-id (or (::session/profile-id request) (let [handler-name (:type path-params)
(::actoken/profile-id request)) etag (yreq/get-header request "if-none-match")
profile-id (or (::session/profile-id request)
(::actoken/profile-id request)
(if (::http/auth-with-shared-key request)
uuid/zero
nil))
ip-addr (inet/parse-request request) ip-addr (inet/parse-request request)
session-id (get-external-session-id request)
event-origin (get-external-event-origin request)
data (-> params data (-> params
(assoc ::handler-name handler-name) (assoc ::handler-name handler-name)
(assoc ::ip-addr ip-addr) (assoc ::ip-addr ip-addr)
(assoc ::request-at (dt/now)) (assoc ::request-at (ct/now))
(assoc ::external-session-id session-id) (assoc ::cond/key etag)
(assoc ::external-event-origin event-origin) (cond-> (uuid? profile-id)
(assoc ::session/id (::session/id request)) (assoc ::profile-id profile-id)))
(assoc ::cond/key etag)
(cond-> (uuid? profile-id)
(assoc ::profile-id profile-id)))
data (vary-meta data assoc ::http/request request) data (with-meta data
handler-fn (get methods (keyword handler-name) default-handler)] {::http/request request})
(when (and (or (= method :get) handler-fn (get methods (keyword handler-name) default-handler)]
(= method :head))
(not (str/starts-with? handler-name "get-")))
(ex/raise :type :restriction
:code :method-not-allowed
:hint "method not allowed for this request"))
(binding [cond/*enabled* true] (when (and (or (= method :get)
(let [response (handler-fn data)] (= method :head))
(handle-response request response))))) (not (str/starts-with? handler-name "get-")))
(ex/raise :type :restriction
:code :method-not-allowed
:hint "method not allowed for this request"))
;; FIXME: why we have this cond enabled here, we need to move it outside this handler
(binding [cond/*enabled* true]
(let [response (handler-fn data)]
(handle-response request response)))))))
(defn- wrap-metrics (defn- wrap-metrics
"Wrap service method with metrics measurement." "Wrap service method with metrics measurement."
[{:keys [::mtx/metrics ::metrics-id]} f mdata] [{:keys [::mtx/metrics ::metrics-id]} f mdata]
(let [labels (into-array String [(::sv/name mdata)])] (let [labels (into-array String [(::sv/name mdata)])]
(fn [cfg params] (fn [cfg params]
(let [tp (dt/tpoint)] (let [tp (ct/tpoint)]
(try (try
(f cfg params) (f cfg params)
(finally (finally
@@ -200,7 +200,7 @@
::sm/explain (explain params))))))) ::sm/explain (explain params)))))))
f)) f))
(defn- wrap-all (defn- wrap
[cfg f mdata] [cfg f mdata]
(as-> f $ (as-> f $
(wrap-db-transaction cfg $ mdata) (wrap-db-transaction cfg $ mdata)
@@ -214,17 +214,30 @@
(wrap-params-validation cfg $ mdata) (wrap-params-validation cfg $ mdata)
(wrap-authentication cfg $ mdata))) (wrap-authentication cfg $ mdata)))
(defn- wrap (defn- wrap-management
[cfg f mdata] [cfg f mdata]
(l/trc :hint "register method" :name (::sv/name mdata)) (as-> f $
(let [f (wrap-all cfg f mdata)] (wrap-db-transaction cfg $ mdata)
(partial f cfg))) (retry/wrap-retry cfg $ mdata)
(climit/wrap cfg $ mdata)
(wrap-metrics cfg $ mdata)
(wrap-audit cfg $ mdata)
(wrap-spec-conform cfg $ mdata)
(wrap-params-validation cfg $ mdata)
(wrap-authentication cfg $ mdata)))
(defn- process-method (defn- process-method
[cfg [vfn mdata]] [cfg module wrap-fn [f mdata]]
[(keyword (::sv/name mdata)) [mdata (wrap cfg vfn mdata)]]) (l/trc :hint "add method" :module module :name (::sv/name mdata))
(let [f (wrap-fn cfg f mdata)
k (keyword (::sv/name mdata))]
[k [mdata (partial f cfg)]]))
(defn- resolve-command-methods ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; API METHODS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- resolve-methods
[cfg] [cfg]
(let [cfg (assoc cfg ::type "command" ::metrics-id :rpc-command-timing)] (let [cfg (assoc cfg ::type "command" ::metrics-id :rpc-command-timing)]
(->> (sv/scan-ns (->> (sv/scan-ns
@@ -239,7 +252,6 @@
'app.rpc.commands.files 'app.rpc.commands.files
'app.rpc.commands.files-create 'app.rpc.commands.files-create
'app.rpc.commands.files-share 'app.rpc.commands.files-share
'app.rpc.commands.files-temp
'app.rpc.commands.files-update 'app.rpc.commands.files-update
'app.rpc.commands.files-snapshot 'app.rpc.commands.files-snapshot
'app.rpc.commands.files-thumbnails 'app.rpc.commands.files-thumbnails
@@ -254,7 +266,7 @@
'app.rpc.commands.verify-token 'app.rpc.commands.verify-token
'app.rpc.commands.viewer 'app.rpc.commands.viewer
'app.rpc.commands.webhooks) 'app.rpc.commands.webhooks)
(map (partial process-method cfg)) (map (partial process-method cfg "rpc" wrap))
(into {})))) (into {}))))
(def ^:private schema:methods-params (def ^:private schema:methods-params
@@ -262,6 +274,7 @@
::session/manager ::session/manager
::http.client/client ::http.client/client
::db/pool ::db/pool
::rds/pool
::mbus/msgbus ::mbus/msgbus
::sto/storage ::sto/storage
::mtx/metrics ::mtx/metrics
@@ -277,7 +290,50 @@
(defmethod ig/init-key ::methods (defmethod ig/init-key ::methods
[_ cfg] [_ cfg]
(let [cfg (d/without-nils cfg)] (let [cfg (d/without-nils cfg)]
(resolve-command-methods cfg))) (resolve-methods cfg)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MANAGEMENT METHODS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- resolve-management-methods
[cfg]
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
(->> (sv/scan-ns
'app.rpc.management.subscription
'app.rpc.management.exporter)
(map (partial process-method cfg "management" wrap-management))
(into {}))))
(def ^:private schema:management-methods-params
[:map {:title "management-methods-params"}
::session/manager
::http.client/client
::db/pool
::rds/pool
::mbus/msgbus
::sto/storage
::mtx/metrics
::setup/props])
(defmethod ig/assert-key ::management-methods
[_ params]
(assert (sm/check schema:management-methods-params params)))
(defmethod ig/init-key ::management-methods
[_ cfg]
(let [cfg (d/without-nils cfg)]
(resolve-management-methods cfg)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ROUTES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- redirect
[href]
(fn [_]
{::yres/status 308
::yres/headers {"location" (str href)}}))
(def ^:private schema:methods (def ^:private schema:methods
[:map-of :keyword [:tuple :map ::sm/fn]]) [:map-of :keyword [:tuple :map ::sm/fn]])
@@ -292,11 +348,50 @@
(assert (db/pool? (::db/pool params)) "expect valid database pool") (assert (db/pool? (::db/pool params)) "expect valid database pool")
(assert (some? (::setup/props params))) (assert (some? (::setup/props params)))
(assert (session/manager? (::session/manager params)) "expect valid session manager") (assert (session/manager? (::session/manager params)) "expect valid session manager")
(assert (valid-methods? (::methods params)) "expect valid methods map")) (assert (valid-methods? (::methods params)) "expect valid methods map")
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
(defmethod ig/init-key ::routes (defmethod ig/init-key ::routes
[_ {:keys [::methods] :as cfg}] [_ {:keys [::methods ::management-methods ::setup/props] :as cfg}]
(let [methods (update-vals methods peek)]
[["/rpc" {:middleware [[session/authz cfg] (let [public-uri (cf/get :public-uri)
[actoken/authz cfg]]} management-key (or (cf/get :management-api-key)
["/command/:type" {:handler (partial rpc-handler methods)}]]])) (get props :management-key))]
["/api"
["/management"
["/methods/:type"
{:middleware [[mw/shared-key-auth management-key]
[session/authz cfg]]
:handler (make-rpc-handler management-methods)}]
(doc/routes :methods management-methods
:label "management"
:base-uri (u/join public-uri "/api/management")
:description "MANAGEMENT API")]
["/main"
["/methods/:type"
{:middleware [[mw/cors]
[sec/client-header-check]
[session/authz cfg]
[actoken/authz cfg]]
:handler (make-rpc-handler methods)}]
(doc/routes :methods methods
:label "main"
:base-uri (u/join public-uri "/api/main")
:description "MAIN API")]
;; BACKWARD COMPATIBILITY
["/_doc" {:handler (redirect (u/join public-uri "/api/main/doc"))}]
["/doc" {:handler (redirect (u/join public-uri "/api/main/doc"))}]
["/openapi" {:handler (redirect (u/join public-uri "/api/main/doc/openapi"))}]
["/openapi.join" {:handler (redirect (u/join public-uri "/api/main/doc/openapi.json"))}]
["/rpc/command/:type"
{:middleware [[mw/cors]
[sec/client-header-check]
[session/authz cfg]
[actoken/authz cfg]]
:handler (make-rpc-handler methods)}]]))
+12 -17
View File
@@ -11,17 +11,16 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.util.cache :as cache] [app.util.cache :as cache]
[app.util.services :as-alias sv] [app.util.services :as-alias sv]
[app.util.time :as dt]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[clojure.edn :as edn] [clojure.edn :as edn]
[clojure.set :as set] [clojure.set :as set]
[datoteka.fs :as fs] [datoteka.fs :as fs]
[integrant.core :as ig] [integrant.core :as ig]
[promesa.exec :as px]
[promesa.exec.bulkhead :as pbh]) [promesa.exec.bulkhead :as pbh])
(:import (:import
clojure.lang.ExceptionInfo clojure.lang.ExceptionInfo
@@ -154,7 +153,7 @@
:id limit-id :id limit-id
:label limit-label :label limit-label
:queue queue :queue queue
:elapsed (some-> elapsed dt/format-duration) :elapsed (some-> elapsed ct/format-duration)
:params @limit-params))) :params @limit-params)))
(def ^:private idseq (AtomicLong. 0)) (def ^:private idseq (AtomicLong. 0))
@@ -171,19 +170,19 @@
mlabels (into-array String [(id->str limit-id)]) mlabels (into-array String [(id->str limit-id)])
limit-id (id->str limit-id limit-key) limit-id (id->str limit-id limit-key)
limiter (cache/get cache limit-id (partial create-limiter config)) limiter (cache/get cache limit-id (partial create-limiter config))
tpoint (dt/tpoint) tpoint (ct/tpoint)
req-id (.incrementAndGet ^AtomicLong idseq)] req-id (.incrementAndGet ^AtomicLong idseq)]
(try (try
(let [stats (pbh/get-stats limiter)] (let [stats (pbh/get-stats limiter)]
(measure metrics mlabels stats nil) (measure metrics mlabels stats nil)
(log "enqueued" req-id stats limit-id limit-label limit-params nil)) (log "enqueued" req-id stats limit-id limit-label limit-params nil))
(px/invoke! limiter (fn [] (pbh/invoke! limiter (fn []
(let [elapsed (tpoint) (let [elapsed (tpoint)
stats (pbh/get-stats limiter)] stats (pbh/get-stats limiter)]
(measure metrics mlabels stats elapsed) (measure metrics mlabels stats elapsed)
(log "acquired" req-id stats limit-id limit-label limit-params elapsed) (log "acquired" req-id stats limit-id limit-label limit-params elapsed)
(handler)))) (handler))))
(catch ExceptionInfo cause (catch ExceptionInfo cause
(let [{:keys [type code]} (ex-data cause)] (let [{:keys [type code]} (ex-data cause)]
@@ -289,13 +288,9 @@
(get-limits cfg))) (get-limits cfg)))
(defn invoke! (defn invoke!
"Run a function in context of climit. "Run a function in context of climit."
Intended to be used in virtual threads." [{:keys [::rpc/climit] :as cfg} f params]
[{:keys [::executor ::rpc/climit] :as cfg} f params]
(let [f (if climit (let [f (if climit
(let [f (if (some? executor) (build-exec-chain cfg f)
(fn [cfg params] (px/await! (px/submit! executor (fn [] (f cfg params)))))
f)]
(build-exec-chain cfg f))
f)] f)]
(f cfg params))) (f cfg params)))
+11 -10
View File
@@ -7,6 +7,7 @@
(ns app.rpc.commands.access-token (ns app.rpc.commands.access-token
(:require (:require
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.main :as-alias main] [app.main :as-alias main]
@@ -15,22 +16,22 @@
[app.rpc.quotes :as quotes] [app.rpc.quotes :as quotes]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.services :as sv] [app.util.services :as sv]))
[app.util.time :as dt]))
(defn- decode-row (defn- decode-row
[row] [row]
(dissoc row :perms)) (dissoc row :perms))
(defn create-access-token (defn create-access-token
[{:keys [::db/conn ::setup/props]} profile-id name expiration] [{:keys [::db/conn] :as cfg} profile-id name expiration]
(let [created-at (dt/now) (let [token-id (uuid/next)
token-id (uuid/next) expires-at (some-> expiration (ct/in-future))
token (tokens/generate props {:iss "access-token" created-at (ct/now)
:tid token-id token (tokens/generate cfg {:iss "access-token"
:iat created-at}) :uid profile-id
:iat created-at
:tid token-id})
expires-at (some-> expiration dt/in-future)
token (db/insert! conn :access-token token (db/insert! conn :access-token
{:id token-id {:id token-id
:name name :name name
@@ -49,7 +50,7 @@
(def ^:private schema:create-access-token (def ^:private schema:create-access-token
[:map {:title "create-access-token"} [:map {:title "create-access-token"}
[:name [:string {:max 250 :min 1}]] [:name [:string {:max 250 :min 1}]]
[:expiration {:optional true} ::dt/duration]]) [:expiration {:optional true} ::ct/duration]])
(sv/defmethod ::create-access-token (sv/defmethod ::create-access-token
{::doc/added "1.18" {::doc/added "1.18"
+4 -4
View File
@@ -10,6 +10,7 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
@@ -20,8 +21,7 @@
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
[app.util.inet :as inet] [app.util.inet :as inet]
[app.util.services :as sv] [app.util.services :as sv]))
[app.util.time :as dt]))
(def ^:private event-columns (def ^:private event-columns
[:id [:id
@@ -49,7 +49,7 @@
(defn- adjust-timestamp (defn- adjust-timestamp
[{:keys [timestamp created-at] :as event}] [{:keys [timestamp created-at] :as event}]
(let [margin (inst-ms (dt/diff timestamp created-at))] (let [margin (inst-ms (ct/diff timestamp created-at))]
(if (or (neg? margin) (if (or (neg? margin)
(> margin 3600000)) (> margin 3600000))
;; If event is in future or lags more than 1 hour, we reasign ;; If event is in future or lags more than 1 hour, we reasign
@@ -63,7 +63,7 @@
[{:keys [::db/pool]} {:keys [::rpc/profile-id events] :as params}] [{:keys [::db/pool]} {:keys [::rpc/profile-id events] :as params}]
(let [request (-> params meta ::http/request) (let [request (-> params meta ::http/request)
ip-addr (inet/parse-request request) ip-addr (inet/parse-request request)
tnow (dt/now) tnow (ct/now)
xform (comp xform (comp
(map (fn [event] (map (fn [event]
(-> event (-> event
+122 -48
View File
@@ -6,20 +6,25 @@
(ns app.rpc.commands.auth (ns app.rpc.commands.auth
(:require (:require
[app.auth :as auth]
[app.auth.oidc :as oidc]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.email :as eml] [app.email :as eml]
[app.email.blacklist :as email.blacklist] [app.email.blacklist :as email.blacklist]
[app.email.whitelist :as email.whitelist] [app.email.whitelist :as email.whitelist]
[app.http :as-alias http]
[app.http.session :as session] [app.http.session :as session]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.media :as media]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit] [app.rpc.climit :as-alias climit]
[app.rpc.commands.profile :as profile] [app.rpc.commands.profile :as profile]
@@ -28,9 +33,9 @@
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
[app.setup :as-alias setup] [app.setup :as-alias setup]
[app.setup.welcome-file :refer [create-welcome-file]] [app.setup.welcome-file :refer [create-welcome-file]]
[app.storage :as sto]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk] [app.worker :as wrk]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@@ -42,7 +47,7 @@
(defn- elapsed-verify-threshold? (defn- elapsed-verify-threshold?
[profile] [profile]
(let [elapsed (dt/diff (:modified-at profile) (dt/now)) (let [elapsed (ct/diff (:modified-at profile) (ct/now))
verify-threshold (cf/get :email-verify-threshold)] verify-threshold (cf/get :email-verify-threshold)]
(pos? (compare elapsed verify-threshold)))) (pos? (compare elapsed verify-threshold))))
@@ -62,7 +67,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :account-without-password :code :account-without-password
:hint "the current account does not have password") :hint "the current account does not have password")
(let [result (profile/verify-password cfg password (:password profile))] (let [result (auth/verify-password password (:password profile))]
(when (:update result) (when (:update result)
(l/trc :hint "updating profile password" (l/trc :hint "updating profile password"
:id (str (:id profile)) :id (str (:id profile))
@@ -85,7 +90,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :wrong-credentials)) :code :wrong-credentials))
(when-let [deleted-at (:deleted-at profile)] (when-let [deleted-at (:deleted-at profile)]
(when (dt/is-after? (dt/now) deleted-at) (when (ct/is-after? (ct/now) deleted-at)
(ex/raise :type :validation (ex/raise :type :validation
:code :wrong-credentials))) :code :wrong-credentials)))
@@ -98,7 +103,7 @@
(profile/strip-private-attrs)) (profile/strip-private-attrs))
invitation (when-let [token (:invitation-token params)] invitation (when-let [token (:invitation-token params)]
(tokens/verify (::setup/props cfg) {:token token :iss :team-invitation})) (tokens/verify cfg {:token token :iss :team-invitation}))
;; If invitation member-id does not matches the profile-id, we just proceed to ignore the ;; If invitation member-id does not matches the profile-id, we just proceed to ignore the
;; invitation because invitations matches exactly; and user can't login with other email and ;; invitation because invitations matches exactly; and user can't login with other email and
@@ -108,7 +113,7 @@
(assoc profile :is-admin (let [admins (cf/get :admins)] (assoc profile :is-admin (let [admins (cf/get :admins)]
(contains? admins (:email profile)))))] (contains? admins (:email profile)))))]
(-> response (-> response
(rph/with-transform (session/create-fn cfg (:id profile))) (rph/with-transform (session/create-fn cfg profile))
(rph/with-meta {::audit/props (audit/profile->props profile) (rph/with-meta {::audit/props (audit/profile->props profile)
::audit/profile-id (:id profile)}))))] ::audit/profile-id (:id profile)}))))]
@@ -144,7 +149,24 @@
[cfg params] [cfg params]
(if (= (:profile-id params) (if (= (:profile-id params)
(::rpc/profile-id params)) (::rpc/profile-id params))
(rph/with-transform {} (session/delete-fn cfg)) (let [{:keys [claims]}
(rph/get-auth-data params)
provider
(some->> (get claims :sso-provider-id)
(oidc/get-provider cfg))
response
(if (and provider (:logout-uri provider))
(let [params {"logout_hint" (get claims :sso-session-id)
"client_id" (get provider :client-id)
"post_logout_redirect_uri" (str (cf/get :public-uri))}
uri (-> (u/uri (:logout-uri provider))
(assoc :query (u/map->query-string params)))]
{:redirect-uri uri})
{})]
(rph/with-transform response (session/delete-fn cfg)))
{})) {}))
;; ---- COMMAND: Recover Profile ;; ---- COMMAND: Recover Profile
@@ -152,11 +174,11 @@
(defn recover-profile (defn recover-profile
[{:keys [::db/conn] :as cfg} {:keys [token password]}] [{:keys [::db/conn] :as cfg} {:keys [token password]}]
(letfn [(validate-token [token] (letfn [(validate-token [token]
(let [tdata (tokens/verify (::setup/props cfg) {:token token :iss :password-recovery})] (let [tdata (tokens/verify cfg {:token token :iss :password-recovery})]
(:profile-id tdata))) (:profile-id tdata)))
(update-password [conn profile-id] (update-password [conn profile-id]
(let [pwd (profile/derive-password cfg password)] (let [pwd (auth/derive-password password)]
(db/update! conn :profile {:password pwd :is-active true} {:id profile-id}) (db/update! conn :profile {:password pwd :is-active true} {:id profile-id})
nil))] nil))]
@@ -191,7 +213,7 @@
:hint "registration disabled")) :hint "registration disabled"))
(when (contains? params :invitation-token) (when (contains? params :invitation-token)
(let [invitation (tokens/verify (::setup/props cfg) (let [invitation (tokens/verify cfg
{:token (:invitation-token params) {:token (:invitation-token params)
:iss :team-invitation})] :iss :team-invitation})]
(when-not (= (:email params) (:member-email invitation)) (when-not (= (:email params) (:member-email invitation))
@@ -244,11 +266,11 @@
:backend "penpot" :backend "penpot"
:iss :prepared-register :iss :prepared-register
:profile-id (:id profile) :profile-id (:id profile)
:exp (dt/in-future {:days 7}) :exp (ct/in-future {:days 7})
:props {:newsletter-updates (or accept-newsletter-updates false)}} :props {:newsletter-updates (or accept-newsletter-updates false)}}
params (d/without-nils params) params (d/without-nils params)
token (tokens/generate (::setup/props cfg) params)] token (tokens/generate cfg params)]
(with-meta {:token token} (with-meta {:token token}
{::audit/profile-id uuid/zero}))) {::audit/profile-id uuid/zero})))
@@ -270,11 +292,30 @@
;; ---- COMMAND: Register Profile ;; ---- COMMAND: Register Profile
(defn create-profile! (defn import-profile-picture
[cfg uri]
(try
(let [storage (sto/resolve cfg)
input (media/download-image cfg uri)
input (media/run {:cmd :info :input input})
hash (sto/calculate-hash (:path input))
content (-> (sto/content (:path input) (:size input))
(sto/wrap-with-hash hash))
sobject (sto/put-object! storage {::sto/content content
::sto/deduplicate? true
:bucket "profile"
:content-type (:mtype input)})]
(:id sobject))
(catch Throwable cause
(l/wrn :hint "unable to import profile picture"
:uri uri
:cause cause)
nil)))
(defn create-profile
"Create the profile entry on the database with limited set of input "Create the profile entry on the database with limited set of input
attrs (all the other attrs are filled with default values)." attrs (all the other attrs are filled with default values)."
[conn {:keys [email] :as params}] [{:keys [::db/conn] :as cfg} {:keys [email] :as params}]
(dm/assert! ::sm/email email)
(let [id (or (:id params) (uuid/next)) (let [id (or (:id params) (uuid/next))
props (-> (audit/extract-utm-params params) props (-> (audit/extract-utm-params params)
(merge (:props params)) (merge (:props params))
@@ -282,8 +323,7 @@
:viewed-walkthrough? false :viewed-walkthrough? false
:nudge {:big 10 :small 1} :nudge {:big 10 :small 1}
:v2-info-shown true :v2-info-shown true
:release-notes-viewed (:main cf/version)}) :release-notes-viewed (:main cf/version)}))
(db/tjson))
password (or (:password params) "!") password (or (:password params) "!")
@@ -298,6 +338,12 @@
theme (:theme params nil) theme (:theme params nil)
email (str/lower email) email (str/lower email)
photo-id (some->> (or (:oidc/picture props)
(:google/picture props)
(:github/picture props)
(:gitlab/picture props))
(import-profile-picture cfg))
params {:id id params {:id id
:fullname (:fullname params) :fullname (:fullname params)
:email email :email email
@@ -305,27 +351,26 @@
:lang locale :lang locale
:password password :password password
:deleted-at (:deleted-at params) :deleted-at (:deleted-at params)
:props props :props (db/tjson props)
:theme theme :theme theme
:photo-id photo-id
:is-active is-active :is-active is-active
:is-muted is-muted :is-muted is-muted
:is-demo is-demo}] :is-demo is-demo}]
(try (try
(-> (db/insert! conn :profile params) (-> (db/insert! conn :profile params)
(profile/decode-row)) (profile/decode-row))
(catch org.postgresql.util.PSQLException cause (catch org.postgresql.util.PSQLException cause
(let [state (.getSQLState cause)] (if (db/duplicate-key-error? cause)
(if (not= state "23505") (ex/raise :type :validation
(throw cause) :code :email-already-exists
:hint "email already exists"
:cause cause)
(throw cause))))))
(do
(l/error :hint "not an error" :cause cause)
(ex/raise :type :validation
:code :email-already-exists
:hint "email already exists"
:cause cause))))))))
(defn create-profile-rels! (defn create-profile-rels
[conn {:keys [id] :as profile}] [conn {:keys [id] :as profile}]
(let [features (cfeat/get-enabled-features cf/flags) (let [features (cfeat/get-enabled-features cf/flags)
team (teams/create-team conn team (teams/create-team conn
@@ -342,17 +387,17 @@
(defn send-email-verification! (defn send-email-verification!
[{:keys [::db/conn] :as cfg} profile] [{:keys [::db/conn] :as cfg} profile]
(let [vtoken (tokens/generate (::setup/props cfg) (let [vtoken (tokens/generate cfg
{:iss :verify-email {:iss :verify-email
:exp (dt/in-future "72h") :exp (ct/in-future "72h")
:profile-id (:id profile) :profile-id (:id profile)
:email (:email profile)}) :email (:email profile)})
;; NOTE: this token is mainly used for possible complains ;; NOTE: this token is mainly used for possible complains
;; identification on the sns webhook ;; identification on the sns webhook
ptoken (tokens/generate (::setup/props cfg) ptoken (tokens/generate cfg
{:iss :profile-identity {:iss :profile-identity
:profile-id (:id profile) :profile-id (:id profile)
:exp (dt/in-future {:days 30})})] :exp (ct/in-future {:days 30})})]
(eml/send! {::eml/conn conn (eml/send! {::eml/conn conn
::eml/factory eml/register ::eml/factory eml/register
:public-uri (cf/get :public-uri) :public-uri (cf/get :public-uri)
@@ -363,7 +408,7 @@
(defn register-profile (defn register-profile
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}] [{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}]
(let [claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register}) (let [claims (tokens/verify cfg {:token token :iss :prepared-register})
params (into claims params) params (into claims params)
profile (if-let [profile-id (:profile-id claims)] profile (if-let [profile-id (:profile-id claims)]
@@ -375,18 +420,19 @@
;; to detect if the profile is already registered ;; to detect if the profile is already registered
(or (profile/get-profile-by-email conn (:email claims)) (or (profile/get-profile-by-email conn (:email claims))
(let [is-active (or (boolean (:is-active claims)) (let [is-active (or (boolean (:is-active claims))
(boolean (:email-verified claims))
(not (contains? cf/flags :email-verification))) (not (contains? cf/flags :email-verification)))
params (-> params params (-> params
(assoc :is-active is-active) (assoc :is-active is-active)
(update :password #(profile/derive-password cfg %))) (update :password auth/derive-password))
profile (->> (create-profile! conn params) profile (->> (create-profile cfg params)
(create-profile-rels! conn))] (create-profile-rels conn))]
(vary-meta profile assoc :created true)))) (vary-meta profile assoc :created true))))
created? (-> profile meta :created true?) created? (-> profile meta :created true?)
invitation (when-let [token (:invitation-token params)] invitation (when-let [token (:invitation-token params)]
(tokens/verify (::setup/props cfg) {:token token :iss :team-invitation})) (tokens/verify cfg {:token token :iss :team-invitation}))
props (-> (audit/profile->props profile) props (-> (audit/profile->props profile)
(assoc :from-invitation (some? invitation))) (assoc :from-invitation (some? invitation)))
@@ -418,10 +464,10 @@
(and (some? invitation) (and (some? invitation)
(= (:email profile) (= (:email profile)
(:member-email invitation))) (:member-email invitation)))
(let [claims (assoc invitation :member-id (:id profile)) (let [invitation (assoc invitation :member-id (:id profile))
token (tokens/generate (::setup/props cfg) claims)] token (tokens/generate cfg invitation)]
(-> {:invitation-token token} (-> {:invitation-token token}
(rph/with-transform (session/create-fn cfg (:id profile))) (rph/with-transform (session/create-fn cfg profile claims))
(rph/with-meta {::audit/replace-props props (rph/with-meta {::audit/replace-props props
::audit/context {:action "accept-invitation"} ::audit/context {:action "accept-invitation"}
::audit/profile-id (:id profile)}))) ::audit/profile-id (:id profile)})))
@@ -432,7 +478,7 @@
created? created?
(if (:is-active profile) (if (:is-active profile)
(-> (profile/strip-private-attrs profile) (-> (profile/strip-private-attrs profile)
(rph/with-transform (session/create-fn cfg (:id profile))) (rph/with-transform (session/create-fn cfg profile claims))
(rph/with-defer create-welcome-file-when-needed) (rph/with-defer create-welcome-file-when-needed)
(rph/with-meta (rph/with-meta
{::audit/replace-props props {::audit/replace-props props
@@ -466,7 +512,7 @@
(when (= action "resend-email-verification") (when (= action "resend-email-verification")
(db/update! conn :profile (db/update! conn :profile
{:modified-at (dt/now)} {:modified-at (ct/now)}
{:id (:id profile)}) {:id (:id profile)})
(send-email-verification! cfg profile)) (send-email-verification! cfg profile))
@@ -493,17 +539,17 @@
(defn- request-profile-recovery (defn- request-profile-recovery
[{:keys [::db/conn] :as cfg} {:keys [email] :as params}] [{:keys [::db/conn] :as cfg} {:keys [email] :as params}]
(letfn [(create-recovery-token [{:keys [id] :as profile}] (letfn [(create-recovery-token [{:keys [id] :as profile}]
(let [token (tokens/generate (::setup/props cfg) (let [token (tokens/generate cfg
{:iss :password-recovery {:iss :password-recovery
:exp (dt/in-future "15m") :exp (ct/in-future "15m")
:profile-id id})] :profile-id id})]
(assoc profile :token token))) (assoc profile :token token)))
(send-email-notification [conn profile] (send-email-notification [conn profile]
(let [ptoken (tokens/generate (::setup/props cfg) (let [ptoken (tokens/generate cfg
{:iss :profile-identity {:iss :profile-identity
:profile-id (:id profile) :profile-id (:id profile)
:exp (dt/in-future {:days 30})})] :exp (ct/in-future {:days 30})})]
(eml/send! {::eml/conn conn (eml/send! {::eml/conn conn
::eml/factory eml/password-recovery ::eml/factory eml/password-recovery
:public-uri (cf/get :public-uri) :public-uri (cf/get :public-uri)
@@ -544,7 +590,7 @@
:else :else
(do (do
(db/update! conn :profile (db/update! conn :profile
{:modified-at (dt/now)} {:modified-at (ct/now)}
{:id (:id profile)}) {:id (:id profile)})
(->> profile (->> profile
(create-recovery-token) (create-recovery-token)
@@ -561,4 +607,32 @@
[cfg params] [cfg params]
(db/tx-run! cfg request-profile-recovery params)) (db/tx-run! cfg request-profile-recovery params))
;; --- COMMAND: get-sso-config
(defn- extract-domain
"Extract the domain part from email"
[email]
(let [at (str/last-index-of email "@")]
(when (and (>= at 0)
(< at (dec (count email))))
(-> (subs email (inc at))
(str/trim)
(str/lower)))))
(def ^:private schema:get-sso-provider
[:map {:title "get-sso-config"}
[:email ::sm/email]])
(def ^:private schema:get-sso-provider-result
[:map {:title "SSOProvider"}
[:id ::sm/uuid]])
(sv/defmethod ::get-sso-provider
{::rpc/auth false
::doc/added "2.12"
::sm/params schema:get-sso-provider
::sm/result schema:get-sso-provider-result}
[cfg {:keys [email]}]
(when-let [domain (extract-domain email)]
(when-let [config (db/get* cfg :sso-provider {:domain domain})]
(select-keys config [:id]))))
+63 -59
View File
@@ -11,8 +11,9 @@
[app.binfile.v1 :as bf.v1] [app.binfile.v1 :as bf.v1]
[app.binfile.v3 :as bf.v3] [app.binfile.v3 :as bf.v3]
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as u]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.http.sse :as sse] [app.http.sse :as sse]
@@ -24,12 +25,12 @@
[app.rpc.commands.projects :as projects] [app.rpc.commands.projects :as projects]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.storage :as sto]
[app.storage.tmp :as tmp]
[app.tasks.file-gc] [app.tasks.file-gc]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as-alias wrk] [app.worker :as-alias wrk]
[promesa.exec :as px] [datoteka.fs :as fs]))
[yetti.response :as yres]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -39,62 +40,47 @@
schema:export-binfile schema:export-binfile
[:map {:title "export-binfile"} [:map {:title "export-binfile"}
[:file-id ::sm/uuid] [:file-id ::sm/uuid]
[:version {:optional true} ::sm/int]
[:include-libraries ::sm/boolean] [:include-libraries ::sm/boolean]
[:embed-assets ::sm/boolean]]) [:embed-assets ::sm/boolean]])
(defn stream-export-v1 (defn- export-binfile
[cfg {:keys [file-id include-libraries embed-assets] :as params}] [{:keys [::sto/storage] :as cfg} {:keys [file-id include-libraries embed-assets]}]
(yres/stream-body (let [output (tmp/tempfile*)]
(fn [_ output-stream] (try
(try (-> cfg
(-> cfg (assoc ::bfc/ids #{file-id})
(assoc ::bfc/ids #{file-id}) (assoc ::bfc/embed-assets embed-assets)
(assoc ::bfc/embed-assets embed-assets) (assoc ::bfc/include-libraries include-libraries)
(assoc ::bfc/include-libraries include-libraries) (bf.v3/export-files! output))
(bf.v1/export-files! output-stream))
(catch Throwable cause
(l/err :hint "exception on exporting file"
:file-id (str file-id)
:cause cause))))))
(defn stream-export-v3 (let [data (sto/content output)
[cfg {:keys [file-id include-libraries embed-assets] :as params}] object (sto/put-object! storage
(yres/stream-body {::sto/content data
(fn [_ output-stream] ::sto/touched-at (ct/in-future {:minutes 60})
(try :content-type "application/zip"
(-> cfg :bucket "tempfile"})]
(assoc ::bfc/ids #{file-id})
(assoc ::bfc/embed-assets embed-assets) (-> (cf/get :public-uri)
(assoc ::bfc/include-libraries include-libraries) (u/join "/assets/by-id/")
(bf.v3/export-files! output-stream)) (u/join (str (:id object)))))
(catch Throwable cause
(l/err :hint "exception on exporting file" (finally
:file-id (str file-id) (fs/delete output)))))
:cause cause))))))
(sv/defmethod ::export-binfile (sv/defmethod ::export-binfile
"Export a penpot file in a binary format." "Export a penpot file in a binary format."
{::doc/added "1.15" {::doc/added "1.15"
::doc/changes [["2.12" "Remove version parameter, only one version is supported"]]
::webhooks/event? true ::webhooks/event? true
::sm/params schema:export-binfile} ::sm/params schema:export-binfile}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id version file-id] :as params}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(files/check-read-permissions! pool profile-id file-id) (files/check-read-permissions! pool profile-id file-id)
(fn [_] (sse/response (partial export-binfile cfg params)))
(let [version (or version 1)
body (case (int version)
1 (stream-export-v1 cfg params)
2 (throw (ex-info "not-implemented" {}))
3 (stream-export-v3 cfg params))]
{::yres/status 200
::yres/headers {"content-type" "application/octet-stream"}
::yres/body body})))
;; --- Command: import-binfile ;; --- Command: import-binfile
(defn- import-binfile (defn- import-binfile
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [profile-id project-id version name file]}] [{:keys [::db/pool] :as cfg} {:keys [profile-id project-id version name file]}]
(let [team (teams/get-team pool (let [team (teams/get-team pool
:profile-id profile-id :profile-id profile-id
:project-id project-id) :project-id project-id)
@@ -105,16 +91,12 @@
(assoc ::bfc/name name) (assoc ::bfc/name name)
(assoc ::bfc/input (:path file))) (assoc ::bfc/input (:path file)))
;; NOTE: the importation process performs some operations that are
;; not very friendly with virtual threads, and for avoid
;; unexpected blocking of other concurrent operations we dispatch
;; that operation to a dedicated executor.
result (case (int version) result (case (int version)
1 (px/invoke! executor (partial bf.v1/import-files! cfg)) 1 (bf.v1/import-files! cfg)
3 (px/invoke! executor (partial bf.v3/import-files! cfg)))] 3 (bf.v3/import-files! cfg))]
(db/update! pool :project (db/update! pool :project
{:modified-at (dt/now)} {:modified-at (ct/now)}
{:id project-id} {:id project-id}
{::db/return-keys false}) {::db/return-keys false})
@@ -125,20 +107,42 @@
[:name [:or [:string {:max 250}] [:name [:or [:string {:max 250}]
[:map-of ::sm/uuid [:string {:max 250}]]]] [:map-of ::sm/uuid [:string {:max 250}]]]]
[:project-id ::sm/uuid] [:project-id ::sm/uuid]
[:file-id {:optional true} ::sm/uuid]
[:version {:optional true} ::sm/int] [:version {:optional true} ::sm/int]
[:file ::media/upload]]) [:file media/schema:upload]])
(sv/defmethod ::import-binfile (sv/defmethod ::import-binfile
"Import a penpot file in a binary format." "Import a penpot file in a binary format. If `file-id` is provided,
an in-place import will be performed instead of creating a new file.
The in-place imports are only supported for binfile-v3 and when a
.penpot file only contains one penpot file.
"
{::doc/added "1.15" {::doc/added "1.15"
::doc/changes ["1.20" "Add file-id param for in-place import"
"1.20" "Set default version to 3"]
::webhooks/event? true ::webhooks/event? true
::sse/stream? true ::sse/stream? true
::sm/params schema:import-binfile} ::sm/params schema:import-binfile}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version] :as params}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version file-id file] :as params}]
(projects/check-edition-permissions! pool profile-id project-id) (projects/check-edition-permissions! pool profile-id project-id)
(let [params (-> params (let [version (or version 3)
(assoc :profile-id profile-id) params (-> params
(assoc :version (or version 1)))] (assoc :profile-id profile-id)
(assoc :version version))
cfg (cond-> cfg
(uuid? file-id)
(assoc ::bfc/file-id file-id))
manifest (case (int version)
1 nil
3 (bf.v3/get-manifest (:path file)))]
(with-meta (with-meta
(sse/response (partial import-binfile cfg params)) (sse/response (partial import-binfile cfg params))
{::audit/props {:file nil}}))) {::audit/props {:file nil
:file-id file-id
:generated-by (:generated-by manifest)
:referer (:referer manifest)}})))
+72 -93
View File
@@ -6,11 +6,13 @@
(ns app.rpc.commands.comments (ns app.rpc.commands.comments
(:require (:require
[app.binfile.common :as bfc]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct]
[app.common.uri :as uri] [app.common.uri :as uri]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@@ -29,7 +31,6 @@
[app.rpc.retry :as rtry] [app.rpc.retry :as rtry]
[app.util.pointer-map :as pmap] [app.util.pointer-map :as pmap]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@@ -163,34 +164,16 @@
(def xf-decode-row (def xf-decode-row
(map decode-row)) (map decode-row))
(def ^:private
sql:get-file
"SELECT f.id, f.modified_at, f.revn, f.features, f.name,
f.project_id, p.team_id, f.data,
f.data_ref_id, f.data_backend
FROM file as f
INNER JOIN project as p on (p.id = f.project_id)
WHERE f.id = ?
AND (f.deleted_at IS NULL OR f.deleted_at > now())")
(defn- get-file (defn- get-file
"A specialized version of get-file for comments module." "A specialized version of get-file for comments module."
[cfg file-id page-id] [cfg file-id page-id]
(let [file (db/exec-one! cfg [sql:get-file file-id])] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(when-not file (let [file (bfc/get-file cfg file-id)
(ex/raise :type :not-found data (get file :data)]
:code :object-not-found (-> file
:hint "file not found")) (assoc :page-name (dm/get-in data [:pages-index page-id :name]))
(assoc :page-id page-id)
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] (dissoc :data)))))
(let [file (->> file
(files/decode-row)
(feat.fdata/resolve-file-data cfg))
data (get file :data)]
(-> file
(assoc :page-name (dm/get-in data [:pages-index page-id :name]))
(assoc :page-id page-id)
(dissoc :data))))))
;; FIXME: rename ;; FIXME: rename
(defn- get-comment-thread (defn- get-comment-thread
@@ -222,7 +205,7 @@
(defn upsert-comment-thread-status! (defn upsert-comment-thread-status!
([conn profile-id thread-id] ([conn profile-id thread-id]
(upsert-comment-thread-status! conn profile-id thread-id (dt/in-future "1s"))) (upsert-comment-thread-status! conn profile-id thread-id (ct/in-future "1s")))
([conn profile-id thread-id mod-at] ([conn profile-id thread-id mod-at]
(db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id mod-at mod-at]))) (db/exec-one! conn [sql:upsert-comment-thread-status thread-id profile-id mod-at mod-at])))
@@ -251,34 +234,39 @@
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
(get-comment-threads conn profile-id file-id)))) (get-comment-threads conn profile-id file-id))))
(def ^:private sql:comment-threads (defn- get-comment-threads-sql
"SELECT DISTINCT ON (ct.id) [where]
ct.*, (str/ffmt
pf.fullname AS owner_fullname, "SELECT DISTINCT ON (ct.id)
pf.email AS owner_email, ct.*,
pf.photo_id AS owner_photo_id, pf.fullname AS owner_fullname,
p.team_id AS team_id, pf.email AS owner_email,
f.name AS file_name, pf.photo_id AS owner_photo_id,
f.project_id AS project_id, p.team_id AS team_id,
first_value(c.content) OVER w AS content, f.name AS file_name,
(SELECT count(1) f.project_id AS project_id,
FROM comment AS c first_value(c.content) OVER w AS content,
WHERE c.thread_id = ct.id) AS count_comments, (SELECT count(1)
(SELECT count(1) FROM comment AS c
FROM comment AS c WHERE c.thread_id = ct.id) AS count_comments,
WHERE c.thread_id = ct.id (SELECT count(1)
AND c.created_at >= coalesce(cts.modified_at, ct.created_at)) AS count_unread_comments FROM comment AS c
FROM comment_thread AS ct WHERE c.thread_id = ct.id
INNER JOIN comment AS c ON (c.thread_id = ct.id) AND c.created_at >= coalesce(cts.modified_at, ct.created_at)) AS count_unread_comments
INNER JOIN file AS f ON (f.id = ct.file_id) FROM comment_thread AS ct
INNER JOIN project AS p ON (p.id = f.project_id) INNER JOIN comment AS c ON (c.thread_id = ct.id)
LEFT JOIN comment_thread_status AS cts ON (cts.thread_id = ct.id AND cts.profile_id = ?) INNER JOIN file AS f ON (f.id = ct.file_id)
LEFT JOIN profile AS pf ON (ct.owner_id = pf.id) INNER JOIN project AS p ON (p.id = f.project_id)
WINDOW w AS (PARTITION BY c.thread_id ORDER BY c.created_at ASC)") LEFT JOIN comment_thread_status AS cts ON (cts.thread_id = ct.id AND cts.profile_id = ?)
LEFT JOIN profile AS pf ON (ct.owner_id = pf.id)
WHERE f.deleted_at IS NULL
AND p.deleted_at IS NULL
%1
WINDOW w AS (PARTITION BY c.thread_id ORDER BY c.created_at ASC)"
where))
(def ^:private sql:comment-threads-by-file-id (def ^:private sql:comment-threads-by-file-id
(str "WITH threads AS (" sql:comment-threads ")" (get-comment-threads-sql "AND ct.file_id = ?"))
"SELECT * FROM threads WHERE file_id = ?"))
(defn- get-comment-threads (defn- get-comment-threads
[conn profile-id file-id] [conn profile-id file-id]
@@ -287,7 +275,30 @@
;; --- COMMAND: Get Unread Comment Threads ;; --- COMMAND: Get Unread Comment Threads
(declare ^:private get-unread-comment-threads) (def ^:private sql:unread-all-comment-threads-by-team
(str "WITH threads AS ("
(get-comment-threads-sql "AND p.team_id = ?")
")"
"SELECT t.* FROM threads AS t
WHERE t.count_unread_comments > 0"))
(def ^:private sql:unread-partial-comment-threads-by-team
(str "WITH threads AS ("
(get-comment-threads-sql "AND p.team_id = ? AND (ct.owner_id = ? OR ? = ANY(ct.mentions))")
")"
"SELECT t.* FROM threads AS t
WHERE t.count_unread_comments > 0"))
(defn- get-unread-comment-threads
[cfg profile-id team-id]
(let [profile (-> (db/get cfg :profile {:id profile-id} ::db/remove-deleted false)
(profile/decode-row))
notify (or (-> profile :props :notifications :dashboard-comments) :all)
result (case notify
:all (db/exec! cfg [sql:unread-all-comment-threads-by-team profile-id team-id])
:partial (db/exec! cfg [sql:unread-partial-comment-threads-by-team profile-id team-id profile-id profile-id])
[])]
(into [] xf-decode-row result)))
(def ^:private (def ^:private
schema:get-unread-comment-threads schema:get-unread-comment-threads
@@ -298,41 +309,8 @@
{::doc/added "1.15" {::doc/added "1.15"
::sm/params schema:get-unread-comment-threads} ::sm/params schema:get-unread-comment-threads}
[cfg {:keys [::rpc/profile-id team-id] :as params}] [cfg {:keys [::rpc/profile-id team-id] :as params}]
(db/run! (teams/check-read-permissions! cfg profile-id team-id)
cfg (get-unread-comment-threads cfg profile-id team-id))
(fn [{:keys [::db/conn]}]
(teams/check-read-permissions! conn profile-id team-id)
(get-unread-comment-threads conn profile-id team-id))))
(def sql:unread-all-comment-threads-by-team
(str "WITH threads AS (" sql:comment-threads ")"
"SELECT * FROM threads WHERE count_unread_comments > 0 AND team_id = ?"))
;; The partial configuration will retrieve only comments created by the user and
;; threads that have a mention to the user.
(def sql:unread-partial-comment-threads-by-team
(str "WITH threads AS (" sql:comment-threads ")"
"SELECT * FROM threads
WHERE count_unread_comments > 0
AND team_id = ?
AND (owner_id = ? OR ? = ANY(mentions))"))
(defn- get-unread-comment-threads
[conn profile-id team-id]
(let [profile (-> (db/get conn :profile {:id profile-id})
(profile/decode-row))
notify (or (-> profile :props :notifications :dashboard-comments) :all)]
(case notify
:all
(->> (db/exec! conn [sql:unread-all-comment-threads-by-team profile-id team-id])
(into [] xf-decode-row))
:partial
(->> (db/exec! conn [sql:unread-partial-comment-threads-by-team profile-id team-id profile-id profile-id])
(into [] xf-decode-row))
[])))
;; --- COMMAND: Get Single Comment Thread ;; --- COMMAND: Get Single Comment Thread
@@ -343,16 +321,17 @@
[:id ::sm/uuid] [:id ::sm/uuid]
[:share-id {:optional true} [:maybe ::sm/uuid]]]) [:share-id {:optional true} [:maybe ::sm/uuid]]])
(def ^:private sql:get-comment-thread
(get-comment-threads-sql "AND ct.file_id = ? AND ct.id = ?"))
(sv/defmethod ::get-comment-thread (sv/defmethod ::get-comment-thread
{::doc/added "1.15" {::doc/added "1.15"
::sm/params schema:get-comment-thread} ::sm/params schema:get-comment-thread}
[cfg {:keys [::rpc/profile-id file-id id share-id] :as params}] [cfg {:keys [::rpc/profile-id file-id id share-id] :as params}]
(db/run! cfg (fn [{:keys [::db/conn]}] (db/run! cfg (fn [{:keys [::db/conn]}]
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
(let [sql (str "WITH threads AS (" sql:comment-threads ")" (some-> (db/exec-one! conn [sql:get-comment-thread profile-id file-id id])
"SELECT * FROM threads WHERE id = ? AND file_id = ?")] (decode-row)))))
(-> (db/exec-one! conn [sql profile-id id file-id])
(decode-row))))))
;; --- COMMAND: Retrieve Comments ;; --- COMMAND: Retrieve Comments
+13 -14
View File
@@ -7,16 +7,16 @@
(ns app.rpc.commands.demo (ns app.rpc.commands.demo
"A demo specific mutations." "A demo specific mutations."
(:require (:require
[app.auth :refer [derive-password]]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.time :as ct]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.commands.auth :as auth] [app.rpc.commands.auth :as auth]
[app.rpc.commands.profile :as profile]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt]
[buddy.core.codecs :as bc] [buddy.core.codecs :as bc]
[buddy.core.nonce :as bn])) [buddy.core.nonce :as bn]))
@@ -39,21 +39,20 @@
fullname (str "Demo User " sem) fullname (str "Demo User " sem)
password (-> (bn/random-bytes 16) password (-> (bn/random-bytes 16)
(bc/bytes->b64u) (bc/bytes->b64 true)
(bc/bytes->str)) (bc/bytes->str))
params {:email email params {:email email
:fullname fullname :fullname fullname
:is-active true :is-active true
:deleted-at (dt/in-future (cf/get-deletion-delay)) :is-demo true
:password (profile/derive-password cfg password) :deleted-at (ct/in-future (cf/get-deletion-delay))
:props {}}] :password (derive-password password)
:props {}}
profile (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
(let [profile (db/tx-run! cfg (fn [{:keys [::db/conn]}] (->> (auth/create-profile cfg params)
(->> (auth/create-profile! conn params) (auth/create-profile-rels conn))))]
(auth/create-profile-rels! conn))))] (with-meta {:email email
(with-meta {:email email :password password}
:password password} {::audit/profile-id (:id profile)})))
{::audit/profile-id (:id profile)}))))
Loaded 100 of 2127 files, more files were not shown because too many files have changed in this diff. Show more