Compare commits

..

18 Commits

Author SHA1 Message Date
Elena Torró
8a2d03a28a Merge pull request #8040 from penpot/ladybenko-12856-render-loader
🎉 Make workspace loader to wait for first render
2026-01-09 11:56:48 +01:00
Belén Albeza
c27a16e31a 🎉 Make workspace loader to wait for first render 2026-01-08 16:09:20 +01:00
Andrey Antukh
1c237a0968 Merge branch 'staging' into staging-render 2026-01-08 13:58:48 +01:00
Andrey Antukh
b0dc7d6ffb 🔧 Change default jmx port on deps.edn 2026-01-08 13:56:22 +01:00
Elena Torró
b7eaeffa88 Merge pull request #8024 from penpot/azazeln28-issue-12835-fix-previous-styles-lost
🐛 Fix previous styles lost when changing selected text
2026-01-08 13:49:06 +01:00
Andrey Antukh
b7cd315872 🐛 Fix wasm-playground on devenv 2026-01-08 13:48:09 +01:00
Eva Marco
743d4e5c8d 🐛 Fix error on shadow token creation (#8029) 2026-01-08 13:26:01 +01:00
Belén Albeza
fb9560c315 🐛 Fix guides dropdown width (#8031)
* 🐛 Fix width of guides column dropdown

* ♻️ Remove deprecated tokens in css

* 🔧 Update changelog
2026-01-08 10:47:11 +01:00
Alejandro Alonso
d53c090900 Merge pull request #8028 from penpot/elenatorro-12956-fix-text-color-tokens
🐛 Fix missing text color token from selected shapes in selected colors list
2026-01-07 16:49:41 +01:00
Elena Torro
621e030095 🐛 Fix missing text color token from selected shapes in selected colors list 2026-01-07 16:41:25 +01:00
Alejandro Alonso
157e4aa2d0 Merge pull request #8025 from penpot/elenatorro-12951-fix-inner-text-shadow-token
🐛 Fix inner shadow selector on shadow token
2026-01-07 16:37:19 +01:00
Elena Torro
7cd2308f3b 🐛 Fix inner shadow selector on shadow token 2026-01-07 16:36:51 +01:00
Alejandro Alonso
c315a15b48 Merge pull request #8026 from penpot/elenatorro-12997-fix-clojure-on-css-box-shadow
🐛 Fix CSS generated box-shadow property
2026-01-07 16:32:12 +01:00
Elena Torro
8a3e6d026e 🐛 Fix CSS generated box-shadow property 2026-01-07 16:28:05 +01:00
Florian Schrödl
0dd062d011 🐛 Fix line-height throwing for int (#7927) 2026-01-07 16:13:10 +01:00
Alejandro Alonso
bfbb546699 Merge pull request #8027 from penpot/superalex-fix-colors-assets-from-shared-libraries
🐛 Fix color assets from shared libraries
2026-01-07 14:16:57 +01:00
Alejandro Alonso
083e77e9c5 🐛 Fix color assets from shared libraries 2026-01-07 14:02:28 +01:00
Aitor Moreno
7819e6c440 🐛 Fix previous styles lost when changing selected text 2026-01-07 12:41:39 +01:00
25 changed files with 250 additions and 229 deletions

View File

@@ -23,6 +23,12 @@
- Fix wrong board size presets in Android [Taiga #12339](https://tree.taiga.io/project/penpot/issue/12339)
- Fix problem with grid layout components and auto sizing [Github #7797](https://github.com/penpot/penpot/issues/7797)
- Fix some alignments on inspect tab [Taiga #12915](https://tree.taiga.io/project/penpot/issue/12915)
- Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835)
- Fix color assets from shared libraries not appearing as assets in Selected colors panel [Taiga #12957](https://tree.taiga.io/project/penpot/issue/12957)
- Fix CSS generated box-shadow property [Taiga #12997](https://tree.taiga.io/project/penpot/issue/12997)
- Fix inner shadow selector on shadow token [Taiga #12951](https://tree.taiga.io/project/penpot/issue/12951)
- Fix missing text color token from selected shapes in selected colors list [Taiga #12956](https://tree.taiga.io/project/penpot/issue/12956)
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
## 2.12.1

View File

@@ -97,8 +97,8 @@
:jmx-remote
{:jvm-opts ["-Dcom.sun.management.jmxremote"
"-Dcom.sun.management.jmxremote.port=9090"
"-Dcom.sun.management.jmxremote.rmi.port=9090"
"-Dcom.sun.management.jmxremote.port=9000"
"-Dcom.sun.management.jmxremote.rmi.port=9000"
"-Dcom.sun.management.jmxremote.local.only=false"
"-Dcom.sun.management.jmxremote.authenticate=false"
"-Dcom.sun.management.jmxremote.ssl=false"

View File

@@ -41,7 +41,10 @@ services:
- 6062:6062
- 6063:6063
- 6064:6064
- 9000:9000
- 9001:9001
- 9090:9090
- 9091:9091
environment:
- EXTERNAL_UID=${CURRENT_USER_ID}

View File

@@ -145,8 +145,8 @@ http {
proxy_pass http://127.0.0.1:3000/;
}
location /playground {
alias /home/penpot/penpot/experiments/;
location /wasm-playground {
alias /home/penpot/penpot/frontend/resources/public/wasm-playground/;
add_header Cache-Control "no-cache, max-age=0";
autoindex on;
}

View File

@@ -58,8 +58,7 @@
:share-id share-id
:object-id (mapv :id objects)
:route "objects"
:skip-children skip-children
:wasm "true"}
:skip-children skip-children}
uri (-> (cf/get :public-uri)
(assoc :path "/render.html")
(assoc :query (u/map->query-string params)))]

View File

@@ -33,9 +33,7 @@
:page-id page-id
:share-id share-id
:object-id object-id
:route "objects"
;;:wasm "true"
}]
:route "objects"}]
(-> base-uri
(assoc :path "/render.html")
(assoc :query (u/map->query-string params)))))

View File

@@ -667,6 +667,9 @@
}
// UI ELEMENTS
// FIXME: This is used multiple times accross the app. We should design this in
// the DS and create a proper component for it.
.asset-element {
@include bodySmallTypography;
display: flex;

View File

@@ -236,7 +236,7 @@
Uses `font-size-value` to calculate the relative line-height value.
Returns an error for an invalid font-size value."
[line-height-value font-size-value font-size-errors]
(let [missing-references (seq (some cto/find-token-value-references line-height-value))
(let [missing-references (seq (cto/find-token-value-references line-height-value))
error
(cond
missing-references

View File

@@ -1122,7 +1122,7 @@
ref-id (:stroke-color-ref-id stroke)
colors (-> libraries
(get ref-id)
(get ref-file)
(get :data)
(ctl/get-colors))
shared? (contains? colors ref-id)
@@ -1167,7 +1167,7 @@
ref-file (get color :ref-file)
ref-id (get color :ref-id)
colors (-> libraries
(get ref-id)
(get ref-file)
(get :data)
(ctl/get-colors))
shared? (contains? colors ref-id)
@@ -1180,19 +1180,20 @@
:index (:index shadow)}))
(defn- text->color-att
[fill file-id libraries]
[fill file-id libraries & {:keys [has-token-applied token-name]}]
(let [ref-file (:fill-color-ref-file fill)
ref-id (:fill-color-ref-id fill)
colors (-> libraries
(get ref-id)
(get ref-file)
(get :data)
(ctl/get-colors))
shared? (contains? colors ref-id)
attrs (cond-> (types.fills/fill->color fill)
(not (or shared? (= ref-file file-id)))
(dissoc :ref-file :ref-id))]
base-attrs (cond-> (types.fills/fill->color fill)
(not (or shared? (= ref-file file-id)))
(dissoc :ref-file :ref-id))
attrs (cond-> base-attrs
has-token-applied (assoc :has-token-applied true)
token-name (assoc :token-name token-name))]
{:attrs attrs
:prop :content
:shape-id (:shape-id fill)
@@ -1200,13 +1201,18 @@
(defn- extract-text-colors
[text file-id libraries]
(let [treat-node
(let [applied-fill-token (get-in text [:applied-tokens :fill])
treat-node
(fn [node shape-id]
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))]
(map-indexed (fn [idx fill]
(let [args (cond-> []
(and (= idx 0) applied-fill-token)
(conj :has-token-applied true :token-name applied-fill-token))]
(apply text->color-att (assoc fill :shape-id shape-id :index idx) file-id libraries args)))
node))]
(->> (txt/node-seq txt/is-text-node? (:content text))
(map :fills)
(mapcat #(treat-node % (:id text)))
(map #(text->color-att % file-id libraries)))))
(mapcat #(treat-node % (:id text))))))
(defn- fill->color-att
"Given a fill map enriched with :shape-id, :index, and optionally
@@ -1232,7 +1238,7 @@
ref-id (:fill-color-ref-id fill)
colors (-> libraries
(get ref-id)
(get ref-file)
(get :data)
(ctl/get-colors))
shared? (contains? colors ref-id)

View File

@@ -45,9 +45,7 @@
[app.main.ui.shapes.svg-raw :as svg-raw]
[app.main.ui.shapes.text :as text]
[app.main.ui.shapes.text.fontfaces :as ff]
[app.render-wasm.api :as wasm.api]
[app.util.dom :as dom]
[app.util.globals :as g]
[app.util.http :as http]
[app.util.strings :as ust]
[app.util.thumbnails :as th]
@@ -55,7 +53,6 @@
[beicon.v2.core :as rx]
[clojure.set :as set]
[cuerdas.core :as str]
[promesa.core :as p]
[rumext.v2 :as mf]))
(def ^:const viewbox-decimal-precision 3)
@@ -174,8 +171,6 @@
;; Don't wrap svg elements inside a <g> otherwise some can break
[:> svg-raw-wrapper {:shape shape :frame frame}]))))))
(set! wasm.api/shape-wrapper-factory shape-wrapper-factory)
(defn format-viewbox
"Format a viewbox given a rectangle"
[{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}]
@@ -485,48 +480,6 @@
[:& ff/fontfaces-style {:fonts fonts}]
[:& shape-wrapper {:shape object}]]]]))
(mf/defc object-wasm
{::mf/wrap [mf/memo]}
[{:keys [objects object-id embed skip-children]
:or {embed false}
:as props}]
(let [object (get objects object-id)
object (cond-> object
(:hide-fill-on-export object)
(assoc :fills [])
skip-children
(assoc :shapes []))
{:keys [width height] :as bounds}
(gsb/get-object-bounds objects object {:ignore-margin? false})
vbox (format-viewbox bounds)
zoom 1
canvas-ref (mf/use-ref nil)]
(mf/use-effect
(fn []
(let [canvas (mf/ref-val canvas-ref)]
(->> @wasm.api/module
(p/fmap
(fn [ready?]
(when ready?
(try
(when (wasm.api/init-canvas-context canvas)
(wasm.api/initialize-viewport
objects zoom vbox "transparent"
(fn []
(wasm.api/render-sync-shape object-id)
(dom/set-attribute! canvas "id" (dm/str "screenshot-" object-id)))))
(catch :default e
(js/console.error "Error initializing canvas context:" e)
false)))))))))
[:canvas {:ref canvas-ref
:width width
:height height
:style {:background "red"}}]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SPRITES (DEBUG)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -4,22 +4,29 @@
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
// FIXME: we need this import for .asset-element
@use "refactor/basic-rules.scss" as deprecated;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "ds/spacing.scss" as *;
.editable-select {
@extend .asset-element;
margin: 0;
padding: 0;
border: deprecated.$s-1 solid var(--input-border-color);
border: $b-1 solid var(--input-border-color);
position: relative;
display: flex;
height: deprecated.$s-32;
height: $sz-32;
width: 100%;
padding: deprecated.$s-8;
border-radius: deprecated.$br-8;
padding: var(--sp-s);
border-radius: $br-8;
cursor: pointer;
.dropdown-button {
@include deprecated.flexCenter;
display: flex;
place-content: center;
svg {
@extend .button-icon-small;
transform: rotate(90deg);
@@ -29,10 +36,11 @@
.custom-select-dropdown {
@extend .dropdown-wrapper;
max-height: deprecated.$s-320;
width: fit-content;
max-height: px2rem(320); // TODO: when this gets addressed in the DS, use a token
.separator {
margin: 0;
height: deprecated.$s-12;
height: $sz-12;
}
.dropdown-element {
@extend .dropdown-element-base;
@@ -43,7 +51,8 @@
}
.check-icon {
@include deprecated.flexCenter;
display: flex;
place-content: center;
svg {
@extend .button-icon-small;
visibility: hidden;

View File

@@ -10,6 +10,7 @@ $z-index-200: 200;
$z-index-300: 300;
$z-index-400: 400;
$z-index-500: 500;
$z-index-600: 600;
:global(:root) {
--z-index-auto: #{$z-index-auto}; // Index for elements such as workspace, rulers ...
@@ -18,4 +19,5 @@ $z-index-500: 500;
--z-index-set: #{$z-index-300}; // Index for configuration elements like modals, color picker, grid edition elements
--z-index-dropdown: #{$z-index-400}; // Index for dropdown like elements, selects, menus, dropdowns
--z-index-notifications: #{$z-index-500}; // Index for notification
--z-index-loaders: #{$z-index-600}; // Index for loaders
}

View File

@@ -217,6 +217,10 @@
design-tokens? (features/use-feature "design-tokens/v1")
wasm-renderer-enabled? (features/use-feature "render-wasm/v1")
first-frame-rendered? (mf/use-state false)
background-color (:background-color wglobal)]
(mf/with-effect []
@@ -241,6 +245,17 @@
(when (and file-loaded? (not page-id))
(st/emit! (dcm/go-to-workspace :file-id file-id ::rt/replace true))))
(mf/with-effect [file-id page-id]
(reset! first-frame-rendered? false))
(mf/with-effect []
(let [handle-wasm-render
(fn [_]
(reset! first-frame-rendered? true))
listener-key (events/listen globals/document "penpot:wasm:render" handle-wasm-render)]
(fn []
(events/unlistenByKey listener-key))))
[:> (mf/provider ctx/current-project-id) {:value project-id}
[:> (mf/provider ctx/current-file-id) {:value file-id}
[:> (mf/provider ctx/current-page-id) {:value page-id}
@@ -249,15 +264,18 @@
[:> modal-container*]
[:section {:class (stl/css :workspace)
:style {:background-color background-color
:touch-action "none"}}
:touch-action "none"
:position "relative"}}
[:> context-menu*]
(if (and file-loaded? page-id)
(when (and file-loaded? page-id)
[:> workspace-inner*
{:page-id page-id
:file-id file-id
:file file
:wglobal wglobal
:layout layout}]
:layout layout}])
(when (or (not (and file-loaded? page-id))
(and wasm-renderer-enabled? (not @first-frame-rendered?)))
[:> workspace-loader*])]]]]]]))
(mf/defc workspace-page*

View File

@@ -20,7 +20,13 @@
}
.workspace-loader {
grid-area: viewport;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: var(--z-index-loaders);
background-color: var(--color-background-primary);
}
.workspace-content {

View File

@@ -54,7 +54,7 @@
modifiers (dm/get-in modifiers [shape-id :modifiers])
shape (gsh/transform-shape shape modifiers)
props (mf/spread-props props {:shape shape :file-id file-id :page-id page-id})]
props (mf/spread-props props {:shape shape :file-id file-id :page-id page-id :libraries libraries})]
(case shape-type
:frame [:> frame/options* props]

View File

@@ -349,6 +349,7 @@
(let [form (mf/use-ctx fc/context)
input-name name
error
(get-in @form [:errors :value value-subfield index input-name])

View File

@@ -35,8 +35,8 @@
on-change
(mf/use-fn
(mf/deps input-name)
(fn [type]
(let [is-inner? (= type "inner")]
(fn [id]
(let [is-inner? (= id "inner")]
(swap! form assoc-in [:data :value indexed-type index input-name] is-inner?))))
props (mf/spread-props props {:default-selected (if value "inner" "drop")

View File

@@ -76,7 +76,7 @@
[token index prop value-subfield]
(let [value (get-in token [:value value-subfield index prop])]
(d/without-nils
{:type (if (= prop :color) :color :number)
{:type (if (= prop :color) :color :dimensions)
:value value})))
(mf/defc shadow-formset*
@@ -114,7 +114,7 @@
:token inset-token
:tokens tokens
:index index
:value-subfield value-subfield
:indexed-type value-subfield
:name :inset}]
(when show-button
[:> icon-button* {:variant "ghost"
@@ -269,13 +269,25 @@
[:value
[:map
[:shadow {:optinal true}
[:shadow {:optional true}
[:vector
[:map
[:offset-x {:optional true} [:maybe :string]]
[:offset-y {:optional true} [:maybe :string]]
[:blur {:optional true} [:maybe :string]]
[:spread {:optional true} [:maybe :string]]
[:blur {:optional true}
[:and
[:maybe :string]
[:fn {:error/fn #(tr "workspace.tokens.shadow-token-blur-value-error")}
(fn [blur]
(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)))))]]]
[:color {:optional true} [:maybe :string]]
[:color-result {:optional true} ::sm/any]
[:inset {:optional true} [:maybe :boolean]]]]]

View File

@@ -63,7 +63,7 @@
(mf/defc object-svg
{::mf/wrap-props false}
[{:keys [object-id embed skip-children wasm]}]
[{:keys [object-id embed skip-children]}]
(let [objects (mf/deref ref:objects)]
;; Set the globa CSS to assign the page size, needed for PDF
@@ -77,41 +77,26 @@
(mth/ceil height) "px")}))))
(when objects
(if wasm
[:& render/object-wasm
{:objects objects
:object-id object-id
:embed embed
:skip-children skip-children}]
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:object-id object-id
:embed embed
:skip-children skip-children}]]))))
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:object-id object-id
:embed embed
:skip-children skip-children}]])))
(mf/defc objects-svg
{::mf/wrap-props false}
[{:keys [object-ids embed skip-children wasm]}]
[{:keys [object-ids embed skip-children]}]
(when-let [objects (mf/deref ref:objects)]
(for [object-id object-ids]
(let [objects (render/adapt-objects-for-shape objects object-id)]
(if wasm
[:& render/object-wasm
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]])))))
[:& (mf/provider ctx/is-render?) {:value true}
[:& render/object-svg
{:objects objects
:key (str object-id)
:object-id object-id
:embed embed
:skip-children skip-children}]]))))
(defn- fetch-objects-bundle
[& {:keys [file-id page-id share-id object-id] :as options}]
@@ -151,7 +136,7 @@
(defn- render-objects
[params]
(try
(let [{:keys [file-id page-id embed share-id object-id skip-children wasm] :as params}
(let [{:keys [file-id page-id embed share-id object-id skip-children] :as params}
(coerce-render-objects-params params)]
(st/emit! (fetch-objects-bundle :file-id file-id :page-id page-id :share-id share-id :object-id object-id))
(if (uuid? object-id)
@@ -162,8 +147,7 @@
:share-id share-id
:object-id object-id
:embed embed
:skip-children skip-children
:wasm wasm}])
:skip-children skip-children}])
(mf/html
[:& objects-svg
@@ -172,8 +156,7 @@
:share-id share-id
:object-ids (into #{} object-id)
:embed embed
:skip-children skip-children
:wasm wasm}])))
:skip-children skip-children}])))
(catch :default cause
(when-let [explain (-> cause ex-data ::sm/explain)]
(js/console.log "Unexpected error")
@@ -324,3 +307,6 @@
(defn ^:dev/after-load after-load
[]
(reinit))

View File

@@ -23,6 +23,7 @@
[app.config :as cf]
[app.main.data.render-wasm :as drw]
[app.main.refs :as refs]
[app.main.render :as render]
[app.main.store :as st]
[app.main.ui.shapes.text]
[app.main.worker :as mw]
@@ -73,9 +74,6 @@
(def noop-fn
(constantly nil))
;;
(def shape-wrapper-factory nil)
;; Based on app.main.render/object-svg
(mf/defc object-svg
{::mf/props :obj}
@@ -83,7 +81,7 @@
(let [objects (mf/deref refs/workspace-page-objects)
shape-wrapper
(mf/with-memo [shape]
(shape-wrapper-factory objects))]
(render/shape-wrapper-factory objects))]
[:svg {:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
@@ -917,85 +915,84 @@
(defn set-object
[shape]
(perf/begin-measure "set-object")
(when shape
(let [shape (svg-filters/apply-svg-derived shape)
id (dm/get-prop shape :id)
type (dm/get-prop shape :type)
(let [shape (svg-filters/apply-svg-derived shape)
id (dm/get-prop shape :id)
type (dm/get-prop shape :type)
parent-id (get shape :parent-id)
masked (get shape :masked-group)
selrect (get shape :selrect)
constraint-h (get shape :constraints-h)
constraint-v (get shape :constraints-v)
clip-content (if (= type :frame)
(not (get shape :show-content))
false)
rotation (get shape :rotation)
transform (get shape :transform)
parent-id (get shape :parent-id)
masked (get shape :masked-group)
selrect (get shape :selrect)
constraint-h (get shape :constraints-h)
constraint-v (get shape :constraints-v)
clip-content (if (= type :frame)
(not (get shape :show-content))
false)
rotation (get shape :rotation)
transform (get shape :transform)
fills (get shape :fills)
strokes (if (= type :group)
[] (get shape :strokes))
children (get shape :shapes)
blend-mode (get shape :blend-mode)
opacity (get shape :opacity)
hidden (get shape :hidden)
content (let [content (get shape :content)]
(if (= type :text)
(ensure-text-content content)
content))
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)
corners (map #(get shape %) [:r1 :r2 :r3 :r4])]
fills (get shape :fills)
strokes (if (= type :group)
[] (get shape :strokes))
children (get shape :shapes)
blend-mode (get shape :blend-mode)
opacity (get shape :opacity)
hidden (get shape :hidden)
content (let [content (get shape :content)]
(if (= type :text)
(ensure-text-content content)
content))
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)
corners (map #(get shape %) [:r1 :r2 :r3 :r4])]
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-constraints constraint-h constraint-v)
(use-shape id)
(set-parent-id parent-id)
(set-shape-type type)
(set-shape-clip-content clip-content)
(set-shape-constraints constraint-h constraint-v)
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(set-shape-corners corners)
(set-shape-blur blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)
(set-shape-bool-type bool-type))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-content content))
(when (some? svg-attrs)
(set-shape-svg-attrs svg-attrs))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(set-shape-shadows shadows)
(when (= type :text)
(set-shape-grow-type grow-type))
(set-shape-rotation rotation)
(set-shape-transform transform)
(set-shape-blend-mode blend-mode)
(set-shape-opacity opacity)
(set-shape-hidden hidden)
(set-shape-children children)
(set-shape-corners corners)
(set-shape-blur blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)
(set-shape-bool-type bool-type))
(when (and (some? content)
(or (= type :path)
(= type :bool)))
(set-shape-path-content content))
(when (some? svg-attrs)
(set-shape-svg-attrs svg-attrs))
(when (and (some? content) (= type :svg-raw))
(set-shape-svg-raw-content (get-static-markup shape)))
(set-shape-shadows shadows)
(when (= type :text)
(set-shape-grow-type grow-type))
(set-shape-layout shape)
(set-shape-selrect selrect)
(set-shape-layout shape)
(set-shape-selrect selrect)
(let [pending_thumbnails (into [] (concat
(set-shape-text-content id content)
(set-shape-text-images id content true)
(set-shape-fills id fills true)
(set-shape-strokes id strokes true)))
pending_full (into [] (concat
(set-shape-text-images id content false)
(set-shape-fills id fills false)
(set-shape-strokes id strokes false)))]
(perf/end-measure "set-object")
{:thumbnails pending_thumbnails
:full pending_full}))))
(let [pending_thumbnails (into [] (concat
(set-shape-text-content id content)
(set-shape-text-images id content true)
(set-shape-fills id fills true)
(set-shape-strokes id strokes true)))
pending_full (into [] (concat
(set-shape-text-images id content false)
(set-shape-fills id fills false)
(set-shape-strokes id strokes false)))]
(perf/end-measure "set-object")
{:thumbnails pending_thumbnails
:full pending_full})))
(defn update-text-layouts
[shapes]
@@ -1058,9 +1055,8 @@
(perf/end-measure "set-objects")
(process-pending shapes thumbnails full noop-fn
(fn []
(if render-callback
(render-callback)
(render-finish))
(when render-callback (render-callback))
(render-finish)
(ug/dispatch! (ug/event "penpot:wasm:set-objects")))))))
(defn clear-focus-mode

View File

@@ -199,7 +199,7 @@
:string-or-size-array (format-string-or-size-array value)
:keyword (format-keyword value)
:tracks (format-tracks value)
:shadow (format-shadow value options)
:shadows (format-shadow value options)
:blur (format-blur value)
:matrix (format-matrix value)
(if (keyword? value) (d/name value) value))))

