Compare commits

...

15 Commits

Author SHA1 Message Date
Eva Marco
b867e08b00 🐛 Fix error when creating a token with an invalid name 2026-01-27 18:09:57 +01:00
Eva Marco
9ca76c745f 🐛 Fix app freeze on token name change (#8214) 2026-01-27 17:31:50 +01:00
Andrey Antukh
3112b240a0 📎 Add missing entry on changelog 2026-01-27 09:28:41 +01:00
Andrey Antukh
56fd66b91a 🐛 Fix several issues related to path edition (#8187)
*  Improve save-path-content event consistency

Mainly removing possible race conditions from the event
implementation.

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

*  Reuse already available shape-id on split-segments
2026-01-27 09:27:42 +01:00
David Barragán Merino
1ce0b60e3d 🔧 Run all the jobs if the workflow is launched manually 2026-01-26 17:13:54 +01:00
David Barragán Merino
ef80901400 🔧 Enable secret inheritance 2026-01-26 14:00:55 +01:00
David Barragán Merino
5306bed548 🔧 Define deploy plugin packages workflows 2026-01-26 13:47:57 +01:00
David Barragán Merino
92a319ddd1 🔧 Rename wrangle to wrangler 2026-01-26 13:47:57 +01:00
David Barragán Merino
68a6d4c9a8 🔧 Add deploy plugin packages workflow placeholder and wrangle config files 2026-01-26 13:47:57 +01:00
Andrey Antukh
f07495ae95 🐛 Fix incorrect handling of numeric values layout padding and gap
Fixes https://github.com/penpot/penpot/issues/8113
2026-01-26 11:01:25 +01:00
Andrey Antukh
23d5fc7408 🐛 Prevent exception on open-new-window when no window is returned
Fixes https://github.com/penpot/penpot/issues/7787
2026-01-26 11:01:25 +01:00
Andrey Antukh
8632b18eec 🐛 Avoid json decoder liner limit exception by chunking
Happens only when we send large binary data serialized with transit
(mainly used for upload fonts data).
2026-01-26 11:01:25 +01:00
Andrey Antukh
33e650242c Add slugify to the filename on assets exportation
Fixes https://github.com/penpot/penpot/issues/8017
2026-01-26 11:01:25 +01:00
Andrey Antukh
e03ad25118 🔧 Backport CI workflow from develop 2026-01-26 10:18:24 +01:00
David Barragán Merino
5d7e6afd76 🔧 Fix a typo in an interpolation 2026-01-23 19:52:20 +01:00
25 changed files with 478 additions and 71 deletions

View File

@@ -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

View File

@@ -11,7 +11,7 @@ on:
- "plugins/libs/plugin-types/REAME.md"
- "plugins/tools/typedoc.css"
- "plugins/CHANGELOG.md"
- "plugins/wrangle-penpot-plugins-api-doc.toml"
- "plugins/wrangler-penpot-plugins-api-doc.toml"
workflow_dispatch:
inputs:
gh_ref:
@@ -98,4 +98,16 @@ jobs:
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 }}
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

View File

