From f36aa30525b26cd153adecfd570f9a98ac1d7b67 Mon Sep 17 00:00:00 2001 From: Miguel de Benito Delgado Date: Tue, 20 May 2025 22:06:36 +0200 Subject: [PATCH] :sparkles: Add copy-as-svg to contextual menu (#6510) * :sparkles: Add "copy as svg" to contextual menu * :globe_with_meridians: Add a few translations of the new string * :books: Document commit message format for translations * :paperclip: Log SVG import errors to the console * :paperclip: Update CHANGES.md (two PRs) --------- Signed-off-by: Miguel de Benito Delgado --- CHANGES.md | 4 +-- CONTRIBUTING.md | 2 ++ frontend/src/app/main/data/workspace.cljs | 3 +- .../app/main/data/workspace/clipboard.cljs | 20 +++++++++++++ .../app/main/data/workspace/svg_upload.cljs | 6 ++-- .../app/main/ui/workspace/context_menu.cljs | 5 ++++ frontend/translations/de.po | 4 +++ frontend/translations/en.po | 4 +++ frontend/translations/es.po | 4 +++ frontend/translations/fr.po | 28 +++++++++++++++++++ 10 files changed, 75 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 62618e6f6..683cba9b0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -77,8 +77,8 @@ A non exhaustive list of changes: - Update base image for Docker Frontend to Nginx 1.28.0 - Allow multi file token import [Github #27](https://github.com/tokens-studio/penpot/issues/27) - Create `input*` wrapper component, and `label*`, `input-field*` and `hint-message*` components [Taiga #10713](https://tree.taiga.io/project/penpot/us/10713) -- Add option for deselect layers with Ctrl+Shift+Drag [Github #2509](https://github.com/penpot/penpot/issues/2509) - +- Deselect layers (and path nodes) with Ctrl+Shift+Drag [Github #2509](https://github.com/penpot/penpot/issues/2509) +- Copy to SVG from contextual menu [Github #838](https://github.com/penpot/penpot/issues/838) ### :bug: Bugs fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 204598e3c..d34cd463f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,6 +83,8 @@ Where type is: - :arrow_up: `:arrow_up:` a commit with dependency updates - :arrow_down: `:arrow_down:` a commit with dependency downgrades - :fire: `:fire:` a commit that removes files or code +- :globe_with_meridians: `:globe_with_meridians:` a commit that adds or updates + translations More info: diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index dfc5a3b4f..f0bb8ec6b 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1545,8 +1545,9 @@ (dm/export dwcp/paste-from-event) (dm/export dwcp/copy-selected-css) (dm/export dwcp/copy-selected-css-nested) -(dm/export dwcp/copy-selected-text) (dm/export dwcp/copy-selected-props) +(dm/export dwcp/copy-selected-svg) +(dm/export dwcp/copy-selected-text) (dm/export dwcp/paste-selected-props) (dm/export dwcp/paste-shapes) (dm/export dwcp/paste-data-valid?) diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index a8e41298c..1e22e4819 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -45,6 +45,7 @@ [app.main.repo :as rp] [app.main.router :as rt] [app.main.streams :as ms] + [app.util.code-gen.markup-svg :as svg] [app.util.code-gen.style-css :as css] [app.util.globals :as ug] [app.util.http :as http] @@ -329,6 +330,25 @@ :else (rx/empty)))))))) +(defn copy-selected-svg + [] + (ptk/reify ::copy-selected-svg + ptk/EffectEvent + (effect [_ state _] + (let [objects (dsh/lookup-page-objects state) + selected (->> (dsh/lookup-selected state) + (ctst/sort-z-index objects) + (mapv (d/getf objects))) + parent-frame-id (cfh/common-parent-frame objects selected) + + maybe-translate + #(if (= parent-frame-id uuid/zero) % + (gsh/translate-to-frame % (get objects parent-frame-id))) + + shapes (mapv maybe-translate selected) + svg (svg/generate-markup objects shapes)] + (wapi/write-to-clipboard svg))))) + (defn copy-selected-css [] (ptk/reify ::copy-selected-css diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 1e1b0a098..a573cd594 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.exceptions :as ex] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] [app.common.files.shapes-builder :as sb] @@ -119,6 +120,7 @@ (dwu/commit-undo-transaction undo-id))) (catch :default cause - (rx/throw {:type :svg-parser - :data cause}))))))) + (js/console.error cause) + (rx/throw (ex/error :type :svg-parser + :hint (ex-message cause))))))))) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 5132bdb9c..e776ea3fc 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -162,6 +162,9 @@ handle-paste-props (mf/use-callback #(st/emit! (dw/paste-selected-props))) + handle-copy-svg + (mf/use-callback #(st/emit! (dw/copy-selected-svg))) + handle-copy-text (mf/use-callback #(st/emit! (dw/copy-selected-text))) @@ -201,6 +204,8 @@ [:> menu-entry* {:title (tr "workspace.shape.menu.copy-paste-as") :on-pointer-enter (when (cf/check-browser? :chrome) handle-hover-copy-paste)} + [:> menu-entry* {:title (tr "workspace.shape.menu.copy-svg") + :on-click handle-copy-svg}] [:> menu-entry* {:title (tr "workspace.shape.menu.copy-css") :on-click handle-copy-css}] [:> menu-entry* {:title (tr "workspace.shape.menu.copy-css-nested") diff --git a/frontend/translations/de.po b/frontend/translations/de.po index bf1ae3ed3..5e6c2decb 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -6141,6 +6141,10 @@ msgstr "Eins nach hinten" msgid "workspace.shape.menu.copy" msgstr "Kopieren" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-svg" +msgstr "Als SVG kopieren" + #: src/app/main/ui/workspace/context_menu.cljs:204 msgid "workspace.shape.menu.copy-css" msgstr "Als CSS kopieren" diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 22055b34d..2cf05e88c 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -6484,6 +6484,10 @@ msgstr "Send backward" msgid "workspace.shape.menu.copy" msgstr "Copy" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-svg" +msgstr "Copy as SVG" + #: src/app/main/ui/workspace/context_menu.cljs:204 msgid "workspace.shape.menu.copy-css" msgstr "Copy as CSS" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 9e23fc109..b9fc75aa8 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -6514,6 +6514,10 @@ msgstr "Enviar atrás" msgid "workspace.shape.menu.copy" msgstr "Copiar" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-svg" +msgstr "Copiar como SVG" + #: src/app/main/ui/workspace/context_menu.cljs:204 msgid "workspace.shape.menu.copy-css" msgstr "Copiar como CSS" diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index 62c0f9864..0f9e4316c 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -5475,6 +5475,34 @@ msgstr "Éloigner" msgid "workspace.shape.menu.copy" msgstr "Copier" +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-svg" +msgstr "Copier comme SVG" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-css" +msgstr "Copier comme CSS" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-css-nested" +msgstr "Copier comme CSS (calques imbriquées)" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-link" +msgstr "Copier le lien" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-paste-as" +msgstr "Copier/Coller comme ..." + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-props" +msgstr "Copier les propriétés" + +#: src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.copy-text" +msgstr "Copier comme texte" + #: src/app/main/ui/workspace/sidebar/assets/common.cljs:454 msgid "workspace.shape.menu.create-annotation" msgstr "Créer une note"