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
20 changed files with 151 additions and 62 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

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

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

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