mirror of
https://github.com/penpot/penpot.git
synced 2026-06-21 23:02:03 +00:00
✨ Show and manage comments while designing in the workspace (#10275)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1c8d26faaf
commit
564cd1b528
@ -8,7 +8,7 @@ desc: Learn how to import and export files in Penpot, the free, open-source desi
|
||||
<p class="main-paragraph">Comments allow the team to have one priceless conversation getting and providing feedback right over the designs and prototypes.<p>
|
||||
|
||||
<h2 id="comment-workspace">At the workspace</h2>
|
||||
<p>At the workspace, activate the comment tool by clicking the comment icon in the navbar or pressing the <kbd>C</kbd> key. <a href="/user-guide/designing/workspace-basics/#comments">More about comments at the Workspace</a></p>
|
||||
<p>At the workspace, activate the comment tool by clicking the comment icon in the navbar or pressing the <kbd>C</kbd> key. Comment bubbles remain visible while you design, so you can read and manage feedback without leaving your design tools. Use <strong>View → Hide comments</strong> / <strong>Show comments</strong> or <kbd>Ctrl+Shift+C</kbd> (<kbd>Cmd+Shift+C</kbd> on macOS) to toggle bubbles on the canvas. <a href="/user-guide/designing/workspace-basics/#comments">More about comments at the Workspace</a></p>
|
||||
<p>URLs in comments are detected automatically and open in a new browser tab when clicked.</p>
|
||||
|
||||
<h2 id="comment-viewmode">At the View mode</h2>
|
||||
|
||||
@ -160,6 +160,9 @@ press <kbd>Shift/⇧</kbd> + left click over the right arrow of a group or a boa
|
||||
<li>Write your comment in the text box.</li>
|
||||
<li>Press Post to leave the comment or Cancel to not do it.</li>
|
||||
</ol>
|
||||
<p>Comment bubbles stay visible on the canvas while you design. You can reply to, edit, and manage threads without staying in Comments mode.</p>
|
||||
<p>Use <strong>View → Hide comments</strong> or <strong>View → Show comments</strong> (after Lock guides) to toggle comment bubbles on the canvas. You can also press <kbd>Ctrl+Shift+C</kbd> (<kbd>Cmd+Shift+C</kbd> on macOS). This preference is saved in your user settings.</p>
|
||||
<p>Press <kbd>C</kbd> or the comment button to enter Comments mode when you want to add a new comment on the canvas. Comments mode still works as before and shows comments if they were hidden.</p>
|
||||
<p>URLs in comments are detected automatically and open in a new browser tab when clicked.</p>
|
||||
<h3>How to reply a comment</h3>
|
||||
<ol>
|
||||
|
||||
@ -211,6 +211,11 @@ desc: Get quickstart tips, shortcuts, and tutorials for Penpot! Learn interface
|
||||
<td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>R</kbd></td>
|
||||
<td style="text-align: center;"><kbd>⌘</kbd><kbd>⇧</kbd><kbd>R</kbd></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Show/hide comments</td>
|
||||
<td style="text-align: center;"><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>C</kbd></td>
|
||||
<td style="text-align: center;"><kbd>⌘</kbd><kbd>⇧</kbd><kbd>C</kbd></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Show/hide shortcuts</td>
|
||||
<td style="text-align: center;"><kbd>?</kbd></td>
|
||||
|
||||
@ -16,14 +16,17 @@
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.workspace.common :as dwco]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.layout :as dwlo]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.router :as rt]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.mouse :as mse]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
@ -31,6 +34,18 @@
|
||||
(declare handle-interrupt)
|
||||
(declare handle-comment-layer-click)
|
||||
|
||||
(defn toggle-comments-visibility
|
||||
[& {:keys [origin] :as _opts}]
|
||||
(ptk/reify ::toggle-comments-visibility
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [visible? (contains? (:workspace-layout state) :display-comments)]
|
||||
(rx/of (vary-meta (dwlo/toggle-layout-flag :display-comments)
|
||||
assoc ::ev/origin (or origin "workspace"))
|
||||
(ntf/success (tr (if visible?
|
||||
"workspace.toast.comments-hidden"
|
||||
"workspace.toast.comments-visible"))))))))
|
||||
|
||||
(defn initialize-comments
|
||||
[file-id]
|
||||
(ptk/reify ::initialize-comments
|
||||
@ -58,12 +73,18 @@
|
||||
(ptk/reify ::handle-interrupt
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [local (:comments-local state)]
|
||||
(let [local (:comments-local state)
|
||||
comments-mode? (= :comments (get-in state [:workspace-drawing :tool]))]
|
||||
(cond
|
||||
(:draft local) (rx/of (dcmt/close-thread))
|
||||
(:open local) (rx/of (dcmt/close-thread))
|
||||
:else (rx/of (dwe/clear-edition-mode)
|
||||
(dws/deselect-all true)))))))
|
||||
;; Only clear edition / deselect on interrupt while the comments
|
||||
;; tool is active. When comments are merely visible during design,
|
||||
;; `select-shape` emits `:interrupt` and this would otherwise wipe
|
||||
;; the freshly selected shape, breaking click selection.
|
||||
comments-mode? (rx/of (dwe/clear-edition-mode)
|
||||
(dws/deselect-all true))
|
||||
:else (rx/empty))))))
|
||||
|
||||
;; Event responsible of the what should be executed when user clicked
|
||||
;; on the comments layer. An option can be create a new draft thread,
|
||||
@ -74,15 +95,17 @@
|
||||
(ptk/reify ::handle-comment-layer-click
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [local (:comments-local state)]
|
||||
(if (some? (:open local))
|
||||
(rx/of (dcmt/close-thread))
|
||||
(let [page-id (:current-page-id state)
|
||||
file-id (:current-file-id state)
|
||||
params {:position position
|
||||
:page-id page-id
|
||||
:file-id file-id}]
|
||||
(rx/of (dcmt/create-draft params))))))))
|
||||
(if (not= :comments (get-in state [:workspace-drawing :tool]))
|
||||
(rx/empty)
|
||||
(let [local (:comments-local state)]
|
||||
(if (some? (:open local))
|
||||
(rx/of (dcmt/close-thread))
|
||||
(let [page-id (:current-page-id state)
|
||||
file-id (:current-file-id state)
|
||||
params {:position position
|
||||
:page-id page-id
|
||||
:file-id file-id}]
|
||||
(rx/of (dcmt/create-draft params)))))))))
|
||||
|
||||
(defn center-to-comment-thread
|
||||
[{:keys [position] :as thread}]
|
||||
@ -126,7 +149,18 @@
|
||||
thread-id (:id thread)]
|
||||
|
||||
(rx/concat
|
||||
(rx/of #(update % :comment-threads assoc thread-id thread))
|
||||
(rx/of (fn [state]
|
||||
(-> state
|
||||
(update :comment-threads assoc thread-id thread)
|
||||
;; Keep the page positions map in sync so subsequent
|
||||
;; frame moves compute the relative offset from the
|
||||
;; latest position instead of a stale one.
|
||||
(dsh/update-page page-id
|
||||
#(update-in % [:comment-thread-positions thread-id]
|
||||
(fn [pos]
|
||||
(-> pos
|
||||
(assoc :position (:position thread))
|
||||
(assoc :frame-id (:frame-id thread)))))))))
|
||||
(->> (rp/cmd! :update-comment-thread-position thread)
|
||||
(rx/catch #(rx/throw {:type :update-comment-thread-position}))
|
||||
(rx/ignore))))))))
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
[app.main.data.workspace.drawing.box :as box]
|
||||
[app.main.data.workspace.drawing.common :as common]
|
||||
[app.main.data.workspace.drawing.curve :as curve]
|
||||
[app.main.data.workspace.layout :as dwlo]
|
||||
[app.main.data.workspace.path :as path]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
@ -57,6 +58,9 @@
|
||||
|
||||
;; NOTE: comments are a special case and they manage they
|
||||
;; own interrupt cycle.
|
||||
(when (= tool :comments)
|
||||
(rx/of (dwlo/toggle-layout-flag :display-comments :force? true)))
|
||||
|
||||
(when (and (not= tool :comments)
|
||||
(not= tool :path))
|
||||
(let [stopper (rx/filter (ptk/type? ::clear-drawing) stream)]
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
:element-options
|
||||
:rulers
|
||||
:display-guides
|
||||
:display-comments
|
||||
:lock-guides
|
||||
:snap-guides
|
||||
:scale-text
|
||||
@ -60,6 +61,7 @@
|
||||
:element-options
|
||||
:rulers
|
||||
:display-guides
|
||||
:display-comments
|
||||
:snap-guides
|
||||
:dynamic-alignment
|
||||
:display-artboard-names
|
||||
@ -143,7 +145,8 @@
|
||||
{:hide-palettes :app.main.data.workspace/hide-palettes?
|
||||
:colorpalette :app.main.data.workspace/show-colorpalette?
|
||||
:textpalette :app.main.data.workspace/show-textpalette?
|
||||
:rulers :app.main.data.workspace/show-rulers?})
|
||||
:rulers :app.main.data.workspace/show-rulers?
|
||||
:display-comments :app.main.data.workspace/show-comments?})
|
||||
|
||||
(defn load-layout-flags
|
||||
"Given the current layout flags, and updates them with the data
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.layers :as dwly]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
@ -322,6 +323,12 @@
|
||||
:subsections [:tools]
|
||||
:fn #(st/emit! (dwd/select-for-drawing :comments))}
|
||||
|
||||
:toggle-comments-visibility
|
||||
{:tooltip (ds/meta-shift "C")
|
||||
:command (ds/c-mod "shift+c")
|
||||
:subsections [:main-menu]
|
||||
:fn #(st/emit! (dwcm/toggle-comments-visibility {:origin "workspace-shortcuts"}))}
|
||||
|
||||
:insert-image {:tooltip (ds/shift "K")
|
||||
:command "shift+k"
|
||||
:subsections [:tools]
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.data.shortcuts :as scd]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
@ -355,7 +356,15 @@
|
||||
(r/set-resize-type! :bottom)
|
||||
(st/emit! (dw/remove-layout-flag :colorpalette)
|
||||
(-> (dw/toggle-layout-flag :textpalette)
|
||||
(vary-meta assoc ::ev/origin "workspace:menu")))))]
|
||||
(vary-meta assoc ::ev/origin "workspace:menu")))))
|
||||
|
||||
toggle-comments-visibility
|
||||
(mf/use-fn
|
||||
(mf/deps on-close)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwcm/toggle-comments-visibility {:origin "workspace:menu"}))
|
||||
(on-close)))]
|
||||
|
||||
[:> dropdown-menu* {:show true
|
||||
:class (stl/css :base-menu :sub-menu :pos-3)
|
||||
@ -400,6 +409,19 @@
|
||||
(tr "workspace.header.menu.unlock-guides")
|
||||
(tr "workspace.header.menu.lock-guides"))]]
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :base-menu-item :submenu-item)
|
||||
:on-click toggle-comments-visibility
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(toggle-comments-visibility event)))
|
||||
:data-testid "display-comments"
|
||||
:id "file-menu-comments"}
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(if (contains? layout :display-comments)
|
||||
(tr "workspace.header.menu.hide-comments")
|
||||
(tr "workspace.header.menu.show-comments"))]
|
||||
[:> shortcuts* {:id :toggle-comments-visibility}]]
|
||||
|
||||
(when-not ^boolean read-only?
|
||||
[:*
|
||||
[:> dropdown-menu-item* {:class (stl/css :base-menu-item :submenu-item)
|
||||
|
||||
@ -223,7 +223,9 @@
|
||||
on-frame-select (actions/on-frame-select selected read-only?)
|
||||
|
||||
disable-events? (contains? layout :comments)
|
||||
show-comments? (= drawing-tool :comments)
|
||||
comments-mode? (= drawing-tool :comments)
|
||||
show-comments? (or comments-mode?
|
||||
(contains? layout :display-comments))
|
||||
show-cursor-tooltip? tooltip
|
||||
show-draw-area? drawing-obj
|
||||
show-gradient-handlers? (= (count selected) 1)
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.refs :as refs]
|
||||
@ -31,12 +34,38 @@
|
||||
|
||||
threads-map (mf/deref refs/threads)
|
||||
|
||||
;; Active transform modifiers (e.g. while dragging a board). We use
|
||||
;; them to move comment bubbles live alongside their frame, instead of
|
||||
;; only repositioning them at drop time. The SVG (legacy) renderer keeps
|
||||
;; them in `:workspace-modifiers`, while the WASM renderer pushes them
|
||||
;; through the `wasm-modifiers` stream as plain transform matrices.
|
||||
modifiers (mf/deref refs/workspace-modifiers)
|
||||
wasm-mods (into {} (mf/deref refs/workspace-wasm-modifiers))
|
||||
objects (mf/deref refs/workspace-page-objects)
|
||||
|
||||
threads
|
||||
(mf/with-memo [threads-map local profile page-id]
|
||||
(->> (vals threads-map)
|
||||
(filter #(= (:page-id %) page-id))
|
||||
(dcm/apply-filters local profile)))
|
||||
|
||||
;; Returns the position translation matrix for a frame that is being
|
||||
;; transformed, or nil when the frame has no active modifier. The delta
|
||||
;; matches `move-frame-comment-threads` (frame top-left displacement) so
|
||||
;; the bubble does not jump when the modifier is committed.
|
||||
frame-position-modifier
|
||||
(fn [frame-id]
|
||||
(when-let [frame (get objects frame-id)]
|
||||
(let [frame'
|
||||
(if-let [modifier (get-in modifiers [frame-id :modifiers])]
|
||||
(gsh/transform-shape frame modifier)
|
||||
(when-let [transform (get wasm-mods frame-id)]
|
||||
(gsh/apply-transform frame transform)))]
|
||||
(when (some? frame')
|
||||
(let [delta (gpt/to-vec (gpt/point (:x frame) (:y frame))
|
||||
(gpt/point (:x frame') (:y frame')))]
|
||||
(gmt/translate-matrix delta))))))
|
||||
|
||||
viewport
|
||||
(assoc vport :offset-x pos-x :offset-y pos-y)
|
||||
|
||||
@ -67,9 +96,11 @@
|
||||
(if group?
|
||||
[:> cmt/comment-floating-group* {:thread-group thread-group
|
||||
:zoom zoom
|
||||
:position-modifier (frame-position-modifier (:frame-id thread))
|
||||
:key (:seqn thread)}]
|
||||
[:> cmt/comment-floating-bubble* {:thread thread
|
||||
:zoom zoom
|
||||
:position-modifier (frame-position-modifier (:frame-id thread))
|
||||
:is-open (= (:id thread) (:open local))
|
||||
:key (:seqn thread)}])))
|
||||
|
||||
@ -79,6 +110,7 @@
|
||||
[:> cmt/comment-floating-thread*
|
||||
{:thread thread
|
||||
:viewport viewport
|
||||
:position-modifier (frame-position-modifier (:frame-id thread))
|
||||
:zoom zoom}])))
|
||||
|
||||
(when-let [draft (:draft local)]
|
||||
|
||||
@ -333,7 +333,9 @@
|
||||
on-frame-select (actions/on-frame-select selected read-only?)
|
||||
|
||||
disable-events? (contains? layout :comments)
|
||||
show-comments? (= drawing-tool :comments)
|
||||
comments-mode? (= drawing-tool :comments)
|
||||
show-comments? (or comments-mode?
|
||||
(contains? layout :display-comments))
|
||||
show-cursor-tooltip? tooltip
|
||||
show-draw-area? drawing-obj
|
||||
show-gradient-handlers? (= (count selected) 1)
|
||||
|
||||
@ -6767,6 +6767,18 @@ msgstr "Hide fonts palette"
|
||||
msgid "workspace.header.menu.lock-guides"
|
||||
msgstr "Lock guides"
|
||||
|
||||
msgid "workspace.header.menu.show-comments"
|
||||
msgstr "Show comments"
|
||||
|
||||
msgid "workspace.header.menu.hide-comments"
|
||||
msgstr "Hide comments"
|
||||
|
||||
msgid "workspace.toast.comments-visible"
|
||||
msgstr "Comments visible"
|
||||
|
||||
msgid "workspace.toast.comments-hidden"
|
||||
msgstr "Comments hidden"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:853
|
||||
msgid "workspace.header.menu.mcp.plugin.status.connect"
|
||||
msgstr "Connect"
|
||||
|
||||
@ -6596,6 +6596,18 @@ msgstr "Ocultar nombres de tableros"
|
||||
msgid "workspace.header.menu.hide-guides"
|
||||
msgstr "Ocultar guías"
|
||||
|
||||
msgid "workspace.header.menu.show-comments"
|
||||
msgstr "Mostrar comentarios"
|
||||
|
||||
msgid "workspace.header.menu.hide-comments"
|
||||
msgstr "Ocultar comentarios"
|
||||
|
||||
msgid "workspace.toast.comments-visible"
|
||||
msgstr "Comentarios visibles"
|
||||
|
||||
msgid "workspace.toast.comments-hidden"
|
||||
msgstr "Comentarios ocultos"
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:413
|
||||
msgid "workspace.header.menu.hide-palette"
|
||||
msgstr "Ocultar paleta de colores"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user