View File

@@ -47,17 +47,18 @@
[acc {:keys [schema in value type] :as problem}]
(let [props (m/properties schema)
tprops (m/type-properties schema)
field (or (first in)
(:error/field props))
field (or (:error/field props)
in)
field (if (vector? field)
field
[field])]
(if (contains? acc field)
(if (and (= 1 (count field))
(contains? acc (first field)))
acc
(cond
(nil? field)
(or (nil? field)
(empty? field))
acc
(or (= type :malli.core/missing-key)

View File

@@ -242,7 +242,6 @@ export class SelectionController extends EventTarget {
continue;
}
let styleValue = element.style.getPropertyValue(styleName);
if (styleName === "font-family") {
styleValue = sanitizeFontFamily(styleValue);
}
@@ -277,22 +276,29 @@ export class SelectionController extends EventTarget {
this.#applyDefaultStylesToCurrentStyle();
const root = startNode.parentElement.parentElement.parentElement;
this.#applyStylesFromElementToCurrentStyle(root);
// FIXME: I don't like this approximation. Having to iterate nodes twice
// is bad for performance. I think we need another way of "computing"
// the cascade.
for (const textNode of this.#textNodeIterator.iterateFrom(
startNode,
endNode,
)) {
const paragraph = textNode.parentElement.parentElement;
if (startNode === endNode) {
const paragraph = startNode.parentElement.parentElement;
this.#applyStylesFromElementToCurrentStyle(paragraph);
}
for (const textNode of this.#textNodeIterator.iterateFrom(
startNode,
endNode,
)) {
const textSpan = textNode.parentElement;
this.#mergeStylesFromElementToCurrentStyle(textSpan);
const textSpan = startNode.parentElement;
this.#applyStylesFromElementToCurrentStyle(textSpan);
} else {
// FIXME: I don't like this approximation. Having to iterate nodes twice
// is bad for performance. I think we need another way of "computing"
// the cascade.
for (const textNode of this.#textNodeIterator.iterateFrom(
startNode,
endNode,
)) {
const paragraph = textNode.parentElement.parentElement;
this.#applyStylesFromElementToCurrentStyle(paragraph);
}
for (const textNode of this.#textNodeIterator.iterateFrom(
startNode,
endNode,
)) {
const textSpan = textNode.parentElement;
this.#mergeStylesFromElementToCurrentStyle(textSpan);
}
}
return this;
}

View File

@@ -8032,6 +8032,14 @@ msgstr "Name"
msgid "workspace.tokens.token-name-duplication-validation-error"
msgstr "A token already exists at the path: %s"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs
msgid "workspace.tokens.shadow-token-blur-value-error"
msgstr "Blur value cannot be negative"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs
msgid "workspace.tokens.shadow-token-spread-value-error"
msgstr "Spread value cannot be negative"
#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:42, src/app/main/ui/workspace/tokens/management/create/form.cljs:68
msgid "workspace.tokens.token-name-length-validation-error"
msgstr "Name should be at least 1 character"

View File

@@ -7908,6 +7908,14 @@ msgstr "Nombre"
msgid "workspace.tokens.token-name-duplication-validation-error"
msgstr "Ya existe un token en la ruta: %s"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs
msgid "workspace.tokens.shadow-token-blur-value-error"
msgstr "El valor de blur no puede ser negativo"
#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs
msgid "workspace.tokens.shadow-token-spread-value-error"
msgstr "El valor de spread no puede ser negativo"
#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:42, src/app/main/ui/workspace/tokens/management/create/form.cljs:68
msgid "workspace.tokens.token-name-length-validation-error"
msgstr "El nombre debería ser de al menos 1 caracter"