0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-23 15:26:29 -05:00

Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Andrey Antukh 2025-02-11 11:25:40 +01:00
commit a3a757f842
16 changed files with 121 additions and 29 deletions

View file

@ -38,6 +38,8 @@
(def r-mentions-split #"@\[[^\]]*\]\([^\)]*\)") (def r-mentions-split #"@\[[^\]]*\]\([^\)]*\)")
(def r-mentions #"@\[([^\]]*)\]\(([^\)]*)\)") (def r-mentions #"@\[([^\]]*)\]\(([^\)]*)\)")
(def comment-max-length 750)
(defn- format-comment (defn- format-comment
[{:keys [content]}] [{:keys [content]}]
(->> (d/interleave-all (->> (d/interleave-all
@ -442,7 +444,7 @@
[:map {:title "create-comment-thread"} [:map {:title "create-comment-thread"}
[:file-id ::sm/uuid] [:file-id ::sm/uuid]
[:position ::gpt/point] [:position ::gpt/point]
[:content [:string {:max 750}]] [:content [:string {:max comment-max-length}]]
[:page-id ::sm/uuid] [:page-id ::sm/uuid]
[:frame-id ::sm/uuid] [:frame-id ::sm/uuid]
[:share-id {:optional true} [:maybe ::sm/uuid]] [:share-id {:optional true} [:maybe ::sm/uuid]]
@ -585,7 +587,7 @@
schema:create-comment schema:create-comment
[:map {:title "create-comment"} [:map {:title "create-comment"}
[:thread-id ::sm/uuid] [:thread-id ::sm/uuid]
[:content [:string {:max 250}]] [:content [:string {:max comment-max-length}]]
[:share-id {:optional true} [:maybe ::sm/uuid]] [:share-id {:optional true} [:maybe ::sm/uuid]]
[:mentions {:optional true} [::sm/set ::sm/uuid]]]) [:mentions {:optional true} [::sm/set ::sm/uuid]]])
@ -655,7 +657,7 @@
schema:update-comment schema:update-comment
[:map {:title "update-comment"} [:map {:title "update-comment"}
[:id ::sm/uuid] [:id ::sm/uuid]
[:content [:string {:max 250}]] [:content [:string {:max comment-max-length}]]
[:share-id {:optional true} [:maybe ::sm/uuid]] [:share-id {:optional true} [:maybe ::sm/uuid]]
[:mentions {:optional true} [::sm/set ::sm/uuid]]]) [:mentions {:optional true} [::sm/set ::sm/uuid]]])

View file

@ -275,7 +275,7 @@
[_ _ _ {:keys [::logger ::props ::level ::cause ::trace ::message]}] [_ _ _ {:keys [::logger ::props ::level ::cause ::trace ::message]}]
(when (enabled? logger level) (when (enabled? logger level)
(let [hstyles (str/ffmt "font-weight: 600; color: %" (level->color level)) (let [hstyles (str/ffmt "font-weight: 600; color: %" (level->color level))
mstyles (str/ffmt "font-weight: 300; color: %" "#282a2e") mstyles (str/ffmt "font-weight: 300; color: %" (level->color level))
header (str/concat "%c" (level->name level) " [" logger "] ") header (str/concat "%c" (level->name level) " [" logger "] ")
message (str/concat header "%c" @message)] message (str/concat header "%c" @message)]

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -130,6 +130,21 @@ title: Shortcuts
<td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Z</kbd></td> <td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Z</kbd></td>
<td style="text-align: center;"><kbd>⌘</kbd><kbd>Z</kbd></td> <td style="text-align: center;"><kbd>⌘</kbd><kbd>Z</kbd></td>
</tr> </tr>
<tr>
<td>Copy link to board</td>
<td style="text-align: center;"><kbd>Shift</kbd><kbd>Alt</kbd><kbd>C</kbd></td>
<td style="text-align: center;"><kbd>⇧</kbd><kbd>Alt</kbd><kbd>C</kbd></td>
</tr>
<tr>
<td>Copy properties</td>
<td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>C</kbd></td>
<td style="text-align: center;"><kbd>⌘</kbd><kbd>⌥</kbd><kbd>C</kbd></td>
</tr>
<tr>
<td>Paste properties</td>
<td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>V</kbd></td>
<td style="text-align: center;"><kbd>⌘</kbd><kbd>⌥</kbd><kbd>V</kbd></td>
</tr>
</tbody> </tbody>
</table> </table>

View file

@ -103,7 +103,6 @@ title: 04· Layer basics
<p>At the dropdown menu (right click on a layer to show it) there's the option "Select layer" that allows the user to select one layer among the ones that are under the cursor's location.</p> <p>At the dropdown menu (right click on a layer to show it) there's the option "Select layer" that allows the user to select one layer among the ones that are under the cursor's location.</p>
<p><img src="/img/layers-select-menu.gif" alt="layers select" /></p> <p><img src="/img/layers-select-menu.gif" alt="layers select" /></p>
<h2 id="group-layers">Group layers</h2> <h2 id="group-layers">Group layers</h2>
<p>Grouped layers can be moved, transformed or styled at the same time. </p> <p>Grouped layers can be moved, transformed or styled at the same time. </p>
<ul> <ul>
@ -128,7 +127,6 @@ title: 04· Layer basics
</video> </video>
</figure> </figure>
<h2 id="move-layers">Move layers</h2> <h2 id="move-layers">Move layers</h2>
<p>To move one or more layers on the viewport you have to select them first and then click and drag the selection where you want to place them. You can also use the design panel to set a precise position relative to the viewport or the board.</p> <p>To move one or more layers on the viewport you have to select them first and then click and drag the selection where you want to place them. You can also use the design panel to set a precise position relative to the viewport or the board.</p>
<figure> <figure>
@ -137,7 +135,6 @@ title: 04· Layer basics
</video> </video>
</figure> </figure>
<h2 id="resize-layers">Resize layers</h2> <h2 id="resize-layers">Resize layers</h2>
<p>To resize a selected layer you can use the handles at the edges of the selection box. Make sure the cursor is in resizing mode. You can also use the design panel where you can link width and height.</p> <p>To resize a selected layer you can use the handles at the edges of the selection box. Make sure the cursor is in resizing mode. You can also use the design panel where you can link width and height.</p>
<ul> <ul>
@ -204,6 +201,18 @@ title: 04· Layer basics
</video> </video>
</figure> </figure>
<h2 id="scale-elements">Copy CSS properties</h2>
<p>To copy CSS properties from layers:</p>
<ol>
<li>Select one or more layers.</li>
<li>Right click to show the layer menu.</li>
<li>Press <strong>Copy/Paste as... > Copy as CSS</strong> in case you only want to get the CSS properties from the selected layer/s.</li>
<li>Press <strong>Copy/Paste as... > Copy as CSS (nested layers)</strong> in case you only want to get the CSS properties from the selected layer/s and all the contained layers.</li>
</ol>
<figure>
<img alt="Copy CSS properties" src="/img/layers/copy-css.webp"/>
</figure>
<h2 id="collapse-groups">Collapse groups and boards</h2> <h2 id="collapse-groups">Collapse groups and boards</h2>
<p>Groups and boards can have their contents expanded and collapsed. Click on the arrow at the <p>Groups and boards can have their contents expanded and collapsed. Click on the arrow at the
right side to toggle the visibility of their contents. </p> right side to toggle the visibility of their contents. </p>

View file

@ -60,6 +60,17 @@ are shown by default at the <a href="/user-guide/view-mode">View mode</a>, actin
</video> </video>
</figure> </figure>
<h3>Copy link to board</h3>
<p>You can get the link to each individual board, making it easy to share them with team members or include direct links in documentation.</p>
<figure>
<img src="/img/objects/board-copy-link.webp" alt="copy link to board">
</figure>
<p>There are two ways to copy a direct link to a board:</p>
<ul>
<li>Using the menu: Select the board, right click and select the "Copy link" option.</li>
<li>Using the shortcut: Select the board and press <kbd>Shift/⇧</kbd> + <kbd>Alt/⌥</kbd> + <kbd>C</kbd>.</li>
</ul>
<h3>Clip content</h3> <h3>Clip content</h3>
<p>Boards offer the option to clip its content (or not).</p> <p>Boards offer the option to clip its content (or not).</p>
<figure> <figure>

View file

@ -181,4 +181,29 @@ title: 06· Styling
<li><strong>Saturation</strong></li> <li><strong>Saturation</strong></li>
<li><strong>Color</strong></li> <li><strong>Color</strong></li>
<li><strong>Luminosity</strong></li> <li><strong>Luminosity</strong></li>
</ul> </ul>
<h2 id="copy-paste-properties">Copy/Paste properties</h2>
<p>You can copy and apply properties, including fills, strokes, shadows, and others from one layer to another—or multiple layers with just a few clicks. You can do it using the layer's menu or shortcuts.</p>
<figure>
<video title="Apply blur to a layer" muted="" playsinline="" controls="" width="100%" poster="/img/styling/copy-properties.webp" height="auto">
<source src="/img/styling/copy-properties.mp4" type="video/mp4">
</video>
</figure>
<p>Using the layer menu</p>
<ol>
<li>Select one layer.</li>
<li>Right click to show the layer menu.</li>
<li>Press <strong>Copy/Paste as... > Copy properties</strong>.</li>
<li>Select one or more other layers.</li>
<li>Right click to show the layer/s menu.</li>
<li>Press <strong>Copy/Paste as... > Paste properties</strong>.</li>
</ol>
<p>Using Shortcuts</p>
<ul>
<li><strong>Copy properties</strong>: <kbd>Ctrl/⌘</kbd> + <kbd>Alt/⌥</kbd> + <kbd>C</kbd></li>
<li><strong>Paste properties</strong>: <kbd>Ctrl/⌘</kbd> + <kbd>Alt/⌥</kbd> + <kbd>V</kbd></li>
</ul>

View file

@ -146,7 +146,7 @@
;; Input text for comments with mentions ;; Input text for comments with mentions
(mf/defc comment-input* (mf/defc comment-input*
{::mf/private true} {::mf/private true}
[{:keys [value placeholder max-length autofocus on-focus on-blur on-change on-esc on-ctrl-enter]}] [{:keys [value placeholder autofocus on-focus on-blur on-change on-esc on-ctrl-enter]}]
(let [value (d/nilv value "") (let [value (d/nilv value "")
prev-value (h/use-previous value) prev-value (h/use-previous value)
@ -196,7 +196,7 @@
(dom/append-child! node (create-text-node))) (dom/append-child! node (create-text-node)))
(let [new-input (parse-nodes node)] (let [new-input (parse-nodes node)]
(when (and on-change (<= (count new-input) max-length)) (when on-change
(on-change new-input)))))) (on-change new-input))))))
handle-select handle-select
@ -637,6 +637,10 @@
:disabled is-disabled} :disabled is-disabled}
(tr "labels.post")]])) (tr "labels.post")]]))
(defn- exceeds-length?
[content]
(> (count content) 750))
(mf/defc comment-reply-form* (mf/defc comment-reply-form*
{::mf/props :obj {::mf/props :obj
::mf/private true} ::mf/private true}
@ -644,7 +648,8 @@
(let [show-buttons? (mf/use-state false) (let [show-buttons? (mf/use-state false)
content (mf/use-state "") content (mf/use-state "")
disabled? (blank-content? @content) disabled? (or (blank-content? @content)
(exceeds-length? @content))
on-focus on-focus
(mf/use-fn (mf/use-fn
@ -678,8 +683,10 @@
:on-blur on-blur :on-blur on-blur
:on-focus on-focus :on-focus on-focus
:on-ctrl-enter on-submit* :on-ctrl-enter on-submit*
:on-change on-change :on-change on-change}]
:max-length 750}] (when (exceeds-length? @content)
[:div {:class (stl/css :error-text)}
(tr "errors.character-limit-exceeded")])
(when (or @show-buttons? (seq @content)) (when (or @show-buttons? (seq @content))
[:> comment-form-buttons* {:on-submit on-submit* [:> comment-form-buttons* {:on-submit on-submit*
:on-cancel on-cancel :on-cancel on-cancel
@ -690,7 +697,8 @@
[{:keys [content on-submit on-cancel]}] [{:keys [content on-submit on-cancel]}]
(let [content (mf/use-state content) (let [content (mf/use-state content)
disabled? (blank-content? @content) disabled? (or (blank-content? @content)
(exceeds-length? @content))
on-change on-change
(mf/use-fn (mf/use-fn
@ -706,8 +714,10 @@
{:value @content {:value @content
:autofocus true :autofocus true
:on-ctrl-enter on-submit* :on-ctrl-enter on-submit*
:on-change on-change :on-change on-change}]
:max-length 750}] (when (exceeds-length? @content)
[:div {:class (stl/css :error-text)}
(tr "errors.character-limit-exceeded")])
[:> comment-form-buttons* {:on-submit on-submit* [:> comment-form-buttons* {:on-submit on-submit*
:on-cancel on-cancel :on-cancel on-cancel
:is-disabled disabled?}]])) :is-disabled disabled?}]]))
@ -726,7 +736,8 @@
pos-x (* (:x position) zoom) pos-x (* (:x position) zoom)
pos-y (* (:y position) zoom) pos-y (* (:y position) zoom)
disabled? (blank-content? content) disabled? (or (blank-content? content)
(exceeds-length? content))
on-esc on-esc
(mf/use-fn (mf/use-fn
@ -769,8 +780,10 @@
:autofocus true :autofocus true
:on-esc on-esc :on-esc on-esc
:on-change on-change :on-change on-change
:on-ctrl-enter on-submit* :on-ctrl-enter on-submit*}]
:max-length 750}] (when (exceeds-length? content)
[:div {:class (stl/css :error-text)}
(tr "errors.character-limit-exceeded")])
[:> comment-form-buttons* {:on-submit on-submit* [:> comment-form-buttons* {:on-submit on-submit*
:on-cancel on-esc :on-cancel on-esc
:is-disabled disabled?}]] :is-disabled disabled?}]]

View file

@ -22,6 +22,11 @@
color: var(--comment-subtitle-color); color: var(--comment-subtitle-color);
} }
.error-text {
@include bodySmallTypography;
color: var(--color-foreground-error);
}
.location { .location {
color: var(--comment-subtitle-color); color: var(--comment-subtitle-color);
display: flex; display: flex;
@ -246,6 +251,7 @@
grid-template-columns: 1fr auto auto; grid-template-columns: 1fr auto auto;
justify-content: flex-end; justify-content: flex-end;
gap: $s-8; gap: $s-8;
margin-top: $s-8;
} }
.open-mentions-button { .open-mentions-button {
@ -321,7 +327,6 @@
border: $s-1 solid var(--input-border-color); border: $s-1 solid var(--input-border-color);
color: var(--input-foreground-color); color: var(--input-foreground-color);
height: $s-36; height: $s-36;
margin-bottom: $s-8;
max-width: $s-260; max-width: $s-260;
overflow-y: auto; overflow-y: auto;
padding: $s-8; padding: $s-8;

View file

@ -73,10 +73,11 @@ export function mapContentFragmentFromDocument(document, root, styleDefaults) {
currentParagraph = createParagraph(undefined, currentStyle); currentParagraph = createParagraph(undefined, currentStyle);
} }
} }
const inline = createInline(new Text(currentNode.nodeValue), currentStyle); const inline = createInline(new Text(currentNode.nodeValue), currentStyle);
const fontSize = inline.style.getPropertyValue("font-size"); const fontSize = inline.style.getPropertyValue("font-size");
if (!fontSize) console.warn("font-size", fontSize); if (!fontSize) console.warn("font-size", fontSize);
const fontFamily = inline.style.getPropertyValue("font-family");
if (!fontFamily) console.warn("font-family", fontFamily);
currentParagraph.appendChild(inline); currentParagraph.appendChild(inline);
currentNode = nodeIterator.nextNode(); currentNode = nodeIterator.nextNode();

View file

@ -23,7 +23,8 @@ export function mergeStyleDeclarations(target, source) {
// for (const styleName of source) { // for (const styleName of source) {
for (let index = 0; index < source.length; index++) { for (let index = 0; index < source.length; index++) {
const styleName = source.item(index); const styleName = source.item(index);
target.setProperty(styleName, source.getPropertyValue(styleName)); const styleValue = source.getPropertyValue(styleName);
target.setProperty(styleName, styleValue);
} }
return target return target
} }
@ -108,9 +109,10 @@ export function getComputedStyle(element) {
inertElement.style.setProperty(styleName, newValue); inertElement.style.setProperty(styleName, newValue);
} }
} else { } else {
const newValue = currentElement.style.getPropertyValue(styleName);
inertElement.style.setProperty( inertElement.style.setProperty(
styleName, styleName,
currentElement.style.getPropertyValue(styleName) newValue
); );
} }
} }
@ -130,9 +132,10 @@ export function getComputedStyle(element) {
* @returns {CSSStyleDeclaration} * @returns {CSSStyleDeclaration}
*/ */
export function normalizeStyles(node, styleDefaults = getStyleDefaultsDeclaration()) { export function normalizeStyles(node, styleDefaults = getStyleDefaultsDeclaration()) {
const computedStyle = getComputedStyle(node.parentElement);
const styleDeclaration = mergeStyleDeclarations( const styleDeclaration = mergeStyleDeclarations(
styleDefaults, styleDefaults,
getComputedStyle(node.parentElement) computedStyle
); );
// If there's a color property, we should convert it to // If there's a color property, we should convert it to
@ -149,7 +152,7 @@ export function normalizeStyles(node, styleDefaults = getStyleDefaultsDeclaratio
// If there's a font-family property and not a --font-id, then // If there's a font-family property and not a --font-id, then
// we remove the font-family because it will not work. // we remove the font-family because it will not work.
const fontFamily = styleDeclaration.getPropertyValue("font-family"); const fontFamily = styleDeclaration.getPropertyValue("font-family");
const fontId = styleDeclaration.getPropertyPriority("--font-id"); const fontId = styleDeclaration.getPropertyValue("--font-id");
if (fontFamily && !fontId) { if (fontFamily && !fontId) {
styleDeclaration.removeProperty("font-family"); styleDeclaration.removeProperty("font-family");
} }

View file

@ -1152,6 +1152,10 @@ msgstr "The fonts %s could not be loaded"
msgid "errors.cannot-upload" msgid "errors.cannot-upload"
msgstr "Cannot upload the media file." msgstr "Cannot upload the media file."
#: src/app/main/ui/comments.cljs:689
msgid "errors.character-limit-exceeded"
msgstr "Character limit exceeded"
#: src/app/main/data/workspace.cljs:1463, src/app/main/data/workspace.cljs:1660 #: src/app/main/data/workspace.cljs:1463, src/app/main/data/workspace.cljs:1660
msgid "errors.clipboard-not-implemented" msgid "errors.clipboard-not-implemented"
msgstr "Your browser cannot do this operation" msgstr "Your browser cannot do this operation"
@ -3528,7 +3532,7 @@ msgstr "Copy"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:94 #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:94
msgid "shortcuts.copy-link" msgid "shortcuts.copy-link"
msgstr "Copy link to clipboard" msgstr "Copy link"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:106 #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:106
msgid "shortcuts.copy-props" msgid "shortcuts.copy-props"
@ -6188,7 +6192,7 @@ msgstr "Copy as CSS (nested layers)"
#: src/app/main/ui/workspace/context_menu.cljs:188 #: src/app/main/ui/workspace/context_menu.cljs:188
msgid "workspace.shape.menu.copy-link" msgid "workspace.shape.menu.copy-link"
msgstr "Copy link to clipboard" msgstr "Copy link"
#: src/app/main/ui/workspace/context_menu.cljs:201 #: src/app/main/ui/workspace/context_menu.cljs:201
msgid "workspace.shape.menu.copy-paste-as" msgid "workspace.shape.menu.copy-paste-as"

View file

@ -1160,6 +1160,10 @@ msgstr "No se han podido cargar las fuentes %s"
msgid "errors.cannot-upload" msgid "errors.cannot-upload"
msgstr "No se puede cargar el archivo multimedia." msgstr "No se puede cargar el archivo multimedia."
#: src/app/main/ui/comments.cljs:689
msgid "errors.character-limit-exceeded"
msgstr "Se ha superado el límite de caracteres"
#: src/app/main/data/workspace.cljs:1463, src/app/main/data/workspace.cljs:1660 #: src/app/main/data/workspace.cljs:1463, src/app/main/data/workspace.cljs:1660
msgid "errors.clipboard-not-implemented" msgid "errors.clipboard-not-implemented"
msgstr "Tu navegador no puede realizar esta operación" msgstr "Tu navegador no puede realizar esta operación"
@ -3524,7 +3528,7 @@ msgstr "Copiar"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:94 #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:94
msgid "shortcuts.copy-link" msgid "shortcuts.copy-link"
msgstr "Copiar enlace al portapapeles" msgstr "Copiar enlace"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:95 #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:95
msgid "shortcuts.create-component" msgid "shortcuts.create-component"
@ -6186,7 +6190,7 @@ msgstr "Copiar como CSS (capas anidadas)"
#: src/app/main/ui/workspace/context_menu.cljs:188 #: src/app/main/ui/workspace/context_menu.cljs:188
msgid "workspace.shape.menu.copy-link" msgid "workspace.shape.menu.copy-link"
msgstr "Copiar enlace al portapapeles" msgstr "Copiar enlace"
#: src/app/main/ui/workspace/context_menu.cljs:201 #: src/app/main/ui/workspace/context_menu.cljs:201
msgid "workspace.shape.menu.copy-paste-as" msgid "workspace.shape.menu.copy-paste-as"