mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 10:56:20 +00:00
✨ Improve workspace debug sidebar workflow (#10678)
This commit is contained in:
parent
c11d3aaaa7
commit
95cfbf5f7c
@ -1568,6 +1568,7 @@
|
|||||||
(dm/export dwcp/paste-shapes)
|
(dm/export dwcp/paste-shapes)
|
||||||
(dm/export dwcp/paste-data-valid?)
|
(dm/export dwcp/paste-data-valid?)
|
||||||
(dm/export dwcp/copy-link-to-clipboard)
|
(dm/export dwcp/copy-link-to-clipboard)
|
||||||
|
(dm/export dwcp/copy-id-to-clipboard)
|
||||||
(dm/export dwcp/copy-as-image)
|
(dm/export dwcp/copy-as-image)
|
||||||
|
|
||||||
;; Drawing
|
;; Drawing
|
||||||
|
|||||||
@ -1138,6 +1138,15 @@
|
|||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(clipboard/to-clipboard (rt/get-current-href)))))
|
(clipboard/to-clipboard (rt/get-current-href)))))
|
||||||
|
|
||||||
|
(defn copy-id-to-clipboard
|
||||||
|
[id]
|
||||||
|
(ptk/reify ::copy-id-to-clipboard
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ _ _]
|
||||||
|
(->> (rx/from (clipboard/to-clipboard id))
|
||||||
|
(rx/map (fn [_]
|
||||||
|
(ntf/info "The id has been copied to the clipboard")))))))
|
||||||
|
|
||||||
(defn copy-as-image
|
(defn copy-as-image
|
||||||
[]
|
[]
|
||||||
(ptk/reify ::copy-as-image
|
(ptk/reify ::copy-as-image
|
||||||
|
|||||||
@ -53,7 +53,7 @@
|
|||||||
:add #{:tokens}}})
|
:add #{:tokens}}})
|
||||||
|
|
||||||
(def valid-options-mode
|
(def valid-options-mode
|
||||||
#{:design :prototype :inspect})
|
#{:design :prototype :inspect :debug})
|
||||||
|
|
||||||
(def default-layout
|
(def default-layout
|
||||||
#{:sitemap
|
#{:sitemap
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||||
[app.util.clipboard :as clipboard]
|
[app.util.clipboard :as clipboard]
|
||||||
|
[app.util.debug :as dbg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr] :as i18n]
|
[app.util.i18n :refer [tr] :as i18n]
|
||||||
[app.util.shape-icon :as usi]
|
[app.util.shape-icon :as usi]
|
||||||
@ -152,6 +153,7 @@
|
|||||||
|
|
||||||
do-copy #(st/emit! (dw/copy-selected))
|
do-copy #(st/emit! (dw/copy-selected))
|
||||||
do-copy-link #(st/emit! (dw/copy-link-to-clipboard))
|
do-copy-link #(st/emit! (dw/copy-link-to-clipboard))
|
||||||
|
do-copy-id #(st/emit! (dw/copy-id-to-clipboard (-> shapes first :id str)))
|
||||||
|
|
||||||
do-cut #(st/emit! (dw/copy-selected)
|
do-cut #(st/emit! (dw/copy-selected)
|
||||||
(dw/delete-selected))
|
(dw/delete-selected))
|
||||||
@ -200,6 +202,10 @@
|
|||||||
(reset! enabled-paste-props* false))))))]
|
(reset! enabled-paste-props* false))))))]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
|
(when (and (not multiple?)
|
||||||
|
(dbg/enabled? :show-ids))
|
||||||
|
[:> menu-entry* {:title (tr "workspace.shape.menu.copy-id")
|
||||||
|
:on-click do-copy-id}])
|
||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.copy")
|
[:> menu-entry* {:title (tr "workspace.shape.menu.copy")
|
||||||
:shortcut (sc/get-tooltip :copy)
|
:shortcut (sc/get-tooltip :copy)
|
||||||
:on-click do-copy}]
|
:on-click do-copy}]
|
||||||
|
|||||||
@ -33,7 +33,6 @@
|
|||||||
[app.main.ui.workspace.right-header :refer [right-header*]]
|
[app.main.ui.workspace.right-header :refer [right-header*]]
|
||||||
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox*]]
|
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox*]]
|
||||||
[app.main.ui.workspace.sidebar.debug :refer [debug-panel*]]
|
[app.main.ui.workspace.sidebar.debug :refer [debug-panel*]]
|
||||||
[app.main.ui.workspace.sidebar.debug-shape-info :refer [debug-shape-info*]]
|
|
||||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
|
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
|
||||||
[app.main.ui.workspace.sidebar.layers :refer [layers-toolbox*]]
|
[app.main.ui.workspace.sidebar.layers :refer [layers-toolbox*]]
|
||||||
[app.main.ui.workspace.sidebar.options :refer [options-toolbox*]]
|
[app.main.ui.workspace.sidebar.options :refer [options-toolbox*]]
|
||||||
@ -41,7 +40,6 @@
|
|||||||
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap*]]
|
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap*]]
|
||||||
[app.main.ui.workspace.sidebar.versions :refer [versions-toolbox*]]
|
[app.main.ui.workspace.sidebar.versions :refer [versions-toolbox*]]
|
||||||
[app.main.ui.workspace.tokens.sidebar :refer [tokens-sidebar-tab*]]
|
[app.main.ui.workspace.tokens.sidebar :refer [tokens-sidebar-tab*]]
|
||||||
[app.util.debug :as dbg]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -285,18 +283,16 @@
|
|||||||
(let [is-comments? (= drawing-tool :comments)
|
(let [is-comments? (= drawing-tool :comments)
|
||||||
is-history? (contains? layout :document-history)
|
is-history? (contains? layout :document-history)
|
||||||
is-inspect? (= section :inspect)
|
is-inspect? (= section :inspect)
|
||||||
|
is-debug? (= section :debug)
|
||||||
dbg-shape-panel? (dbg/enabled? :shape-panel)
|
|
||||||
|
|
||||||
current-section* (mf/use-state :info)
|
current-section* (mf/use-state :info)
|
||||||
current-section (deref current-section*)
|
current-section (deref current-section*)
|
||||||
|
|
||||||
can-be-expanded?
|
can-be-expanded?
|
||||||
(or dbg-shape-panel?
|
(and (not is-comments?)
|
||||||
(and (not is-comments?)
|
(not is-history?)
|
||||||
(not is-history?)
|
is-inspect?
|
||||||
is-inspect?
|
(= current-section :code))
|
||||||
(= current-section :code)))
|
|
||||||
|
|
||||||
{on-pointer-down :on-pointer-down
|
{on-pointer-down :on-pointer-down
|
||||||
on-lost-pointer-capture :on-lost-pointer-capture
|
on-lost-pointer-capture :on-lost-pointer-capture
|
||||||
@ -325,15 +321,16 @@
|
|||||||
[:aside
|
[:aside
|
||||||
{:class (stl/css-case :right-settings-bar true
|
{:class (stl/css-case :right-settings-bar true
|
||||||
:not-expand (not can-be-expanded?)
|
:not-expand (not can-be-expanded?)
|
||||||
:expanded (> width right-sidebar-default-width))
|
:expanded (or is-debug? (> width right-sidebar-default-width)))
|
||||||
|
|
||||||
:id "right-sidebar-aside"
|
:id "right-sidebar-aside"
|
||||||
:data-testid "right-sidebar"
|
:data-testid "right-sidebar"
|
||||||
:data-size (str width)
|
:data-size (str width)
|
||||||
:on-context-menu dom/prevent-default-context-menu
|
:on-context-menu dom/prevent-default-context-menu
|
||||||
:style {:--right-sidebar-width (if can-be-expanded?
|
:style {:--right-sidebar-width (cond
|
||||||
(dm/str width "px")
|
is-debug? (dm/str right-sidebar-default-max-width "px")
|
||||||
(dm/str right-sidebar-default-width "px"))}}
|
can-be-expanded? (dm/str width "px")
|
||||||
|
:else (dm/str right-sidebar-default-width "px"))}}
|
||||||
|
|
||||||
(when can-be-expanded?
|
(when can-be-expanded?
|
||||||
[:div {:class (stl/css :resize-area)
|
[:div {:class (stl/css :resize-area)
|
||||||
@ -348,9 +345,6 @@
|
|||||||
|
|
||||||
[:div {:class (stl/css :settings-bar-inside)}
|
[:div {:class (stl/css :settings-bar-inside)}
|
||||||
(cond
|
(cond
|
||||||
dbg-shape-panel?
|
|
||||||
[:> debug-shape-info*]
|
|
||||||
|
|
||||||
is-comments?
|
is-comments?
|
||||||
[:> comments-sidebar* {}]
|
[:> comments-sidebar* {}]
|
||||||
|
|
||||||
|
|||||||
@ -18,15 +18,28 @@
|
|||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private no-reload-options
|
||||||
|
"Debug options that don't require a page reload to take effect.
|
||||||
|
These options are handled reactively via okulary subscriptions."
|
||||||
|
#{:shape-panel
|
||||||
|
:show-ids
|
||||||
|
:show-touched})
|
||||||
|
|
||||||
(mf/defc debug-panel*
|
(mf/defc debug-panel*
|
||||||
[{:keys [class]}]
|
[{:keys [class]}]
|
||||||
(let [on-toggle-enabled
|
(let [;; dbg/state is an okulary atom; deref'ing it makes this component
|
||||||
|
;; re-render whenever any debug option is toggled, so checkboxes
|
||||||
|
;; reflect the current state without a page reload.
|
||||||
|
_dbg (mf/deref dbg/state)
|
||||||
|
|
||||||
|
on-toggle-enabled
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [event option]
|
(fn [event option]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(dbg/toggle! option)
|
(dbg/toggle! option)
|
||||||
(js* "app.main.reinit(true)")))
|
(when-not (contains? no-reload-options option)
|
||||||
|
(js* "app.main.reinit(true)"))))
|
||||||
|
|
||||||
handle-close
|
handle-close
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.ds.product.panel-title :refer [panel-title*]]
|
|
||||||
[debug :as dbg]
|
[debug :as dbg]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
@ -125,10 +124,6 @@
|
|||||||
(map (d/getf objects)))]
|
(map (d/getf objects)))]
|
||||||
|
|
||||||
[:div {:class (stl/css :shape-info)}
|
[:div {:class (stl/css :shape-info)}
|
||||||
[:> panel-title* {:class (stl/css :shape-info-title)
|
|
||||||
:text "Debug"
|
|
||||||
:on-close #(dbg/disable! :shape-panel)}]
|
|
||||||
|
|
||||||
(if (empty? selected)
|
(if (empty? selected)
|
||||||
[:div {:class (stl/css :attrs-container)} "No shapes selected"]
|
[:div {:class (stl/css :attrs-container)} "No shapes selected"]
|
||||||
(for [[idx current] (d/enumerate selected)]
|
(for [[idx current] (d/enumerate selected)]
|
||||||
|
|||||||
@ -26,7 +26,11 @@
|
|||||||
type-comp type-frame component-id is-hidden is-blocked
|
type-comp type-frame component-id is-hidden is-blocked
|
||||||
variant-id variant-name variant-properties variant-error
|
variant-id variant-name variant-properties variant-error
|
||||||
on-tab-press ref]}]
|
on-tab-press ref]}]
|
||||||
(let [edition* (mf/use-state false)
|
(let [;; Subscribe to dbg/state so the component re-renders when
|
||||||
|
;; :show-ids or :show-touched are toggled without a page reload.
|
||||||
|
_dbg (mf/deref dbg/state)
|
||||||
|
|
||||||
|
edition* (mf/use-state false)
|
||||||
edition? (deref edition*)
|
edition? (deref edition*)
|
||||||
|
|
||||||
local-ref (mf/use-ref)
|
local-ref (mf/use-ref)
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
||||||
[app.main.ui.inspect.right-sidebar :as hrs]
|
[app.main.ui.inspect.right-sidebar :as hrs]
|
||||||
|
[app.main.ui.workspace.sidebar.debug-shape-info :refer [debug-shape-info*]]
|
||||||
[app.main.ui.workspace.sidebar.options.drawing :as drawing]
|
[app.main.ui.workspace.sidebar.options.drawing :as drawing]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.align :refer [align-options*]]
|
[app.main.ui.workspace.sidebar.options.menus.align :refer [align-options*]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options*]]
|
[app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options*]]
|
||||||
@ -38,6 +39,7 @@
|
|||||||
[app.main.ui.workspace.sidebar.options.shapes.rect :as rect]
|
[app.main.ui.workspace.sidebar.options.shapes.rect :as rect]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.svg-raw :as svg-raw]
|
[app.main.ui.workspace.sidebar.options.shapes.svg-raw :as svg-raw]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.text :as text]
|
[app.main.ui.workspace.sidebar.options.shapes.text :as text]
|
||||||
|
[app.util.debug :as dbg]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -199,13 +201,17 @@
|
|||||||
|
|
||||||
[:> hrs/right-sidebar* props]))
|
[:> hrs/right-sidebar* props]))
|
||||||
|
|
||||||
(def ^:private options-tabs
|
(defn- generate-options-tabs
|
||||||
[{:label (tr "workspace.options.design")
|
[]
|
||||||
:id "design"}
|
(cond-> [{:label (tr "workspace.options.design")
|
||||||
{:label (tr "workspace.options.prototype")
|
:id "design"}
|
||||||
:id "prototype"}
|
{:label (tr "workspace.options.prototype")
|
||||||
{:label (tr "workspace.options.inspect")
|
:id "prototype"}
|
||||||
:id "inspect"}])
|
{:label (tr "workspace.options.inspect")
|
||||||
|
:id "inspect"}]
|
||||||
|
(dbg/enabled? :shape-panel)
|
||||||
|
(conj {:label "Debug"
|
||||||
|
:id "debug"})))
|
||||||
|
|
||||||
(defn- on-option-tab-change
|
(defn- on-option-tab-change
|
||||||
[mode]
|
[mode]
|
||||||
@ -225,9 +231,28 @@
|
|||||||
options-mode
|
options-mode
|
||||||
(mf/deref refs/options-mode-global)
|
(mf/deref refs/options-mode-global)
|
||||||
|
|
||||||
|
;; dbg/state is an okulary atom; deref'ing it makes this component
|
||||||
|
;; re-render when debug options change (e.g. :shape-panel toggle)
|
||||||
|
;; so the tabs list is regenerated reactively without a page reload.
|
||||||
|
dbg-state
|
||||||
|
(mf/deref dbg/state)
|
||||||
|
|
||||||
shapes
|
shapes
|
||||||
(mf/with-memo [selected objects]
|
(mf/with-memo [selected objects]
|
||||||
(sequence (keep (d/getf objects)) selected))]
|
(sequence (keep (d/getf objects)) selected))
|
||||||
|
|
||||||
|
options-tabs
|
||||||
|
(generate-options-tabs)]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps dbg-state)
|
||||||
|
(fn []
|
||||||
|
(if (contains? dbg-state :shape-panel)
|
||||||
|
;; shape-panel was just enabled: select the debug tab
|
||||||
|
(on-option-tab-change "debug")
|
||||||
|
;; shape-panel was just disabled: if debug tab is active, go back to design
|
||||||
|
(when (= options-mode :debug)
|
||||||
|
(on-option-tab-change "design")))))
|
||||||
|
|
||||||
[:div {:class (stl/css :tool-window)}
|
[:div {:class (stl/css :tool-window)}
|
||||||
(if (and (:can-edit permissions) (not render-context-lost?))
|
(if (and (:can-edit permissions) (not render-context-lost?))
|
||||||
@ -255,7 +280,10 @@
|
|||||||
:objects objects
|
:objects objects
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:file-id file-id
|
:file-id file-id
|
||||||
:shapes shapes}])]
|
:shapes shapes}]
|
||||||
|
|
||||||
|
:debug
|
||||||
|
[:> debug-shape-info*])]
|
||||||
|
|
||||||
[:div {:class (stl/css :element-options :inspect-options :read-only)}
|
[:div {:class (stl/css :element-options :inspect-options :read-only)}
|
||||||
[:> inspect-tab* {:page-id page-id
|
[:> inspect-tab* {:page-id page-id
|
||||||
|
|||||||
@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
(ns app.util.debug
|
(ns app.util.debug
|
||||||
(:require
|
(:require
|
||||||
[app.main.store :as st]))
|
[app.main.store :as st]
|
||||||
|
[app.util.storage :as storage]
|
||||||
|
[okulary.core :as l]))
|
||||||
|
|
||||||
(defonce state (atom #{#_:events}))
|
(def ^:private storage-key :app.util.debug/enabled-options)
|
||||||
|
|
||||||
(def options
|
(def options
|
||||||
#{;; Displays the bounding box for the shapes
|
#{;; Displays the bounding box for the shapes
|
||||||
@ -105,6 +107,17 @@
|
|||||||
;; Event times
|
;; Event times
|
||||||
:events-times})
|
:events-times})
|
||||||
|
|
||||||
|
(defn- load-state
|
||||||
|
[]
|
||||||
|
(let [stored (get storage/user storage-key #{})]
|
||||||
|
(into #{} (filter options) stored)))
|
||||||
|
|
||||||
|
(defonce state (l/atom (load-state)))
|
||||||
|
|
||||||
|
(defn- persist-state!
|
||||||
|
[state]
|
||||||
|
(swap! storage/user assoc storage-key state))
|
||||||
|
|
||||||
(defn handle-change
|
(defn handle-change
|
||||||
[]
|
[]
|
||||||
(set! st/*debug-events* (contains? @state :events))
|
(set! st/*debug-events* (contains? @state :events))
|
||||||
@ -112,7 +125,9 @@
|
|||||||
|
|
||||||
(when *assert*
|
(when *assert*
|
||||||
(handle-change)
|
(handle-change)
|
||||||
(add-watch state :watcher handle-change))
|
(add-watch state :watcher handle-change)
|
||||||
|
(add-watch state :persistence (fn [_ _ _ new-state]
|
||||||
|
(persist-state! new-state))))
|
||||||
|
|
||||||
(defn enable!
|
(defn enable!
|
||||||
[option]
|
[option]
|
||||||
|
|||||||
@ -8779,6 +8779,9 @@ msgstr "Copy as CSS (nested layers)"
|
|||||||
msgid "workspace.shape.menu.copy-link"
|
msgid "workspace.shape.menu.copy-link"
|
||||||
msgstr "Copy link"
|
msgstr "Copy link"
|
||||||
|
|
||||||
|
msgid "workspace.shape.menu.copy-id"
|
||||||
|
msgstr "Copy id"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/context_menu.cljs:219
|
#: src/app/main/ui/workspace/context_menu.cljs:219
|
||||||
msgid "workspace.shape.menu.copy-paste-as"
|
msgid "workspace.shape.menu.copy-paste-as"
|
||||||
msgstr "Copy/Paste as ..."
|
msgstr "Copy/Paste as ..."
|
||||||
|
|||||||
@ -8551,6 +8551,9 @@ msgstr "Copiar como CSS (capas anidadas)"
|
|||||||
msgid "workspace.shape.menu.copy-link"
|
msgid "workspace.shape.menu.copy-link"
|
||||||
msgstr "Copiar enlace"
|
msgstr "Copiar enlace"
|
||||||
|
|
||||||
|
msgid "workspace.shape.menu.copy-id"
|
||||||
|
msgstr "Copiar id"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/context_menu.cljs:219
|
#: src/app/main/ui/workspace/context_menu.cljs:219
|
||||||
msgid "workspace.shape.menu.copy-paste-as"
|
msgid "workspace.shape.menu.copy-paste-as"
|
||||||
msgstr "Copiar/Pegar como ..."
|
msgstr "Copiar/Pegar como ..."
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user