From 2e68dc76a41165d16b35d6eb2e7bf13aafed9aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Thu, 2 Oct 2025 14:37:03 -0400 Subject: [PATCH] [DevTools] Give a distinct color to the root (#34690) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stacked on #34654. The root is special since it represents "Initial Paint" (or a "Transition" when an Activity is selected). This gives it a different color in the timeline as well as gives it an outline that's clickable. Hovering the timeline now shows "Initial Paint" or "Suspense". Also made the cursor a pointer to invite you to try to click things and some rounded corners. Screenshot 2025-10-02 at 1 26 38 PM Screenshot 2025-10-02 at 1 26 54 PM Screenshot 2025-10-02 at 1 27 24 PM Screenshot 2025-10-02 at 1 27 10 PM --- .../views/SuspenseTab/SuspenseRects.css | 11 ++++- .../views/SuspenseTab/SuspenseRects.js | 8 +++- .../views/SuspenseTab/SuspenseScrubber.css | 10 +++-- .../views/SuspenseTab/SuspenseScrubber.js | 44 +++++++++++++------ 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css index 7ad3152496..ba862051d9 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css @@ -1,5 +1,12 @@ .SuspenseRectsContainer { padding: .25rem; + cursor: pointer; + outline: 1px solid var(--color-component-name); + border-radius: 0.25rem; +} + +.SuspenseRectsContainer[data-highlighted='true'] { + background: var(--color-dimmest); } .SuspenseRectsViewBox { @@ -28,6 +35,8 @@ pointer-events: all; outline-style: solid; outline-width: 1px; + border-radius: 0.125rem; + cursor: pointer; } .SuspenseRectsScaledRect { @@ -42,7 +51,7 @@ /* highlight this boundary */ .SuspenseRectsBoundary:hover:not(:has(.SuspenseRectsBoundary:hover)) > .SuspenseRectsRect, .SuspenseRectsBoundary[data-highlighted='true'] > .SuspenseRectsRect { - background-color: var(--color-background-hover); + background-color: var(--color-background-hover); } .SuspenseRectsRect[data-highlighted='true'] { diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js index cd77a7a62c..8e43944e7a 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js @@ -295,6 +295,7 @@ const ViewBox = createContext((null: any)); function SuspenseRectsContainer(): React$Node { const store = useContext(StoreContext); + const {inspectedElementID} = useContext(TreeStateContext); const treeDispatch = useContext(TreeDispatcherContext); const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext); // TODO: This relies on a full re-render of all children when the Suspense tree changes. @@ -329,8 +330,13 @@ function SuspenseRectsContainer(): React$Node { }); } + const isRootSelected = roots.includes(inspectedElementID); + return ( -
+
.SuspenseScrubberBead, -.SuspenseScrubberStepHighlight > .SuspenseScrubberBeadSelected, -.SuspenseScrubberStep:hover > .SuspenseScrubberBead, -.SuspenseScrubberStep:hover > .SuspenseScrubberBeadSelected { +.SuspenseScrubberStep:hover > .SuspenseScrubberBead { height: 0.75rem; } diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js index cbb76e4164..53d20b6467 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js @@ -14,6 +14,8 @@ import {useRef} from 'react'; import styles from './SuspenseScrubber.css'; +import Tooltip from '../Components/reach-ui/tooltip'; + export default function SuspenseScrubber({ min, max, @@ -53,24 +55,38 @@ export default function SuspenseScrubber({ const steps = []; for (let index = min; index <= max; index++) { steps.push( -
+ label={ + index === min + ? // The first step in the timeline is always a Transition (Initial Paint). + // TODO: Support multiple environments. + 'Initial Paint' + : // TODO: Consider adding the name of this specific boundary if this step has only one. + 'Suspense' + }>
-
, + onPointerDown={handlePress.bind(null, index)} + onMouseEnter={onHoverSegment.bind(null, index)}> +
+
+ , ); }