From cf46051f569cdcd974247e004f8d25af69d21981 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Jan 2026 19:39:52 +0100 Subject: [PATCH 01/20] :fire: Remove .traivis.yml file from the repository --- .travis.yml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4419ff22ff..0000000000 --- a/.travis.yml +++ /dev/null @@ -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 From 8c7fd0af4b4966f8ae48cd724106f0f878d7c888 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Wed, 21 Jan 2026 09:17:03 +0100 Subject: [PATCH 02/20] :bug: Fix shadow reference validation (#8132) --- frontend/src/app/main/ui/forms.cljs | 1 + .../tokens/management/forms/controls/input.cljs | 13 +++++++++---- .../workspace/tokens/management/forms/shadow.cljs | 1 + frontend/translations/es.po | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/forms.cljs b/frontend/src/app/main/ui/forms.cljs index a126ff4320..6e49615470 100644 --- a/frontend/src/app/main/ui/forms.cljs +++ b/frontend/src/app/main/ui/forms.cljs @@ -23,6 +23,7 @@ touched? (and (contains? (:data @form) input-name) (get-in @form [:touched input-name])) + error (get-in @form [:errors input-name]) value (get-in @form [:data input-name] "") diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs index cc9cb79426..2e57f197be 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs @@ -236,12 +236,14 @@ (on-composite-input-change form field value false)) ([form field value trim?] (letfn [(clean-errors [errors] - (-> errors - (dissoc field) - (not-empty)))] + (some-> errors + (update :value #(when (map? %) (dissoc % field))) + (update :value #(when (seq %) %)) + (not-empty)))] (swap! form (fn [state] (-> state (assoc-in [:data :value field] (if trim? (str/trim value) value)) + (assoc-in [:touched :value field] true) (update :errors clean-errors) (update :extra-errors clean-errors))))))) @@ -257,6 +259,9 @@ value (get-in @form [:data :value input-name] "") + touched? + (get-in @form [:touched :value input-name]) + resolve-stream (mf/with-memo [token] (if-let [value (get-in token [:value input-name])] @@ -284,7 +289,7 @@ :hint-message (:message hint) :hint-type (:type hint)}) props - (if error + (if (and touched? error) (mf/spread-props props {:hint-type "error" :hint-message (:message error)}) props) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs index e243042a3d..53c6d8e402 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs @@ -291,6 +291,7 @@ [:color {:optional true} [:maybe :string]] [:color-result {:optional true} ::sm/any] [:inset {:optional true} [:maybe :boolean]]]]] + (if (= active-tab :reference) [:reference {:optional false} ::sm/text] [:reference {:optional true} [:maybe :string]])]] diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 2101493c92..d69baf6480 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7757,7 +7757,7 @@ msgstr "Line height (multiplicador, px o %) o {alias}" #: src/app/main/data/workspace/tokens/errors.cljs:57 msgid "workspace.tokens.missing-references" -msgstr "Referéncias de tokens no encontradas:" +msgstr "Referencias de tokens no encontradas: " #: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123 msgid "workspace.tokens.more-options" From b6a9579c98121898a823e4f96c9bb96630d59f93 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 21 Jan 2026 12:23:44 +0100 Subject: [PATCH 03/20] :sparkles: 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. --- frontend/src/app/main/ui/dashboard/file_menu.cljs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 85b3dc2ac8..c2e825c2a5 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -193,11 +193,11 @@ restore-fn (fn [_] (st/emit! (dd/restore-files-immediately - (with-meta {:team-id (:id current-team) + (with-meta {:team-id current-team-id :ids (into #{} d/xf:map-id files)} {:on-success #(st/emit! (ntf/success (tr "dashboard.restore-success-notification" (:name file))) - (dd/fetch-projects (:id current-team)) - (dd/fetch-deleted-files (:id current-team))) + (dd/fetch-projects current-team-id) + (dd/fetch-deleted-files current-team-id)) :on-error #(st/emit! (ntf/error (tr "dashboard.errors.error-on-restore-file" (:name file))))})))) on-restore-immediately @@ -214,7 +214,7 @@ on-delete-immediately (fn [] (let [accept-fn #(st/emit! (dd/delete-files-immediately - {:team-id (:id current-team) + {:team-id current-team-id :ids (into #{} d/xf:map-id files)}))] (st/emit! (modal/show {:type :confirm @@ -244,8 +244,7 @@ (for [project current-projects] {:name (get-project-name project) :id (get-project-id project) - :handler (on-move (:id current-team) - (:id project))}) + :handler (on-move current-team-id (:id project))}) (when (seq other-teams) [{:name (tr "dashboard.move-to-other-team") :id "move-to-other-team" From 7cce4c65326697a6d4b186064e941110b141d344 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Wed, 21 Jan 2026 13:09:22 +0100 Subject: [PATCH 04/20] :bug: Fix unhandled exception tokens creation dialog (#8136) --- CHANGES.md | 2 +- frontend/src/app/main/refs.cljs | 3 +++ .../management/forms/controls/color_input.cljs | 10 +++++++++- .../tokens/management/forms/form_container.cljs | 13 ++++++------- .../tokens/management/forms/generic_form.cljs | 13 +++++++++---- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6d861db12..a6e50eb8f1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -29,7 +29,7 @@ - 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) ## 2.12.1 diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 76d02544cc..7372e337f7 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -480,6 +480,9 @@ (def workspace-token-sets-tree (l/derived (d/nilf ctob/get-set-tree) tokens-lib)) +(def workspace-all-tokens-map + (l/derived (d/nilf ctob/get-all-tokens) tokens-lib)) + (def workspace-active-theme-paths (l/derived (d/nilf ctob/get-active-theme-paths) tokens-lib)) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs index ce18196c22..a4bfbf1b0c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs @@ -140,6 +140,9 @@ error (get-in @form [:errors input-name]) + extra-error + (get-in @form [:extra-errors input-name]) + value (get-in @form [:data input-name] "") @@ -247,9 +250,14 @@ :hint-type (:type hint)}) props - (if (and error touched?) + (cond + (and error touched?) (mf/spread-props props {:hint-type "error" :hint-message (:message error)}) + (and extra-error touched?) + (mf/spread-props props {:hint-type "error" + :hint-message (:message extra-error)}) + :else props)] (mf/with-effect [resolve-stream tokens token input-name] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs index 70797979c6..af394eadee 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs @@ -23,20 +23,19 @@ (let [token-type (or (:type token) token-type) - tokens-in-selected-set - (mf/deref refs/workspace-all-tokens-in-selected-set) - token-path (mf/with-memo [token] (cft/token-name->path (:name token))) - tokens-tree-in-selected-set - (mf/with-memo [token-path tokens-in-selected-set] - (-> (ctob/tokens-tree tokens-in-selected-set) + all-tokens (mf/deref refs/workspace-all-tokens-map) + + all-tokens + (mf/with-memo [token-path all-tokens] + (-> (ctob/tokens-tree all-tokens) (d/dissoc-in token-path))) props (mf/spread-props props {:token-type token-type - :tokens-tree-in-selected-set tokens-tree-in-selected-set + :all-token-tree all-tokens :token token}) text-case-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-case-value-enter")}) text-decoration-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-decoration-value-enter")}) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs index 038dbeae03..1a4a6b14b9 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs @@ -14,6 +14,7 @@ [app.main.constants :refer [max-input-length]] [app.main.data.modal :as modal] [app.main.data.workspace.tokens.application :as dwta] + [app.main.data.workspace.tokens.errors :as wte] [app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.data.workspace.tokens.propagation :as dwtp] [app.main.refs :as refs] @@ -85,7 +86,7 @@ action is-create selected-token-set-id - tokens-tree-in-selected-set + all-token-tree token-type make-schema input-component @@ -123,8 +124,8 @@ (assoc (:name token) token))) schema - (mf/with-memo [tokens-tree-in-selected-set active-tab] - (make-schema tokens-tree-in-selected-set active-tab)) + (mf/with-memo [all-token-tree active-tab] + (make-schema all-token-tree active-tab)) initial (mf/with-memo [token] @@ -207,7 +208,11 @@ :value (:value valid-token) :description description})) (dwtp/propagate-workspace-tokens) - (modal/hide))))))))] + (modal/hide))))) + (fn [{:keys [errors]}] + (let [error-messages (wte/humanize-errors errors) + error-message (first error-messages)] + (swap! form assoc-in [:extra-errors :value] {:message error-message}))))))] [:> fc/form* {:class (stl/css :form-wrapper) :form form From 525adcfcbed2ba2e8e671a89ad13fe08ce27cd0b Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 21 Jan 2026 13:12:06 +0100 Subject: [PATCH 05/20] :sparkles: Add wasm build on watch app script (devenv) --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 8279b3fd3b..a1a89771b1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,7 +48,7 @@ "watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook", "clear:shadow-cache": "rm -rf .shadow-cljs", "watch": "exit 0", - "watch:app": "yarn run clear:shadow-cache && concurrently --kill-others-on-fail \"yarn run watch:app:assets\" \"yarn run watch:app:main\" \"yarn run watch:app:libs\"", + "watch:app": "yarn run clear:shadow-cache && yarn run build:wasm && concurrently --kill-others-on-fail \"yarn run watch:app:assets\" \"yarn run watch:app:main\" \"yarn run watch:app:libs\"", "watch:storybook": "yarn run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"" }, "devDependencies": { From b8c70be9a26a399d4a6a930c7a5a805c81bfd5c9 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 21 Jan 2026 13:14:02 +0100 Subject: [PATCH 06/20] :sparkles: Make frontend build and watch process more resilent to errors --- frontend/scripts/_helpers.js | 50 +++++++++++++++++++++++++--- frontend/scripts/_worker.js | 2 -- frontend/scripts/build-app-assets.js | 1 + frontend/scripts/watch.js | 28 +++++++++++----- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index c1c018c74c..58d3586995 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -174,6 +174,7 @@ export async function watch(baseDir, predicate, callback) { const watcher = new Watcher(baseDir, { persistent: true, recursive: true, + debounce: 500 }); watcher.on("change", (path) => { @@ -181,8 +182,19 @@ export async function watch(baseDir, predicate, callback) { callback(path); } }); + + + watcher.on("error", (cause) => { + console.log("WATCHER ERROR", cause); + }); } +export async function ensureDirectories() { + await fs.mkdir("./resources/public/js/worker/", { recursive: true }); + await fs.mkdir("./resources/public/css/", { recursive: true }); +} + + async function readManifestFile(resource) { const manifestPath = "resources/public/" + resource; let content = await fs.readFile(manifestPath, { encoding: "utf8" }); @@ -260,6 +272,9 @@ const markedOptions = { marked.use(markedOptions); export async function compileTranslations() { + const outputDir = "resources/public/js/"; + await fs.mkdir(outputDir, { recursive: true }); + const langs = [ "ar", "ca", @@ -341,7 +356,6 @@ export async function compileTranslations() { } const esm = `export default ${JSON.stringify(result, null, 0)};\n`; - const outputDir = "resources/public/js/"; const outputFile = ph.join(outputDir, "translation." + lang + ".js"); await fs.writeFile(outputFile, esm); } @@ -499,17 +513,43 @@ export async function compileStyles() { export async function compileSvgSprites() { const start = process.hrtime(); log.info("init: compile svgsprite"); - await generateSvgSprites(); + let error = false; + + try { + await generateSvgSprites(); + } catch (cause) { + error = cause; + } + const end = process.hrtime(start); - log.info("done: compile svgsprite", `(${ppt(end)})`); + + if (error) { + log.error("error: compile svgsprite", `(${ppt(end)})`); + console.error(error); + } else { + log.info("done: compile svgsprite", `(${ppt(end)})`); + } } export async function compileTemplates() { const start = process.hrtime(); + let error = false; log.info("init: compile templates"); - await generateTemplates(); + + try { + await generateTemplates(); + } catch (cause) { + error = cause; + } + const end = process.hrtime(start); - log.info("done: compile templates", `(${ppt(end)})`); + + if (error) { + log.error("error: compile templates", `(${ppt(end)})`); + console.error(error); + } else { + log.info("done: compile templates", `(${ppt(end)})`); + } } export async function compilePolyfills() { diff --git a/frontend/scripts/_worker.js b/frontend/scripts/_worker.js index 80ba2f6bf6..c46b26993b 100644 --- a/frontend/scripts/_worker.js +++ b/frontend/scripts/_worker.js @@ -28,14 +28,12 @@ async function compileFile(path) { ], sourceMap: false, }); - // console.dir(result); resolve({ inputPath: path, outputPath: dest, css: result.css, }); } catch (cause) { - console.error(cause); reject(cause); } }); diff --git a/frontend/scripts/build-app-assets.js b/frontend/scripts/build-app-assets.js index 6ccc435839..e0fe385dce 100644 --- a/frontend/scripts/build-app-assets.js +++ b/frontend/scripts/build-app-assets.js @@ -1,5 +1,6 @@ import * as h from "./_helpers.js"; +await h.ensureDirectories(); await h.compileStyles(); await h.copyAssets(); await h.copyWasmPlayground(); diff --git a/frontend/scripts/watch.js b/frontend/scripts/watch.js index e2d9ff399d..38007d4454 100644 --- a/frontend/scripts/watch.js +++ b/frontend/scripts/watch.js @@ -12,19 +12,31 @@ let sass = null; async function compileSassAll() { const start = process.hrtime(); + let error = false; + log.info("init: compile styles"); - sass = await h.compileSassAll(worker); - let output = await h.concatSass(sass); - await fs.writeFile("./resources/public/css/main.css", output); + try { + sass = await h.compileSassAll(worker); + let output = await h.concatSass(sass); + await fs.writeFile("./resources/public/css/main.css", output); - if (isDebug) { - let debugCSS = await h.compileSassDebug(worker); - await fs.writeFile("./resources/public/css/debug.css", debugCSS); + if (isDebug) { + let debugCSS = await h.compileSassDebug(worker); + await fs.writeFile("./resources/public/css/debug.css", debugCSS); + } + } catch (cause) { + error = cause; } const end = process.hrtime(start); - log.info("done: compile styles", `(${ppt(end)})`); + + if (error) { + log.error("error: compile styles", `(${ppt(end)})`); + console.error(error); + } else { + log.info("done: compile styles", `(${ppt(end)})`); + } } async function compileSass(path) { @@ -48,7 +60,7 @@ async function compileSass(path) { } } -await fs.mkdir("./resources/public/css/", { recursive: true }); +await h.ensureDirectories(); await compileSassAll(); await h.copyAssets(); await h.copyWasmPlayground(); From 01a4ffeb8b2e67067ff4735d10f2ed36ba8b2858 Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Wed, 21 Jan 2026 15:41:00 +0100 Subject: [PATCH 07/20] :arrow_up: Updated plugins release to 1.4.0 (#8148) --- frontend/package.json | 2 +- frontend/yarn.lock | 20 ++++++------ plugins/CHANGELOG.md | 31 +++++++++++++++++++ plugins/docs/publish-package.md | 37 +++++++++++++++-------- plugins/libs/plugin-types/package.json | 2 +- plugins/libs/plugins-runtime/package.json | 4 +-- plugins/libs/plugins-styles/package.json | 2 +- plugins/pnpm-lock.yaml | 14 +++------ plugins/tools/scripts/publish.ts | 11 ------- 9 files changed, 74 insertions(+), 49 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index a1a89771b1..df11969036 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@penpot/draft-js": "portal:./packages/draft-js", "@penpot/mousetrap": "portal:./packages/mousetrap", - "@penpot/plugins-runtime": "1.3.2", + "@penpot/plugins-runtime": "1.4.0", "@penpot/svgo": "penpot/svgo#v3.2", "@penpot/text-editor": "portal:./text-editor", "@playwright/test": "1.57.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7b6b7718c1..46f749ead1 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1191,21 +1191,21 @@ __metadata: languageName: node linkType: soft -"@penpot/plugin-types@npm:^1.3.2": - version: 1.3.2 - resolution: "@penpot/plugin-types@npm:1.3.2" - checksum: 10c0/3f624472c260721ad89bf8d944e75acf6a9c9577271a757acb77574102213914051d1a32d5ab16e6ba16ae077fff78cf7a0f6d11d18351dfc214426677a67468 +"@penpot/plugin-types@npm:^1.4.0": + version: 1.4.0 + resolution: "@penpot/plugin-types@npm:1.4.0" + checksum: 10c0/bd97a17e7083efa307260dcaec841596012ae65964a947738a6ef5d6114b161616f3cf83fa21ed7ede322334c25f1c3cd4027a5409ce336f40455681a4ea192d languageName: node linkType: hard -"@penpot/plugins-runtime@npm:1.3.2": - version: 1.3.2 - resolution: "@penpot/plugins-runtime@npm:1.3.2" +"@penpot/plugins-runtime@npm:1.4.0": + version: 1.4.0 + resolution: "@penpot/plugins-runtime@npm:1.4.0" dependencies: - "@penpot/plugin-types": "npm:^1.3.2" + "@penpot/plugin-types": "npm:^1.4.0" ses: "npm:^1.1.0" zod: "npm:^3.22.4" - checksum: 10c0/b6d2cb3a57bcbe58232db52b8224d1817495e96b34997bfa72421629b5f34a8c9cc71357c315dcab9d52ea036ed632a5efe0ac50f52e730901c02d498dfa1313 + checksum: 10c0/497c36c8e0edb7bc8e5ca60ab89fbc0f63e96adab6733bd7244df53921395e31743f37f9a28e57139009d1c65bee181f972f23bc821d2c1e0f05ce610b473ac1 languageName: node linkType: hard @@ -4176,7 +4176,7 @@ __metadata: dependencies: "@penpot/draft-js": "portal:./packages/draft-js" "@penpot/mousetrap": "portal:./packages/mousetrap" - "@penpot/plugins-runtime": "npm:1.3.2" + "@penpot/plugins-runtime": "npm:1.4.0" "@penpot/svgo": "penpot/svgo#v3.2" "@penpot/text-editor": "portal:./text-editor" "@playwright/test": "npm:1.57.0" diff --git a/plugins/CHANGELOG.md b/plugins/CHANGELOG.md index 67b0b183fa..b36a5386dc 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -1,3 +1,34 @@ +## 1.4.0 (2026-01-21) + +### 🚀 Features + +- switch component ([7d68450](https://github.com/penpot/penpot-plugins/commit/7d68450)) +- Add variants to plugins API ([04f3c26](https://github.com/penpot/penpot-plugins/commit/04f3c26)) +- format ci job ([17b5834](https://github.com/penpot/penpot-plugins/commit/17b5834)) +- fix problem with ci ([4b3c50f](https://github.com/penpot/penpot-plugins/commit/4b3c50f)) +- change in workflow ([3a69f51](https://github.com/penpot/penpot-plugins/commit/3a69f51)) +- **plugin-types:** add methods to modify the index for shapes ([4ad50af](https://github.com/penpot/penpot-plugins/commit/4ad50af)) +- **plugin-types:** change content type and added new attributes ([dbb68a5](https://github.com/penpot/penpot-plugins/commit/dbb68a5)) +- **plugins-runtime:** add data method to image data ([f077481](https://github.com/penpot/penpot-plugins/commit/f077481)) +- **plugins-runtime:** fix problem with linter ([30f4984](https://github.com/penpot/penpot-plugins/commit/30f4984)) +- **plugins-runtime:** allow openPage() to toggle opening on a new window or not ([da8288b](https://github.com/penpot/penpot-plugins/commit/da8288b)) + +### 🩹 Fixes + +- package-lock.json ([d1d940a](https://github.com/penpot/penpot-plugins/commit/d1d940a)) +- fonts gdpr & switch provider ([d63231e](https://github.com/penpot/penpot-plugins/commit/d63231e)) +- missing changes ([b8fc936](https://github.com/penpot/penpot-plugins/commit/b8fc936)) +- format ci ([e0fab2e](https://github.com/penpot/penpot-plugins/commit/e0fab2e)) +- fetch main only in pr ([e48c5d4](https://github.com/penpot/penpot-plugins/commit/e48c5d4)) + +### ❤️ Thank You + +- alonso.torres +- Juanfran @juanfran +- Michał Korczak +- Miguel de Benito Delgado +- Pablo Alba + ## 1.3.2 (2025-07-04) ### 🩹 Fixes diff --git a/plugins/docs/publish-package.md b/plugins/docs/publish-package.md index aaee111e6a..a694792f68 100644 --- a/plugins/docs/publish-package.md +++ b/plugins/docs/publish-package.md @@ -7,6 +7,29 @@ This guide details the process of publishing `plugin-types`, for plugin development. Below is a walkthrough for publishing these packages and managing releases. +**Warning** +Before generating the release, please, check the update the changelog with +the changes that will be released. + +## Problem with pnpm + +There is an issue with dependencies and release with pnpm. For it to work +you need to add the following into your `.npmrc` + +``` +link-workspace-packages=true +``` + +## NPM Authentication + +You need to generate a temporary access token in the NPM website. + +Once you have the token add the following to the `.npmrc` + +``` +//registry.npmjs.org/:_authToken= +``` + ## Publishing Libraries Publishing packages enables the distribution of types and styles @@ -35,28 +58,16 @@ pnpm run release -- --dry-run false This command will: -- Update the `CHANGELOG.md` - Update the library's `package.json` version - Generate a commit -- Create a new git tag - Publish to NPM with the `latest` tag Ensure everything is correct before proceeding with the git push. Once verified, execute the following commands: ```shell +git commit -m ":arrow_up: Updated plugins release to X.X.X" git push -git push origin vX.X.X -``` - -Replace `vX.X.X` with the new version number. - -> 📘 To update the documentation site, you must also update the `stable` branch: - -```shell -git checkout stable -git merge main -git push origin stable ``` For detailed information, refer to the [Nx Release diff --git a/plugins/libs/plugin-types/package.json b/plugins/libs/plugin-types/package.json index c9dfa6bdc7..2c905672ea 100644 --- a/plugins/libs/plugin-types/package.json +++ b/plugins/libs/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@penpot/plugin-types", - "version": "1.3.2", + "version": "1.4.0", "typings": "./index.d.ts", "type": "module" } diff --git a/plugins/libs/plugins-runtime/package.json b/plugins/libs/plugins-runtime/package.json index 2cc3ae3055..676e98b057 100644 --- a/plugins/libs/plugins-runtime/package.json +++ b/plugins/libs/plugins-runtime/package.json @@ -1,8 +1,8 @@ { "name": "@penpot/plugins-runtime", - "version": "1.3.2", + "version": "1.4.0", "dependencies": { - "@penpot/plugin-types": "^1.3.2", + "@penpot/plugin-types": "^1.4.0", "ses": "^1.1.0", "zod": "^3.22.4" }, diff --git a/plugins/libs/plugins-styles/package.json b/plugins/libs/plugins-styles/package.json index 511c3f7f86..372745ea33 100644 --- a/plugins/libs/plugins-styles/package.json +++ b/plugins/libs/plugins-styles/package.json @@ -1,5 +1,5 @@ { "name": "@penpot/plugin-styles", - "version": "1.3.2", + "version": "1.4.0", "dependencies": {} } diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml index d1dd76b579..4230d80c51 100644 --- a/plugins/pnpm-lock.yaml +++ b/plugins/pnpm-lock.yaml @@ -230,8 +230,8 @@ importers: libs/plugins-runtime: dependencies: '@penpot/plugin-types': - specifier: ^1.3.2 - version: 1.3.2 + specifier: ^1.4.0 + version: link:../plugin-types ses: specifier: ^1.1.0 version: 1.14.0 @@ -4200,12 +4200,6 @@ packages: } engines: { node: '>= 10.0.0' } - '@penpot/plugin-types@1.3.2': - resolution: - { - integrity: sha512-f0kmmZaFNs9sGtSmqmSJQYCs5Qt+KYgTD8RneUjL+Dv+zfNQnd5e4L+iHSYFJ4HWvcDvTiK7F/gya7PwMTu7WA==, - } - '@phenomnomnominal/tsquery@5.0.1': resolution: { @@ -13194,6 +13188,7 @@ packages: integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==, } engines: { node: '>=10' } + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me tar@7.5.2: resolution: @@ -13201,6 +13196,7 @@ packages: integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==, } engines: { node: '>=18' } + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me terser-webpack-plugin@5.3.16: resolution: @@ -18203,8 +18199,6 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true - '@penpot/plugin-types@1.3.2': {} - '@phenomnomnominal/tsquery@5.0.1(typescript@5.6.3)': dependencies: esquery: 1.6.0 diff --git a/plugins/tools/scripts/publish.ts b/plugins/tools/scripts/publish.ts index 7ea5970617..41ec00b7a2 100644 --- a/plugins/tools/scripts/publish.ts +++ b/plugins/tools/scripts/publish.ts @@ -69,17 +69,6 @@ const determineArgs = async () => { }, ); - await releaseChangelog({ - dryRun: args.dryRun, - versionData: result.projectsVersionData, - version: result.workspaceVersion, - gitCommitMessage: `chore(release): publish ${result.workspaceVersion} [skip ci]`, - gitCommit: true, - gitTag: true, - verbose: args.verbose, - firstRelease: args.firstRelease, - }); - if (!args.skipPublish) { await releasePublish({ dryRun: args.dryRun, From 656f81f89fdf9ea236bfc88665f90415c79d80b6 Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Wed, 21 Jan 2026 17:36:58 +0100 Subject: [PATCH 08/20] :arrow_up: Update plugins to 1.4.2 (#8157) --- frontend/package.json | 2 +- frontend/yarn.lock | 20 +++++++++---------- plugins/CHANGELOG.md | 4 ++++ plugins/libs/plugin-types/package.json | 2 +- plugins/libs/plugins-runtime/package.json | 4 ++-- .../plugins-runtime/src/lib/create-sandbox.ts | 4 ++-- plugins/libs/plugins-styles/package.json | 2 +- plugins/pnpm-lock.yaml | 2 +- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index df11969036..f4c36c6ef6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@penpot/draft-js": "portal:./packages/draft-js", "@penpot/mousetrap": "portal:./packages/mousetrap", - "@penpot/plugins-runtime": "1.4.0", + "@penpot/plugins-runtime": "1.4.2", "@penpot/svgo": "penpot/svgo#v3.2", "@penpot/text-editor": "portal:./text-editor", "@playwright/test": "1.57.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 46f749ead1..ebc1ed2427 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1191,21 +1191,21 @@ __metadata: languageName: node linkType: soft -"@penpot/plugin-types@npm:^1.4.0": - version: 1.4.0 - resolution: "@penpot/plugin-types@npm:1.4.0" - checksum: 10c0/bd97a17e7083efa307260dcaec841596012ae65964a947738a6ef5d6114b161616f3cf83fa21ed7ede322334c25f1c3cd4027a5409ce336f40455681a4ea192d +"@penpot/plugin-types@npm:^1.4.2": + version: 1.4.2 + resolution: "@penpot/plugin-types@npm:1.4.2" + checksum: 10c0/b0972fe75c97e697eb1044c89db660393886b3c30676f8436ff4ab56c5bf0397b2c675697ae1b9c5fe40bc95a803aecf6d7ac356dbf6d3535bf8baec5d05eab1 languageName: node linkType: hard -"@penpot/plugins-runtime@npm:1.4.0": - version: 1.4.0 - resolution: "@penpot/plugins-runtime@npm:1.4.0" +"@penpot/plugins-runtime@npm:1.4.2": + version: 1.4.2 + resolution: "@penpot/plugins-runtime@npm:1.4.2" dependencies: - "@penpot/plugin-types": "npm:^1.4.0" + "@penpot/plugin-types": "npm:^1.4.2" ses: "npm:^1.1.0" zod: "npm:^3.22.4" - checksum: 10c0/497c36c8e0edb7bc8e5ca60ab89fbc0f63e96adab6733bd7244df53921395e31743f37f9a28e57139009d1c65bee181f972f23bc821d2c1e0f05ce610b473ac1 + checksum: 10c0/af084d906cce9a6dea956fe5420111d7ea37c7620737a1e3d4f12958cb302a8f697c1229c237107c28fbb3b9f37eee308e6d16262b04ad56ae6f76c7a12f12e5 languageName: node linkType: hard @@ -4176,7 +4176,7 @@ __metadata: dependencies: "@penpot/draft-js": "portal:./packages/draft-js" "@penpot/mousetrap": "portal:./packages/mousetrap" - "@penpot/plugins-runtime": "npm:1.4.0" + "@penpot/plugins-runtime": "npm:1.4.2" "@penpot/svgo": "penpot/svgo#v3.2" "@penpot/text-editor": "portal:./text-editor" "@playwright/test": "npm:1.57.0" diff --git a/plugins/CHANGELOG.md b/plugins/CHANGELOG.md index b36a5386dc..e15c253923 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.2 (2026-01-21) + +- **plugin-types:** fix atob/btoa functions + ## 1.4.0 (2026-01-21) ### 🚀 Features diff --git a/plugins/libs/plugin-types/package.json b/plugins/libs/plugin-types/package.json index 2c905672ea..0e8e1ca47d 100644 --- a/plugins/libs/plugin-types/package.json +++ b/plugins/libs/plugin-types/package.json @@ -1,6 +1,6 @@ { "name": "@penpot/plugin-types", - "version": "1.4.0", + "version": "1.4.2", "typings": "./index.d.ts", "type": "module" } diff --git a/plugins/libs/plugins-runtime/package.json b/plugins/libs/plugins-runtime/package.json index 676e98b057..5d3756b9fc 100644 --- a/plugins/libs/plugins-runtime/package.json +++ b/plugins/libs/plugins-runtime/package.json @@ -1,8 +1,8 @@ { "name": "@penpot/plugins-runtime", - "version": "1.4.0", + "version": "1.4.2", "dependencies": { - "@penpot/plugin-types": "^1.4.0", + "@penpot/plugin-types": "^1.4.2", "ses": "^1.1.0", "zod": "^3.22.4" }, diff --git a/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts b/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts index 9d03f9da95..70c024835e 100644 --- a/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts +++ b/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts @@ -118,8 +118,8 @@ export function createSandbox( // Window properties console: ses.harden(window.console), devicePixelRatio: ses.harden(window.devicePixelRatio), - atob: ses.harden(window.atob), - btoa: ses.harden(window.btoa), + atob: ses.harden(window.atob.bind(null)), + btoa: ses.harden(window.btoa.bind(null)), structuredClone: ses.harden(window.structuredClone), }; diff --git a/plugins/libs/plugins-styles/package.json b/plugins/libs/plugins-styles/package.json index 372745ea33..10144ed9d3 100644 --- a/plugins/libs/plugins-styles/package.json +++ b/plugins/libs/plugins-styles/package.json @@ -1,5 +1,5 @@ { "name": "@penpot/plugin-styles", - "version": "1.4.0", + "version": "1.4.2", "dependencies": {} } diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml index 4230d80c51..b1cdc2d9bc 100644 --- a/plugins/pnpm-lock.yaml +++ b/plugins/pnpm-lock.yaml @@ -230,7 +230,7 @@ importers: libs/plugins-runtime: dependencies: '@penpot/plugin-types': - specifier: ^1.4.0 + specifier: ^1.4.2 version: link:../plugin-types ses: specifier: ^1.1.0 From e6b5364a8403c34138343edb4763c5fffd79bc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 21 Jan 2026 18:31:54 +0100 Subject: [PATCH 09/20] :wrench: Deploy penpot api documentation --- .github/workflows/plugins-deploy-api-doc.yml | 73 ++++++++++++++++++++ plugins/wrangle-penpot-plugins-api-doc.toml | 4 ++ 2 files changed, 77 insertions(+) create mode 100644 .github/workflows/plugins-deploy-api-doc.yml create mode 100644 plugins/wrangle-penpot-plugins-api-doc.toml diff --git a/.github/workflows/plugins-deploy-api-doc.yml b/.github/workflows/plugins-deploy-api-doc.yml new file mode 100644 index 0000000000..1c67d4f47b --- /dev/null +++ b/.github/workflows/plugins-deploy-api-doc.yml @@ -0,0 +1,73 @@ +name: Plugins/api-doc deployer + +on: + push: + branches: + - develop + - staging + - main + paths: + - "plugins/**" + - ".github/workflows/deploy-plugin-docs.yml" + - "wrangle-penpot-plugins-api-doc.toml" + workflow_dispatch: + inputs: + gh_ref: + description: 'Name of the branch or ref' + type: string + required: true + default: 'develop' + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + defaults: + run: + working-directory: plugins + 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 }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + cache: "pnpm" + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build docs + 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 ;; + staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;; + develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;; + *) echo "Unsupported branch ${REF}" && exit 1 ;; + esac + + - name: Deploy to Cloudflare Workers + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: deploy --config wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }} diff --git a/plugins/wrangle-penpot-plugins-api-doc.toml b/plugins/wrangle-penpot-plugins-api-doc.toml new file mode 100644 index 0000000000..e9535be2d8 --- /dev/null +++ b/plugins/wrangle-penpot-plugins-api-doc.toml @@ -0,0 +1,4 @@ +name = "penpot-plugins-api-doc" +compatibility_date = "2025-01-01" + +assets = { directory = "dist/doc" } From 2574ad33153039b28d30bd004f6373bc72cb0006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 22 Jan 2026 11:38:22 +0100 Subject: [PATCH 10/20] :wrench: Fixes to the API documentation deployer --- .github/workflows/plugins-deploy-api-doc.yml | 58 +++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/.github/workflows/plugins-deploy-api-doc.yml b/.github/workflows/plugins-deploy-api-doc.yml index 1c67d4f47b..62a87745bb 100644 --- a/.github/workflows/plugins-deploy-api-doc.yml +++ b/.github/workflows/plugins-deploy-api-doc.yml @@ -7,16 +7,22 @@ on: - staging - main paths: - - "plugins/**" - - ".github/workflows/deploy-plugin-docs.yml" - - "wrangle-penpot-plugins-api-doc.toml" + - "plugins/libs/plugin-types/index.d.ts" + - "plugins/libs/plugin-types/REAME.md" + - "plugins/tools/typedoc.css" + - "plugins/CHANGELOG.md" + - "plugins/wrangle-penpot-plugins-api-doc.toml" workflow_dispatch: inputs: gh_ref: - description: 'Name of the branch or ref' - type: string + description: 'Name of the branch' + type: choice required: true default: 'develop' + options: + - develop + - staging + - main permissions: contents: read @@ -24,9 +30,6 @@ permissions: jobs: deploy: runs-on: ubuntu-latest - defaults: - run: - working-directory: plugins steps: - name: Extract some useful variables id: vars @@ -39,20 +42,44 @@ jobs: fetch-depth: 0 ref: ${{ steps.vars.outputs.gh_ref }} + # START: Setup Node and PNPM enabling cache - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - cache: "pnpm" + node-version-file: .nvmrc - - name: Setup pnpm - uses: pnpm/action-setup@v4 + - 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: - run_install: false + 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 dependencies - run: pnpm install --frozen-lockfile + - 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 @@ -68,6 +95,7 @@ jobs: - 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 wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }} From e53f33520419bf912a15d3fe18c26196df141290 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 23 Jan 2026 09:35:53 +0100 Subject: [PATCH 11/20] :bug: Fix unhandled error on tokens modal (#8165) --- backend/src/app/email.clj | 2 -- .../src/app/main/data/style_dictionary.cljs | 11 +++++----- .../src/app/main/ui/dashboard/file_menu.cljs | 1 - .../forms/controls/color_input.cljs | 17 ++++++++------ .../forms/controls/fonts_combobox.cljs | 17 ++++++++------ .../management/forms/controls/input.cljs | 22 +++++++++++-------- .../management/forms/form_container.cljs | 13 ++++++----- .../tokens/management/forms/generic_form.cljs | 19 ++++++++-------- 8 files changed, 54 insertions(+), 48 deletions(-) diff --git a/backend/src/app/email.clj b/backend/src/app/email.clj index 43361039d9..44d5cd7e67 100644 --- a/backend/src/app/email.clj +++ b/backend/src/app/email.clj @@ -124,8 +124,6 @@ (throw (IllegalArgumentException. "invalid email body provided"))) (doseq [[name content] attachments] - - (prn "attachment" name) (let [attachment-part (MimeBodyPart.)] (.setFileName attachment-part ^String name) (.setContent attachment-part ^String content (str "text/plain; charset=" charset)) diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 1bf2de0d0a..53405e3073 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -126,7 +126,7 @@ If the `value` is not parseable and/or has missing references returns a map with `:errors`. If the `value` is parseable but is out of range returns a map with `warnings`." [value] - (let [missing-references? (seq (seq (cto/find-token-value-references value))) + (let [missing-references? (seq (cto/find-token-value-references value)) parsed-value (cft/parse-token-value value) out-of-scope (not (<= 0 (:value parsed-value) 1)) references (seq (cto/find-token-value-references value))] @@ -152,15 +152,14 @@ [value] (let [missing-references? (seq (cto/find-token-value-references value)) parsed-value (cft/parse-token-value value) - out-of-scope (< (:value parsed-value) 0) - references (seq (cto/find-token-value-references value))] + out-of-scope (< (:value parsed-value) 0)] (cond (and parsed-value (not out-of-scope)) parsed-value - references - {:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)] - :references references} + missing-references? + {:errors [(wte/error-with-value :error.style-dictionary/missing-reference missing-references?)] + :references missing-references?} (and (not missing-references?) out-of-scope) {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-stroke-width value)]} diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index c2e825c2a5..dfecbc779b 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -202,7 +202,6 @@ on-restore-immediately (fn [] - (prn files) (st/emit! (modal/show {:type :confirm :title (tr "dashboard-restore-file-confirmation.title") diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs index a4bfbf1b0c..9f9d395013 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs @@ -53,10 +53,12 @@ (defn- resolve-value - [tokens prev-token value] + [tokens prev-token token-name value] (let [token {:value value - :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"} + :name (if (str/blank? token-name) + "__PENPOT__TOKEN__NAME__PLACEHOLDER__" + token-name)} tokens (-> tokens @@ -131,6 +133,7 @@ (let [form (mf/use-ctx fc/context) input-name name + token-name (get-in @form [:data :name] nil) touched? @@ -260,10 +263,10 @@ :else props)] - (mf/with-effect [resolve-stream tokens token input-name] + (mf/with-effect [resolve-stream tokens token input-name token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] @@ -309,7 +312,7 @@ (let [form (mf/use-ctx fc/context) input-name name - + token-name (get-in @form [:data :name] nil) error (get-in @form [:errors :value value-subfield index input-name]) @@ -422,10 +425,10 @@ :hint-message (:message error)}) props)] - (mf/with-effect [resolve-stream tokens token input-name index value-subfield] + (mf/with-effect [resolve-stream tokens token input-name index value-subfield token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs index ba6a8348c2..80f2d91133 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs @@ -49,10 +49,12 @@ ;; validate data within the form state. (defn- resolve-value - [tokens prev-token value] + [tokens prev-token token-name value] (let [token {:value (cto/split-font-family value) - :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"} + :name (if (str/blank? token-name) + "__PENPOT__TOKEN__NAME__PLACEHOLDER__" + token-name)} tokens (-> tokens @@ -73,6 +75,7 @@ [{:keys [token tokens name] :rest props}] (let [form (mf/use-ctx fc/context) input-name name + token-name (get-in @form [:data :name] nil) touched? (and (contains? (:data @form) input-name) @@ -152,10 +155,10 @@ :hint-message (:message error)}) props)] - (mf/with-effect [resolve-stream tokens token input-name touched?] + (mf/with-effect [resolve-stream tokens token input-name touched? token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] @@ -200,7 +203,7 @@ [{:keys [token tokens name] :rest props}] (let [form (mf/use-ctx fc/context) input-name name - + token-name (get-in @form [:data :name] nil) error (get-in @form [:errors :value input-name]) @@ -276,10 +279,10 @@ :hint-message (:message error)}) props)] - (mf/with-effect [resolve-stream tokens token input-name] + (mf/with-effect [resolve-stream tokens token input-name token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs index 2e57f197be..0f1b2a79b1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs @@ -139,10 +139,12 @@ (defn- resolve-value - [tokens prev-token value] + [tokens prev-token token-name value] (let [token {:value value - :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"} + :name (if (str/blank? token-name) + "__PENPOT__TOKEN__NAME__PLACEHOLDER__" + token-name)} tokens (-> tokens ;; Remove previous token when renaming a token @@ -163,6 +165,7 @@ (let [form (mf/use-ctx fc/context) input-name name + token-name (get-in @form [:data :name] nil) touched? (and (contains? (:data @form) input-name) @@ -206,11 +209,11 @@ :hint-message (:message error)}) props)] - (mf/with-effect [resolve-stream tokens token input-name] + (mf/with-effect [resolve-stream tokens token input-name token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] @@ -252,6 +255,7 @@ (let [form (mf/use-ctx fc/context) input-name name + token-name (get-in @form [:data :name] nil) error (get-in @form [:errors :value input-name]) @@ -298,10 +302,10 @@ (mf/spread-props props {:hint-formated true}) props)] - (mf/with-effect [resolve-stream tokens token input-name name] + (mf/with-effect [resolve-stream tokens token input-name name token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] @@ -365,7 +369,7 @@ (let [form (mf/use-ctx fc/context) input-name name - + token-name (get-in @form [:data :name] nil) error (get-in @form [:errors :value value-subfield index input-name]) @@ -410,10 +414,10 @@ (mf/spread-props props {:hint-formated true}) props)] - (mf/with-effect [resolve-stream tokens token input-name index value-subfield] + (mf/with-effect [resolve-stream tokens token input-name index value-subfield token-name] (let [subs (->> resolve-stream (rx/debounce 300) - (rx/mapcat (partial resolve-value tokens token)) + (rx/mapcat (partial resolve-value tokens token token-name)) (rx/map (fn [result] (d/update-when result :error (fn [error] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs index af394eadee..70797979c6 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/form_container.cljs @@ -23,19 +23,20 @@ (let [token-type (or (:type token) token-type) + tokens-in-selected-set + (mf/deref refs/workspace-all-tokens-in-selected-set) + token-path (mf/with-memo [token] (cft/token-name->path (:name token))) - all-tokens (mf/deref refs/workspace-all-tokens-map) - - all-tokens - (mf/with-memo [token-path all-tokens] - (-> (ctob/tokens-tree all-tokens) + tokens-tree-in-selected-set + (mf/with-memo [token-path tokens-in-selected-set] + (-> (ctob/tokens-tree tokens-in-selected-set) (d/dissoc-in token-path))) props (mf/spread-props props {:token-type token-type - :all-token-tree all-tokens + :tokens-tree-in-selected-set tokens-tree-in-selected-set :token token}) text-case-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-case-value-enter")}) text-decoration-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-decoration-value-enter")}) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs index 1a4a6b14b9..a260540a92 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs @@ -86,7 +86,7 @@ action is-create selected-token-set-id - all-token-tree + tokens-tree-in-selected-set token-type make-schema input-component @@ -111,8 +111,7 @@ token-title (str/lower (:title token-properties)) - tokens - (mf/deref refs/workspace-active-theme-sets-tokens) + tokens (mf/deref refs/workspace-all-tokens-map) tokens (mf/with-memo [tokens token] @@ -124,8 +123,8 @@ (assoc (:name token) token))) schema - (mf/with-memo [all-token-tree active-tab] - (make-schema all-token-tree active-tab)) + (mf/with-memo [tokens-tree-in-selected-set active-tab] + (make-schema tokens-tree-in-selected-set active-tab)) initial (mf/with-memo [token] @@ -208,11 +207,11 @@ :value (:value valid-token) :description description})) (dwtp/propagate-workspace-tokens) - (modal/hide))))) - (fn [{:keys [errors]}] - (let [error-messages (wte/humanize-errors errors) - error-message (first error-messages)] - (swap! form assoc-in [:extra-errors :value] {:message error-message}))))))] + (modal/hide))) + (fn [{:keys [errors]}] + (let [error-messages (wte/humanize-errors errors) + error-message (first error-messages)] + (swap! form assoc-in [:extra-errors :value] {:message error-message}))))))))] [:> fc/form* {:class (stl/css :form-wrapper) :form form From 5146221513c45142825d45f89f73e10738e1ea29 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 23 Jan 2026 09:50:36 +0100 Subject: [PATCH 12/20] :bug: Fix allow negative spread values on shadow token creation (#8167) * :bug: Fix allow negative spread values on shadow token creation * :tada: Add test --- CHANGES.md | 1 + frontend/playwright/ui/specs/tokens.spec.js | 186 ++++++++++++++++++ .../src/app/main/data/style_dictionary.cljs | 2 +- .../tokens/management/forms/shadow.cljs | 7 +- 4 files changed, 189 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a6e50eb8f1..fe518a5597 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,7 @@ - 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) ## 2.12.1 diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index f8502b5ecb..d7715a7f4c 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -1256,6 +1256,192 @@ test.describe("Tokens: Tokens Tab", () => { ).toBeEnabled(); }); + test("User creates shadow token with negative spread", async ({ page }) => { + const emptyNameError = "Name should be at least 1 character"; + + const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = + await setupEmptyTokensFile(page, {flags: ["enable-token-shadow"]}); + + // Open modal + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + + const addTokenButton = tokensTabPanel.getByRole("button", { + name: `Add Token: Shadow`, + }); + + await addTokenButton.click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + + await expect( + tokensUpdateCreateModal.getByPlaceholder( + "Enter a value or alias with {alias}", + ), + ).toBeVisible(); + + const nameField = tokensUpdateCreateModal.getByLabel("Name"); + const colorField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Color", + }); + const offsetXField = tokensUpdateCreateModal.getByRole("textbox", { + name: "X", + }); + const offsetYField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Y", + }); + const blurField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Blur", + }); + const spreadField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Spread", + }); + const submitButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + + // 1. Check default values + await expect(offsetXField).toHaveValue("4"); + await expect(offsetYField).toHaveValue("4"); + await expect(blurField).toHaveValue("4"); + await expect(spreadField).toHaveValue("0"); + + // 2. Name filled + empty value → disabled + await nameField.fill("my-token"); + await expect(submitButton).toBeDisabled(); + + // 3. Invalid color → disabled + error message + await colorField.fill("1"); + + await expect( + tokensUpdateCreateModal.getByText("Invalid color value: 1"), + ).toBeVisible(); + + await expect(submitButton).toBeDisabled(); + + await colorField.fill("{missing-reference}"); + + await expect( + tokensUpdateCreateModal.getByText( + "Missing token references: missing-reference", + ), + ).toBeVisible(); + + // 4. Empty name → disabled + error message + await nameField.fill(""); + + const emptyNameErrorNode = + tokensUpdateCreateModal.getByText(emptyNameError); + + await expect(emptyNameErrorNode).toBeVisible(); + await expect(submitButton).toBeDisabled(); + + // + // ------- SUCCESSFUL FIELDS ------- + // + + // 5. Valid color → resolved + + await colorField.fill("red"); + await expect( + tokensUpdateCreateModal.getByText("Resolved value: #ff0000"), + ).toBeVisible(); + const colorSwatch = tokensUpdateCreateModal.getByTestId( + "token-form-color-bullet", + ); + await colorSwatch.click(); + const rampSelector = tokensUpdateCreateModal.getByTestId( + "value-saturation-selector", + ); + await expect(rampSelector).toBeVisible(); + await rampSelector.click({ position: { x: 50, y: 50 } }); + + await expect( + tokensUpdateCreateModal.getByText("Resolved value:"), + ).toBeVisible(); + + const sliderOpacity = tokensUpdateCreateModal.getByTestId("slider-opacity"); + await sliderOpacity.click({ position: { x: 50, y: 0 } }); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Color" }), + ).toHaveValue(/rgba\s*\([^)]*\)/); + + // 6. Valid offset → resolved + await offsetXField.fill("3 + 3"); + + await expect( + tokensUpdateCreateModal.getByText("Resolved value: 6"), + ).toBeVisible(); + + await offsetYField.fill("3 + 7"); + + await expect( + tokensUpdateCreateModal.getByText("Resolved value: 10"), + ).toBeVisible(); + + // 7. Valid blur → resolved + + await blurField.fill("3 + 1"); + await expect( + tokensUpdateCreateModal.getByText("Resolved value: 4"), + ).toBeVisible(); + + // 8. Valid spread → resolved + + await spreadField.fill("3 - 3"); + await expect( + tokensUpdateCreateModal.getByText("Resolved value: 0"), + ).toBeVisible(); + + await spreadField.fill("1 - 3"); + await expect( + tokensUpdateCreateModal.getByText("Resolved value: -2"), + ).toBeVisible(); + + await nameField.fill("my-token"); + await expect(submitButton).toBeEnabled(); + await submitButton.click(); + + await expect( + tokensTabPanel.getByRole("button", { name: "my-token" }), + ).toBeEnabled(); + + // + // ------- SECOND TOKEN WITH VALID REFERENCE ------- + // + await addTokenButton.click(); + + await nameField.fill("my-token-2"); + const referenceToggle = + tokensUpdateCreateModal.getByTestId("reference-opt"); + const compositeToggle = + tokensUpdateCreateModal.getByTestId("composite-opt"); + await referenceToggle.click(); + + const referenceInput = tokensUpdateCreateModal.getByPlaceholder( + "Enter a token shadow alias", + ); + await expect(referenceInput).toBeVisible(); + + await compositeToggle.click(); + await expect(colorField).toBeVisible(); + + await referenceToggle.click(); + const referenceField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Reference", + }); + await referenceField.fill("{my-token}"); + await expect( + tokensUpdateCreateModal.getByText( + "Resolved value: - X: 6 - Y: 10 - Blur: 4 - Spread: -2", + ), + ).toBeVisible(); + + await expect(submitButton).toBeEnabled(); + await submitButton.click(); + await expect( + tokensTabPanel.getByRole("button", { name: "my-token-2" }), + ).toBeEnabled(); + }); + test("User creates typography token", async ({ page }) => { const emptyNameError = "Name should be at least 1 character"; const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 53405e3073..0ed5fd68d9 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -364,7 +364,7 @@ "Parses shadow spread value (non-negative number)." [value] (let [parsed (parse-sd-token-general-value value) - valid? (and (:value parsed) (>= (:value parsed) 0))] + valid? (:value parsed)] (cond valid? parsed diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs index 53c6d8e402..c569c81e30 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs @@ -282,12 +282,7 @@ (let [n (d/parse-double blur)] (or (nil? n) (not (< n 0)))))]]] [:spread {:optional true} - [:and - [:maybe :string] - [:fn {:error/fn #(tr "workspace.tokens.shadow-token-spread-value-error")} - (fn [spread] - (let [n (d/parse-double spread)] - (or (nil? n) (not (< n 0)))))]]] + [:maybe :string]] [:color {:optional true} [:maybe :string]] [:color-result {:optional true} ::sm/any] [:inset {:optional true} [:maybe :boolean]]]]] From 9c9b672e3edd1d6705c7c84c149c924e3d3aa1e0 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 23 Jan 2026 10:05:20 +0100 Subject: [PATCH 13/20] :bug: Fix spanish translations on import export token modal (#8172) --- CHANGES.md | 1 + frontend/translations/es.po | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index fe518a5597..10aca874cd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,7 @@ - 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) ## 2.12.1 diff --git a/frontend/translations/es.po b/frontend/translations/es.po index d69baf6480..40fd3fd30f 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7594,6 +7594,10 @@ msgstr "Error al importar: No se pudo procesar el JSON." msgid "workspace.tokens.export" msgstr "Exportar" +#: src/app/main/ui/workspace/tokens/export/modal.cljs:125 +msgid "workspace.tokens.export-tokens" +msgstr "Exportar tokens" + #: src/app/main/ui/workspace/tokens/export/modal.cljs:118 msgid "workspace.tokens.export.multiple-files" msgstr "Múltiples ficheros" @@ -7638,10 +7642,26 @@ msgstr "Nombre del grupo" msgid "workspace.tokens.grouping-set-alert" msgstr "La agrupación de sets aun no está soportada." +#: src/app/main/ui/workspace/tokens/import/modal.cljs:233 +msgid "workspace.tokens.import-button-prefix" +msgstr "Importar %s" + #: src/app/main/data/workspace/tokens/errors.cljs:32, src/app/main/data/workspace/tokens/errors.cljs:37 msgid "workspace.tokens.import-error" msgstr "Error al importar:" +#: src/app/main/ui/workspace/tokens/import/modal.cljs:273 +msgid "workspace.tokens.import-menu-folder-option" +msgstr "Carpeta" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:271 +msgid "workspace.tokens.import-menu-json-option" +msgstr "Archivo JSON único" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:272 +msgid "workspace.tokens.import-menu-zip-option" +msgstr "Archivo ZIP" + #: src/app/main/ui/workspace/tokens/import/modal.cljs:241 msgid "workspace.tokens.import-multiple-files" msgstr "" @@ -7656,7 +7676,7 @@ msgstr "" #: src/app/main/ui/workspace/tokens/import/modal.cljs:237 msgid "workspace.tokens.import-tokens" -msgstr "Import tokens" +msgstr "Importar tokens" #: src/app/main/ui/workspace/tokens/sidebar.cljs:414, src/app/main/ui/workspace/tokens/sidebar.cljs:415 #, unused From 15d369493b0371430eeb360be0ed140edddd8bbe Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Fri, 23 Jan 2026 12:48:01 +0100 Subject: [PATCH 14/20] :bug: Fix problem with z-index modal in dashboard (#8178) --- frontend/src/app/main/ui/dashboard/team.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index 90e33f5cca..259fdeb565 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -628,6 +628,7 @@ width: $sz-400; padding: var(--sp-xxxl); background-color: var(--color-background-primary); + z-index: var(--z-index-set); &.hero { top: px2rem(216); From 5d7e6afd762bd8847eb201dd80446f52f99cf8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 5 Jan 2026 09:13:14 +0100 Subject: [PATCH 15/20] :wrench: Fix a typo in an interpolation --- .github/workflows/build-tag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-tag.yml b/.github/workflows/build-tag.yml index 80ef7bcaeb..c32e363888 100644 --- a/.github/workflows/build-tag.yml +++ b/.github/workflows/build-tag.yml @@ -33,7 +33,7 @@ jobs: MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }} MATTERMOST_CHANNEL: bot-alerts-cicd TEXT: | - 🐳 *[PENPOT] Docker image available: {{ github.ref_name }}* + 🐳 *[PENPOT] Docker image available: ${{ github.ref_name }}* 🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} @infra From e03ad251187d363e0a97b8bdd96b4db1a4073c8c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 26 Jan 2026 10:10:57 +0100 Subject: [PATCH 16/20] :wrench: Backport CI workflow from develop --- .github/workflows/tests.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d1e008d21..c4e715e55a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,7 @@ concurrency: jobs: lint: name: "Linter" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -34,7 +34,7 @@ jobs: test-common: name: "Common Tests" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -53,7 +53,8 @@ jobs: test-plugins: name: Plugins Runtime Linter & Tests - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 + container: penpotapp/devenv:latest steps: - uses: actions/checkout@v4 @@ -98,7 +99,7 @@ jobs: test-frontend: name: "Frontend Tests" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -119,7 +120,7 @@ jobs: test-render-wasm: name: "Render WASM Tests" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -143,7 +144,7 @@ jobs: test-backend: name: "Backend Tests" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest services: @@ -182,7 +183,7 @@ jobs: test-library: name: "Library Tests" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -196,7 +197,7 @@ jobs: build-integration: name: "Build Integration Bundle" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest steps: @@ -217,7 +218,7 @@ jobs: test-integration-1: name: "Integration Tests 1/4" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest needs: build-integration @@ -247,7 +248,7 @@ jobs: test-integration-2: name: "Integration Tests 2/4" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest needs: build-integration @@ -277,7 +278,7 @@ jobs: test-integration-3: name: "Integration Tests 3/4" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest needs: build-integration @@ -307,7 +308,7 @@ jobs: test-integration-4: name: "Integration Tests 4/4" - runs-on: ubuntu-24.04 + runs-on: penpot-runner-02 container: penpotapp/devenv:latest needs: build-integration From 33e650242ca2d8d7a9588f25dd9598273c12e3e8 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Jan 2026 13:47:09 +0100 Subject: [PATCH 17/20] :sparkles: Add slugify to the filename on assets exportation Fixes https://github.com/penpot/penpot/issues/8017 --- CHANGES.md | 1 + exporter/src/app/handlers/resources.cljs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 10aca874cd..77f4dd262b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ - 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) ## 2.12.1 diff --git a/exporter/src/app/handlers/resources.cljs b/exporter/src/app/handlers/resources.cljs index 8b0a55ba35..5394e673a8 100644 --- a/exporter/src/app/handlers/resources.cljs +++ b/exporter/src/app/handlers/resources.cljs @@ -36,7 +36,7 @@ {:path path :mtype (mime/get type) :name name - :filename (str/concat name (mime/get-extension type)) + :filename (str/concat (str/slug name) (mime/get-extension type)) :id task-id})) (defn create-zip From 8632b18eecbfbcf967759c403c7988d6a74b90c2 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Jan 2026 15:22:01 +0100 Subject: [PATCH 18/20] :bug: 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). --- CHANGES.md | 1 + backend/src/app/rpc/commands/fonts.clj | 35 +++++++++++++++++++++++--- frontend/src/app/main/data/fonts.cljs | 20 ++++++++++++--- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 77f4dd262b..6754a5045a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,7 @@ - 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) ## 2.12.1 diff --git a/backend/src/app/rpc/commands/fonts.clj b/backend/src/app/rpc/commands/fonts.clj index 05454d6698..8ca20eac49 100644 --- a/backend/src/app/rpc/commands/fonts.clj +++ b/backend/src/app/rpc/commands/fonts.clj @@ -27,7 +27,17 @@ [app.rpc.helpers :as rph] [app.rpc.quotes :as quotes] [app.storage :as sto] - [app.util.services :as sv])) + [app.storage.tmp :as tmp] + [app.util.services :as sv] + [datoteka.io :as io]) + (:import + java.io.InputStream + java.io.OutputStream + java.io.SequenceInputStream + java.util.Collections)) + +(set! *warn-on-reflection* true) + (def valid-weight #{100 200 300 400 500 600 700 800 900 950}) (def valid-style #{"normal" "italic"}) @@ -105,7 +115,7 @@ (defn create-font-variant [{:keys [::sto/storage ::db/conn]} {:keys [data] :as params}] - (letfn [(generate-missing! [data] + (letfn [(generate-missing [data] (let [data (media/run {:cmd :generate-fonts :input data})] (when (and (not (contains? data "font/otf")) (not (contains? data "font/ttf")) @@ -116,8 +126,26 @@ :hint "invalid font upload, unable to generate missing font assets")) data)) + (process-chunks [chunks] + (let [tmp (tmp/tempfile :prefix "penpot.tempfont." :suffix "") + streams (map io/input-stream chunks) + streams (Collections/enumeration streams)] + (with-open [^OutputStream output (io/output-stream tmp) + ^InputStream input (SequenceInputStream. streams)] + (io/copy input output)) + tmp)) + + (join-chunks [data] + (reduce-kv (fn [data mtype content] + (if (vector? content) + (assoc data mtype (process-chunks content)) + data)) + data + data)) + (prepare-font [data mtype] (when-let [resource (get data mtype)] + (let [hash (sto/calculate-hash resource) content (-> (sto/content resource) (sto/wrap-with-hash hash))] @@ -156,7 +184,8 @@ :otf-file-id (:id otf) :ttf-file-id (:id ttf)}))] - (let [data (generate-missing! data) + (let [data (join-chunks data) + data (generate-missing data) assets (persist-fonts-files! data) result (insert-font-variant! assets)] (vary-meta result assoc ::audit/replace-props (update params :data (comp vec keys)))))) diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index 4706e7c5c3..4efa40718f 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -24,6 +24,20 @@ [cuerdas.core :as str] [potok.v2.core :as ptk])) +(def ^:const default-chunk-size + (* 1024 1024 4)) ;; 4MiB + +(defn- chunk-array + [data chunk-size] + (let [total-size (alength data)] + (loop [offset 0 + chunks []] + (if (< offset total-size) + (let [end (min (+ offset chunk-size) total-size) + chunk (.subarray ^js data offset end)] + (recur end (conj chunks chunk))) + chunks)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General purpose events & IMPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -116,9 +130,9 @@ (not= hhea-descender win-descent) (and f-selection (or (not= hhea-ascender os2-ascent) - (not= hhea-descender os2-descent))))] - - {:content {:data (js/Uint8Array. data) + (not= hhea-descender os2-descent)))) + data (js/Uint8Array. data)] + {:content {:data (chunk-array data default-chunk-size) :name name :type type} :font-family (or family "") From 23d5fc7408848b7a9974809a5810c8e8432ca0da Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 21 Jan 2026 09:25:22 +0100 Subject: [PATCH 19/20] :bug: Prevent exception on open-new-window when no window is returned Fixes https://github.com/penpot/penpot/issues/7787 --- CHANGES.md | 1 + frontend/src/app/util/dom.cljs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6754a5045a..c3b176e71e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -34,6 +34,7 @@ - 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) ## 2.12.1 diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 2faa6cc1c0..e22de0dbba 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -802,9 +802,10 @@ ([uri name] (open-new-window uri name "noopener,noreferrer")) ([uri name features] - (let [new-window (.open js/window (str uri) name features)] + (when-let [new-window (.open js/window (str uri) name features)] (when (not= name "_blank") - (.reload (.-location new-window)))))) + (when-let [location (.-location new-window)] + (.reload location)))))) (defn browser-back [] From f07495ae955d53de07b1025a029dfaf4608cf262 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 21 Jan 2026 09:36:14 +0100 Subject: [PATCH 20/20] :bug: Fix incorrect handling of numeric values layout padding and gap Fixes https://github.com/penpot/penpot/issues/8113 --- CHANGES.md | 2 ++ .../workspace/sidebar/options/menus/layout_container.cljs | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c3b176e71e..b175d03cfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -35,6 +35,8 @@ - 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) + ## 2.12.1 diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 320abd7d18..e89033964d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -375,7 +375,7 @@ (mf/use-fn (mf/deps on-change ids) (fn [value attr event] - (if (or (string? value) (int? value)) + (if (or (string? value) (number? value)) (on-change :simple attr value event) (do (let [resolved-value (:resolved-value (first value)) @@ -489,7 +489,7 @@ (mf/use-fn (mf/deps on-change ids) (fn [value attr event] - (if (or (string? value) (int? value)) + (if (or (string? value) (number? value)) (on-change :multiple attr value event) (do (let [resolved-value (:resolved-value (first value))] @@ -724,7 +724,7 @@ (mf/use-fn (mf/deps on-change wrap-type ids) (fn [value event attr] - (if (or (string? value) (int? value)) + (if (or (string? value) (number? value)) (on-change (= "nowrap" wrap-type) attr value event) (do (let [resolved-value (:resolved-value (first value))]