diff --git a/.github/workflows/plugins-deploy-api-doc.yml b/.github/workflows/plugins-deploy-api-doc.yml index f8451e9816..62a87745bb 100644 --- a/.github/workflows/plugins-deploy-api-doc.yml +++ b/.github/workflows/plugins-deploy-api-doc.yml @@ -11,7 +11,7 @@ on: - "plugins/libs/plugin-types/REAME.md" - "plugins/tools/typedoc.css" - "plugins/CHANGELOG.md" - - "plugins/wrangler-penpot-plugins-api-doc.toml" + - "plugins/wrangle-penpot-plugins-api-doc.toml" workflow_dispatch: inputs: gh_ref: @@ -98,16 +98,4 @@ jobs: 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 + command: deploy --config wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }} 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 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 diff --git a/CHANGES.md b/CHANGES.md index f6d861db12..b175d03cfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -29,6 +29,13 @@ - 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) ## 2.12.1 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/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/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 diff --git a/frontend/package.json b/frontend/package.json index 8279b3fd3b..f4c36c6ef6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,13 +48,13 @@ "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": { "@penpot/draft-js": "portal:./packages/draft-js", "@penpot/mousetrap": "portal:./packages/mousetrap", - "@penpot/plugins-runtime": "1.3.2", + "@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/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/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(); 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 "") diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 1bf2de0d0a..0ed5fd68d9 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)]} @@ -365,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/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/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 85b3dc2ac8..dfecbc779b 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -193,16 +193,15 @@ 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 (fn [] - (prn files) (st/emit! (modal/show {:type :confirm :title (tr "dashboard-restore-file-confirmation.title") @@ -214,7 +213,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 +243,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" 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); 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/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))] 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..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? @@ -140,6 +143,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,15 +253,20 @@ :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] + (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] @@ -301,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]) @@ -414,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 cc9cb79426..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] @@ -236,12 +239,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))))))) @@ -250,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]) @@ -257,6 +263,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 +293,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) @@ -293,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] @@ -360,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]) @@ -405,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/generic_form.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs index 038dbeae03..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 @@ -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] @@ -110,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] @@ -207,7 +207,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 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..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,15 +282,11 @@ (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]]]]] + (if (= active-tab :reference) [:reference {:optional false} ::sm/text] [:reference {:optional true} [:maybe :string]])]] 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 [] diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 2101493c92..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 @@ -7757,7 +7777,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" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7b6b7718c1..ebc1ed2427 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.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.3.2": - version: 1.3.2 - resolution: "@penpot/plugins-runtime@npm:1.3.2" +"@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.3.2" + "@penpot/plugin-types": "npm:^1.4.2" ses: "npm:^1.1.0" zod: "npm:^3.22.4" - checksum: 10c0/b6d2cb3a57bcbe58232db52b8224d1817495e96b34997bfa72421629b5f34a8c9cc71357c315dcab9d52ea036ed632a5efe0ac50f52e730901c02d498dfa1313 + 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.3.2" + "@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 67b0b183fa..e15c253923 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -1,3 +1,38 @@ +## 1.4.2 (2026-01-21) + +- **plugin-types:** fix atob/btoa functions + +## 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..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.3.2", + "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 2cc3ae3055..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.3.2", + "version": "1.4.2", "dependencies": { - "@penpot/plugin-types": "^1.3.2", + "@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 511c3f7f86..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.3.2", + "version": "1.4.2", "dependencies": {} } diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml index d1dd76b579..b1cdc2d9bc 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.2 + 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, 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" }