Add new debugger tool components-debugger (#10757)

This commit is contained in:
Pablo Alba 2026-07-22 10:49:18 +02:00 committed by GitHub
parent f9439d2942
commit 3a0aca52c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1526 additions and 2 deletions

View File

@ -0,0 +1,105 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL
(ns app.main.ui.hooks.floating-drag
"Pointer drag hook for floating panels, mirroring the plugin modal drag
handler in plugins-runtime."
(:require
[app.common.geom.point :as gpt]
[app.util.dom :as dom]
[rumext.v2 :as mf]))
(defn- parse-translate
"Reads the current translate offset from an element's computed transform."
[^js el]
(if (and el (.-DOMMatrixReadOnly js/window))
(let [cs (.getComputedStyle js/window el nil)
matrix (js/DOMMatrixReadOnly. (.-transform cs))]
{:x (.-m41 matrix)
:y (.-m42 matrix)})
{:x 0 :y 0}))
(defn- set-dragging-class!
"Toggle `dragging-class` on `target` while a drag is active."
[^js target dragging-class dragging?]
(when (and target dragging-class)
(if dragging?
(dom/add-class! target dragging-class)
(dom/remove-class! target dragging-class))))
(defn use-floating-drag
"Returns pointer handlers to drag `target-ref` by its header.
Optional `on-move` is called on pointer down (e.g. to raise z-index).
Optional `dragging-class` is toggled on `target-ref` while dragging."
([target-ref]
(use-floating-drag target-ref nil nil))
([target-ref on-move]
(use-floating-drag target-ref on-move nil))
([target-ref on-move dragging-class]
(let [dragging-ref (mf/use-ref false)
pointer-id-ref (mf/use-ref nil)
initial-translate-ref (mf/use-ref {:x 0 :y 0})
initial-client-ref (mf/use-ref (gpt/point 0 0))
end-drag
(mf/use-fn
(mf/deps dragging-class)
(fn [event]
(when (mf/ref-val dragging-ref)
(mf/set-ref-val! dragging-ref false)
(mf/set-ref-val! pointer-id-ref nil)
(set-dragging-class! (mf/ref-val target-ref) dragging-class false)
(when event (dom/release-pointer event)))))
handle-lost-pointer-capture
(mf/use-fn
(fn [event]
(end-drag event)))
handle-pointer-up
(mf/use-fn
(fn [event]
(when (= (.-pointerId event) (mf/ref-val pointer-id-ref))
(end-drag event))))
handle-pointer-move
(mf/use-fn
(fn [event]
(when (and (mf/ref-val dragging-ref)
(= (.-pointerId event) (mf/ref-val pointer-id-ref)))
(let [target (mf/ref-val target-ref)
start (mf/ref-val initial-client-ref)
pos (dom/get-client-position event)
{:keys [x y]} (mf/ref-val initial-translate-ref)
delta-x (+ x (- (:x pos) (:x start)))
delta-y (+ y (- (:y pos) (:y start)))]
(when target
(dom/set-css-property! target "transform"
(str "translate(" delta-x "px, " delta-y "px)")))))))
handle-pointer-down
(mf/use-fn
(mf/deps on-move dragging-class)
(fn [event]
(when (and (= (.-button event) 0)
(not (and (instance? js/Element (.-target event))
(.closest (.-target event) "button"))))
(dom/prevent-default event)
(let [target (mf/ref-val target-ref)]
(when target
(mf/set-ref-val! pointer-id-ref (.-pointerId event))
(mf/set-ref-val! initial-client-ref (dom/get-client-position event))
(mf/set-ref-val! initial-translate-ref (parse-translate target))
(mf/set-ref-val! dragging-ref true)
(set-dragging-class! target dragging-class true)
(dom/capture-pointer event)
(when on-move (on-move)))))))]
{:on-pointer-down handle-pointer-down
:on-pointer-move handle-pointer-move
:on-pointer-up handle-pointer-up
:on-lost-pointer-capture handle-lost-pointer-capture})))

View File

@ -25,6 +25,7 @@
[app.main.ui.hooks.resize :refer [use-resize-observer]]
[app.main.ui.modal :refer [modal-container*]]
[app.main.ui.workspace.colorpicker]
[app.main.ui.workspace.components-debugger :refer [components-debugger*]]
[app.main.ui.workspace.context-menu :refer [context-menu*]]
[app.main.ui.workspace.coordinates :as coordinates]
[app.main.ui.workspace.libraries]
@ -271,6 +272,7 @@
[:> (mf/provider ctx/design-tokens) {:value design-tokens?}
[:> (mf/provider ctx/workspace-read-only?) {:value read-only?}
[:> modal-container*]
[:> components-debugger*]
[:section {:class (stl/css :workspace)
:style {:background-color background-color
:touch-action "none"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,224 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC Sucursal en España SL
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "ds/typography.scss" as t;
.wrapper {
position: fixed;
padding: var(--sp-s);
border-radius: $br-8;
border: $b-2 solid var(--color-background-quaternary);
box-shadow: 0 0 var(--sp-s) 0 var(--color-shadow-light);
overflow: hidden;
min-inline-size: $sz-24;
min-block-size: $sz-200;
max-inline-size: 90vw;
max-block-size: 90vh;
resize: both;
user-select: none;
background-color: var(--color-background-primary);
color: var(--color-foreground-secondary);
z-index: var(--z-index-set);
}
.inner {
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
block-size: 100%;
padding: var(--sp-s);
}
.header {
align-items: center;
display: flex;
flex-shrink: 0;
justify-content: space-between;
border-block-end: $b-1 solid var(--color-background-quaternary);
padding-block-end: var(--sp-xxs);
cursor: grab;
touch-action: none;
}
.wrapper.is-dragging .header {
cursor: grabbing;
}
.title {
@include t.use-typography("body-small");
font-weight: var(--font-weight-medium);
margin: 0;
margin-inline-end: var(--sp-xxs);
}
.canvas {
flex: 1;
min-block-size: 0;
overflow: auto;
padding-block-start: var(--sp-s);
}
.canvas-svg {
display: block;
min-block-size: 100%;
min-inline-size: 100%;
}
.selection-preview-box {
--stroke-color: var(--color-background-quaternary);
fill: var(--color-background-primary);
stroke: var(--stroke-color);
stroke-width: $b-2;
&.selected {
--stroke-color: var(--color-accent-secondary);
}
&.swap {
--stroke-color: var(--color-accent-success);
}
&.deleted {
--stroke-color: var(--color-accent-error);
}
}
.selection-preview-icon {
--icon-stroke-color: var(--color-foreground-secondary);
display: block;
&.component {
--icon-stroke-color: var(--color-accent-secondary);
}
}
.selection-preview-text {
@include t.use-typography("body-small");
fill: var(--color-foreground-primary);
user-select: text;
}
.deleted-header-bg {
fill: var(--color-accent-error);
}
.deleted-header-text {
@include t.use-typography("body-small");
fill: var(--color-static-white);
font-weight: var(--font-weight-medium);
}
.info-labels-bg {
fill: var(--color-background-quaternary);
}
.info-text {
@include t.use-typography("body-small");
fill: var(--color-foreground-primary);
text-anchor: start;
user-select: text;
}
.shape-thumbnail-bg {
fill: var(--color-background-primary);
stroke: var(--color-background-quaternary);
stroke-width: $b-1;
&.is-loading {
animation: thumbnail-loading-pulse 1.2s ease-in-out infinite;
}
&.unavailable {
fill: var(--color-background-quaternary);
}
}
.shape-thumbnail-image {
opacity: 0.95;
}
.shape-thumbnail-loading {
@include t.use-typography("body-small");
fill: var(--color-foreground-secondary);
pointer-events: none;
}
.shape-thumbnail-unavailable {
@include t.use-typography("body-small");
fill: var(--color-foreground-secondary);
pointer-events: none;
}
@keyframes thumbnail-loading-pulse {
0%,
100% {
fill: var(--color-background-primary);
}
50% {
fill: var(--color-background-quaternary);
}
}
.highlight-arrows {
pointer-events: none;
}
.highlight-arrow {
stroke: var(--color-accent-secondary);
stroke-width: $b-2;
}
.highlight-arrow-marker-circle {
fill: var(--color-accent-secondary);
}
.highlight-arrow-marker-icon {
fill: var(--color-accent-secondary);
stroke: var(--color-background-primary);
stroke-width: $b-2;
}
.swap-arrow {
stroke: var(--color-accent-success);
stroke-width: $b-2;
}
.swap-arrow-marker-circle {
fill: var(--color-accent-success);
}
.swap-arrow-marker-icon {
fill: var(--color-accent-success);
stroke: var(--color-background-primary);
stroke-width: $b-2;
}
.legend-text {
@include t.use-typography("body-small");
fill: var(--color-foreground-primary);
}
.legend-bg {
fill: var(--color-background-quaternary);
}
::-webkit-resizer {
display: none;
}

View File

@ -23,7 +23,8 @@
These options are handled reactively via okulary subscriptions."
#{:shape-panel
:show-ids
:show-touched})
:show-touched
:components-debugger})
(mf/defc debug-panel*
[{:keys [class]}]

View File

@ -27,7 +27,7 @@
variant-id variant-name variant-properties variant-error
on-tab-press ref]}]
(let [;; Subscribe to dbg/state so the component re-renders when
;; :show-ids or :show-touched are toggled without a page reload.
;; debug options are toggled without a page reload.
_dbg (mf/deref dbg/state)
edition* (mf/use-state false)

View File

@ -89,6 +89,9 @@
;; Show info about shapes
:shape-panel
;; Show the floating components debugger window
:components-debugger
;; Show what is touched in copies
:display-touched