@@ -0,0 +1,127 @@
name: Plugins/package deployer
on:
# Deploy package from manual action
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
plugin_name:
description: 'Pluging name (like plugins/apps/<plugin_name>-plugin)'
type: string
required: true
workflow_call:
inputs:
gh_ref:
description: 'Name of the branch'
type: string
required: true
default: 'develop'
plugin_name:
description: 'Publig name (from plugins/apps/<plugin_name>-plugin)'
type: string
required: true
permissions:
contents: read
jobs:
deploy:
runs-on: penpot-runner-01
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: "Build package for ${{ inputs.plugin_name }}-plugin"
working-directory: plugins
shell: bash
run: npx nx build ${{ inputs.plugin_name }}-plugin
- name: Select Worker name
run: |
REF="${{ inputs.gh_ref }}"
case "$REF" in
main)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pro" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.app" >> $GITHUB_ENV ;;
staging)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-pre" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.penpot.dev" >> $GITHUB_ENV ;;
develop)
echo "WORKER_NAME=${{ inputs.plugin_name }}-plugin-hourly" >> $GITHUB_ENV
echo "WORKER_URI=${{ inputs.plugin_name }}.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Set the custom url
working-directory: plugins
shell: bash
run: |
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" apps/${{ inputs.plugin_name }}-plugin/wrangler.toml
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config apps/${{ inputs.plugin_name }}-plugin/wrangler.toml --name ${{ env.WORKER_NAME }}
- name: Notify Mattermost
if: failure()
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
❌ 🧩📦 *[PENPOT PLUGINS] Error deploying ${{ env.WORKER_NAME }}.*
📄 Triggered from ref: `${{ inputs.gh_ref }}`
Plugin name: `${{ inputs.plugin_name }}-plugin`
Cloudflare worker name: `${{ env.WORKER_NAME }}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra

View File

@@ -0,0 +1,143 @@
name: Plugins/packages deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- 'plugins/apps/*-plugin/**'
- 'libs/plugins-styles/**'
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
colors_to_tokens: ${{ steps.filter.outputs.colors_to_tokens }}
create_palette: ${{ steps.filter.outputs.create_palette }}
lorem_ipsum: ${{ steps.filter.outputs.lorem_ipsum }}
rename_layers: ${{ steps.filter.outputs.rename_layers }}
contrast: ${{ steps.filter.outputs.contrast }}
icons: ${{ steps.filter.outputs.icons }}
poc_state: ${{ steps.filter.outputs.poc_state }}
table: ${{ steps.filter.outputs.table }}
# [For new plugins]
# Add more outputs here
steps:
- uses: actions/checkout@v4
- id: filter
uses: dorny/paths-filter@v3
with:
filters: |
colors_to_tokens:
- 'plugins/apps/colors-to-tokens-plugin/**'
- 'libs/plugins-styles/**'
contrast:
- 'plugins/apps/contrast-plugin/**'
- 'libs/plugins-styles/**'
create_palette:
- 'plugins/apps/create-palette-plugin/**'
- 'libs/plugins-styles/**'
icons:
- 'plugins/apps/icons-plugin/**'
- 'libs/plugins-styles/**'
lorem_ipsum:
- 'plugins/apps/lorem-ipsum-plugin/**'
- 'libs/plugins-styles/**'
rename_layers:
- 'plugins/apps/rename-layers-plugin/**'
- 'libs/plugins-styles/**'
table:
- 'plugins/apps/table-plugin/**'
- 'libs/plugins-styles/**'
# [For new plugins]
# Add more plugin filters here
# another_plugin:
# - 'plugins/apps/another-plugin/**'
# - 'libs/plugins-styles/**'
colors-to-tokens-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.colors_to_tokens == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: colors-to-tokens
contrast-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.contrast == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: contrast
create-palette-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.create_palette == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: create-palette
icons-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.icons == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: icons
lorem-ipsum-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.lorem_ipsum == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: lorem-ipsum
rename-layers-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.rename_layers == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: rename-layers
table-plugin:
needs: detect-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.table == 'true'
uses: ./.github/workflows/plugins-deploy-package.yml
secrets: inherit
with:
gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
plugin_name: table
# [For new plugins]
# Add more jobs for other plugins below, following the same pattern
# another-plugin:
# needs: detect-changes
# if: github.event_name == 'workflow_dispatch' || needs.detect-changes.outputs.another_plugin == 'true'
# uses: ./.github/workflows/plugins-deploy-package.yml
# secrets: inherit
# with:
# gh_ref: "${{ inputs.gh_ref || github.ref_name }}"
# plugin_name: another

View File

@@ -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

View File

@@ -32,6 +32,13 @@
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
- Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167)
- Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171)
- Remove whitespaces from asset export filename [Github #8133](https://github.com/penpot/penpot/pull/8133)
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
- Fix error when creating a token with an invalid name [Taiga #13219](https://tree.taiga.io/project/penpot/issue/13219)
## 2.12.1

View File

@@ -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))))))

View File

@@ -99,7 +99,7 @@
(def token-name-ref
[:re {:title "TokenNameRef" :gen/gen sg/text}
#"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"])
#"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$"])
(def ^:private schema:color
[:map

View File

@@ -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

View File

@@ -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 "")

View File

@@ -70,20 +70,22 @@
(= (-> content last :command) :move-to))
(into [] (take (dec (count content)) content))
content)]
(-> state
(st/set-content content))))
(st/set-content state content)))
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
objects (dsh/lookup-page-objects state page-id)
id (dm/get-in state [:workspace-local :edition])
old-content (dm/get-in state [:workspace-local :edit-path id :old-content])
shape (st/get-path state)]
local (get state :workspace-local)
id (get local :edition)
objects (dsh/lookup-page-objects state page-id)]
(if (and (some? old-content) (some? (:id shape)))
(let [changes (generate-path-changes it objects page-id shape old-content (:content shape))]
(rx/of (dch/commit-changes changes)))
(rx/empty)))))))
;; NOTE: we proceed only if the shape is present on the
;; objects, if shape is a ephimeral drawing shape, we should
;; do nothing
(when-let [shape (get objects id)]
(when-let [old-content (dm/get-in local [:edit-path id :old-content])]
(let [new-content (get shape :content)
changes (generate-path-changes it objects page-id shape old-content new-content)]
(rx/of (dch/commit-changes changes))))))))))

View File

@@ -8,7 +8,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.types.path :as path]
[app.common.types.path.helpers :as path.helpers]
@@ -289,34 +288,34 @@
(declare stop-path-edit)
(defn start-path-edit
[id]
(ptk/reify ::start-path-edit
ptk/UpdateEvent
(update [_ state]
(let [objects (dsh/lookup-page-objects state)
edit-path (dm/get-in state [:workspace-local :edit-path id])
content (st/get-path state :content)
state (cond-> state
(cfh/path-shape? objects id)
(st/set-content (path/close-subpaths content)))]
shape (get objects id)]
(cond-> state
(or (not edit-path)
(= :draw (:edit-mode edit-path)))
(assoc-in [:workspace-local :edit-path id] {:edit-mode :move
:selected #{}
:snap-toggled false})
(and (some? edit-path)
(= :move (:edit-mode edit-path)))
(assoc-in [:workspace-local :edit-path id :edit-mode] :draw))))
(-> state
(st/set-content (path/close-subpaths (:content shape)))
(update-in [:workspace-local :edit-path id]
(fn [state]
(let [state (if state
(if (= :move (:edit-mode state))
(assoc state :edit-mode :draw)
state)
{:edit-mode :move
:selected #{}
:snap-toggled false})]
(assoc state :old-content (:content shape))))))))
ptk/WatchEvent
(watch [_ _ stream]
(let [stopper (->> stream
(rx/filter #(let [type (ptk/type %)]
(= type ::dwe/clear-edition-mode)
(= type ::start-path-edit))))]
(let [stopper (rx/filter #(let [type (ptk/type %)]
(= type ::dwe/clear-edition-mode)
(= type ::start-path-edit))
stream)]
(rx/concat
(rx/of (undo/start-path-undo))
(->> stream
@@ -325,7 +324,8 @@
(rx/map #(stop-path-edit id))
(rx/take-until stopper)))))))
(defn stop-path-edit [id]
(defn stop-path-edit
[id]
(ptk/reify ::stop-path-edit
ptk/UpdateEvent
(update [_ state]
@@ -335,13 +335,12 @@
(watch [_ _ _]
(rx/of (ptk/data-event :layout/update {:ids [id]})))))
(defn split-segments
[{:keys [from-p to-p t]}]
(defn- split-segments
[id {:keys [from-p to-p t]}]
(ptk/reify ::split-segments
ptk/UpdateEvent
(update [_ state]
(let [id (st/get-path-id state)
content (st/get-path state :content)]
(let [content (st/get-path state :content)]
(-> state
(assoc-in [:workspace-local :edit-path id :old-content] content)
(st/set-content (-> content
@@ -353,10 +352,10 @@
(rx/of (changes/save-path-content {:preserve-move-to true})))))
(defn create-node-at-position
[event]
[params]
(ptk/reify ::create-node-at-position
ptk/WatchEvent
(watch [_ state _]
(let [id (st/get-path-id state)]
(rx/of (dwsh/update-shapes [id] path/convert-to-path)
(split-segments event))))))
(split-segments id params))))))

View File

@@ -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))]

View File

@@ -11,6 +11,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.color :as cl]
[app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob]
[app.main.data.style-dictionary :as sd]
[app.main.data.tinycolor :as tinycolor]
@@ -26,6 +27,7 @@
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[malli.core :as m]
[rumext.v2 :as mf]))
;; --- Color Inputs -------------------------------------------------------------
@@ -51,12 +53,15 @@
;; Both variants provide identical color-picker and text-input behavior, but
;; differ in how they persist the value within the forms nested structure.
(defn- resolve-value
[tokens prev-token token-name value]
(let [token
(let [valid-token-name?
(and (string? token-name)
(m/validate cto/token-name-ref token-name))
token
{:value value
:name (if (str/blank? token-name)
:name (if (or (not valid-token-name?) (str/blank? token-name))
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}

View File

@@ -22,6 +22,7 @@
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[malli.core :as m]
[rumext.v2 :as mf]))
;; --- Font Picker Inputs -------------------------------------------------------
@@ -50,9 +51,13 @@
(defn- resolve-value
[tokens prev-token token-name value]
(let [token
(let [valid-token-name?
(and (string? token-name)
(m/validate cto/token-name-ref token-name))
token
{:value (cto/split-font-family value)
:name (if (str/blank? token-name)
:name (if (or (not valid-token-name?) (str/blank? token-name))
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.files.tokens :as cft]
[app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob]
[app.main.data.style-dictionary :as sd]
[app.main.data.workspace.tokens.format :as dwtf]
@@ -18,6 +19,7 @@
[app.util.i18n :refer [tr]]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[malli.core :as m]
[rumext.v2 :as mf]))
;; -----------------------------------------------------------------------------
@@ -140,9 +142,13 @@
(defn- resolve-value
[tokens prev-token token-name value]
(let [token
(let [valid-token-name?
(and (string? token-name)
(m/validate cto/token-name-ref token-name))
token
{:value value
:name (if (str/blank? token-name)
:name (if (or (not valid-token-name?) (str/blank? token-name))
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}
tokens

View File

@@ -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
[]

View File

@@ -0,0 +1,8 @@
name = "color-to-tokens-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/colors-to-tokens-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "contrast-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/contrast-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "create-palette-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/create-palette-plugin" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "icons-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/icons-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "lorem-ipsum-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/lorem-ipsum-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "rename-layers-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/rename-layers-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true

View File

@@ -0,0 +1,8 @@
name = "table-plugin"
compatibility_date = "2025-01-01"
assets = { directory = "../../dist/apps/table-plugin/browser" }
[[routes]]
pattern = "WORKER_URI"
custom_domain = true