Improve workspace debug sidebar workflow (#10678)

This commit is contained in:
Pablo Alba 2026-07-14 12:42:19 +02:00 committed by GitHub
parent c11d3aaaa7
commit 95cfbf5f7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 109 additions and 38 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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* {}]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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