Commit Graph

306 Commits

Author SHA1 Message Date
Sebastian "Sebbie" Silbermann
fbf29ccaa3 [devtools] Restore "double-click to view owners tree" functionality (#33039) 2025-04-30 11:11:33 +02:00
Jorge (Hezi) Cohen
707b3fc6b2 [DevTools] Make Toggle hover state more visible (#32914)
This change adds a background color to Toggles to make them easier to
see. This is especially important when DevTools are not in focus, and
it's harder to see.

Test plan:
1. `yarn build:chrome:local`
2. Inspect components 
3. Hover over "Select an Element in page to inspect it"
4. Observe background change
2025-04-15 11:20:29 +01:00
Piotr Tomczewski
7ff4d057b6 [DevTools] feat: show changed hooks names in the Profiler tab (#31398)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

This PR adds support for displaying the names of changed hooks directly
in the Profiler tab, making it easier to identify specific updates.

A `HookChangeSummary` component has been introduced to show these hook
names, with a `displayMode` prop that toggles between `“compact”` for
tooltips and `“detailed”` for more in-depth views. This keeps tooltip
summaries concise while allowing for a full breakdown where needed.

This functionality also respects the `“Always parse hook names from
source”` setting from the Component inspector, as it uses the same
caching mechanism already in place for the Components tab. Additionally,
even without hook names parsed, the Profiler will now display hook types
(like `State`, `Callback`, etc.) based on data from `inspectedElement`.

To enable this across the DevTools, `InspectedElementContext` has been
moved higher in the component tree, allowing it to be shared between the
Profiler and Components tabs. This update allows hook name data to be
reused across tabs without duplication.

Additionally, a `getAlreadyLoadedHookNames` helper function was added to
efficiently access cached hook names, reducing the need for repeated
fetching when displaying changes.

These changes improve the ability to track specific hook updates within
the Profiler tab, making it clearer to see what’s changed.

### Before
Previously, the Profiler tab displayed only the IDs of changed hooks, as
shown below:
<img width="350" alt="Screenshot 2024-11-01 at 12 02 21_cropped"
src="https://github.com/user-attachments/assets/7a5f5f67-f1c8-4261-9ba3-1c76c9a88af3">

### After (without hook names parsed)
When hook names aren’t parsed, custom hooks and hook types are displayed
based on the inspectedElement data:
<img width="350" alt="Screenshot 2024-11-01 at 12 03 09_cropped"
src="https://github.com/user-attachments/assets/ed857a6d-e6ef-4e5b-982c-bf30c2d8a7e2">

### After (with hook names parsed)
Once hook names are fully parsed, the Profiler tab provides a complete
breakdown of specific hooks that have changed:
<img width="350" alt="Screenshot 2024-11-01 at 12 03 14_cropped"
src="https://github.com/user-attachments/assets/1ddfcc35-7474-4f4d-a084-f4e9f993a5bf">

This should resolve #21856 🎉
2025-04-15 11:10:00 +01:00
Ruslan Lesiutin
f0c767e2a2 feat[devtools]: display native tag for host components for Native (#32762)
Native only. Displays the native tag for Native Host components inside a
badge, when user inspects the component.

Only displaying will be supported for now, because in order to get
native tags indexable, they should be part of the bridge operations,
which is technically a breaking change that requires significantly more
time investment.

The text will only be shown when user hovers over the badge.
![Screenshot 2025-03-26 at 19 46
40](https://github.com/user-attachments/assets/787530cf-c5e5-4b85-8e2a-15b006a3d783)
2025-04-02 22:44:38 +01:00
MU AOHUA
dc9b74647e [DevTools] Add fb local build command (#32644)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary
1. Having a development build for FB will be convenient for fb internal
feature development
2. Add a new checkbox to toggle new internal features added to React
Devtools.

## How did you test this change?
1. yarn test
2. set extra env variables in bash profile and build an internal version
with the new script.
3. toggle on/off the new checkbox, the value is stored in local storage
correctly.

---------

Co-authored-by: Aohua Mu <muaohua@fb.com>
2025-03-25 13:45:48 +00:00
Ricky
fbcda19a23 [devtools] add filters for internal builds (#32646)
We don't have an experimental-only build of devtools, but we can at
least add these filters to the internal build.

A better way would be to use feature detection, but I'm not sure how and
this isn't a very heavily used feautre.
2025-03-17 12:14:19 -04:00
Ricky
1a191701fe [refactor] Add element type for Activity (#32499)
This PR separates Activity to it's own element type separate from
Offscreen. The goal is to allow us to add Activity element boundary
semantics during hydration similar to Suspense semantics, without
impacting the Offscreen behavior in suspended children.
2025-03-17 09:17:00 -04:00
Ruslan Lesiutin
d85cf3e5ab DevTools: refactor NativeStyleEditor, don't use custom cache implementation (#32298)
We have this really old (5+ years) feature for inspecting native styles
of React Native Host components.

We also have a custom Cache implementation in React DevTools, which was
forked from React at some point. We know that this should be removed,
but it spans through critical parts of the application, like fetching
and caching inspected element.

Before this PR, this was also used for caching native style and layouts
of RN Host components. This approach is out of date, and was based on
the presence of Suspense boundary around inspected element View, which
we have removed to speed up element inspection -
https://github.com/facebook/react/pull/30555.
 
Looks like I've introduced a regression in
https://github.com/facebook/react/pull/31956:
- Custom Cache implementation will throw thenables and suspend.
- Because of this, some descendant Suspense boundaries will not resolve
for a long time, and React will throw an error
https://react.dev/errors/482.

I've switched from a usage of this custom Cache implementation to a
naive fetching in effect and keeping the layout and style in a local
state of a Context, which will be propagated downwards. The race should
be impossible, this is guaranteed by the mechanism for queueing messages
through microtasks queue.

The only downside is the UI. If you quickly switch between 2 elements,
and one of them has native style, while the other doesn't, UI will feel
jumpy. We can address this later with a Suspense boundary, if needed.
2025-02-05 12:52:48 +00:00
Ruslan Lesiutin
1f0b03ced0 DevTools: fix host component filter option title (#32296)
Overlook that in https://github.com/facebook/react/pull/32086, because
of ternany, it is already string literals, so html entities names no
longer needed.
2025-02-03 09:59:47 +00:00
Ruslan Lesiutin
221f3002ca chore[DevTools]: make clipboardWrite optional for chromium (#32262)
Addresses https://github.com/facebook/react/issues/32244.

### Chromium
We will use
[chrome.permissions](https://developer.chrome.com/docs/extensions/reference/api/permissions)
for checking / requesting `clipboardWrite` permission before copying
something to the clipboard.

### Firefox
We will keep `clipboardWrite` as a required permission, because there is
no reliable and working API for requesting optional permissions for
extensions that are extending browser DevTools:
- `chrome.permissions` is unavailable for devtools pages -
https://bugzilla.mozilla.org/show_bug.cgi?id=1796933
- You can't call `chrome.permissions.request` from background, because
this instruction has to be executed inside user-event callback,
basically only initiated by user.

I don't really want to come up with solutions like opening a new tab
with a button that user has to click.
2025-01-30 20:08:17 +00:00
Ruslan Lesiutin
89dbd487fc fix[DevTools]: fix HostComponent naming in filters for Native (#32086)
Right now we mention DOM elements as Host elements for React Native,
which doesn't make sense.
2025-01-16 14:17:06 +00:00
Ruslan Lesiutin
b158439a5b chore[DevTools]: don't use batchedUpdate (#32074)
It is no-op for concurrent mode now and React DevTools is using
experimental version of React:

886c5ad936/packages/react-dom/src/shared/ReactDOM.js (L51-L54)


540efebcc3/packages/react-reconciler/src/ReactFiberWorkLoop.js (L1646-L1651)
2025-01-15 14:32:33 +00:00
Szymon Chmal
453f505256 [DevTools] Prevent crash when starting consecutive profiling sessions (#32066)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

This pull request resolves an issue where consecutive profiling sessions
would cause Dev Tools to freeze due to an infinite loop of state
updates. The problem occurs when the startProfiling function triggers a
call to [`selectCommitIndex(0)` in
SnapshotSelector](b3a95caf61/packages/react-devtools-shared/src/devtools/views/Profiler/SnapshotSelector.js (L77-L85))
as previous profiling data is available, which causes a re-render. Then,
[ProfilerContextProvider calls
`selectCommitIndex(null)`](b3a95caf61/packages/react-devtools-shared/src/devtools/views/Profiler/ProfilerContext.js (L231-L241))
to clear the view while profiling is in progress, leading to another
re-render and creating an infinite loop. This behavior was prevented by
clearing the existing profiling data before starting a new session.

Closes #31977
Closes #31679

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

I ran the Dev Tools locally following [the contributing
guideline](b3a95caf61/packages/react-devtools/CONTRIBUTING.md).
I observed the freeze at the start of the second profiling session.
Then, I modified the code to clear the store when starting a new session
and ran the Dev Tools again. This time, no freeze was observed.

Before:

https://github.com/user-attachments/assets/9d790f84-f6d0-4951-8202-e599cf8d225b

After:

https://github.com/user-attachments/assets/af097019-0b8f-49dd-8afc-0f6cd72af787
2025-01-14 20:23:42 +00:00
Ruslan Lesiutin
d2a1b8854d fix[DevTools/Tree]: only scroll to item when panel is visible (#32018)
Stacked on https://github.com/facebook/react/pull/31968. See commit on
top.

Fixes an issue with bank tree view, when we are scrolling to an item
while syncing user DOM selection. This should only have an effect on
browser extension. Added events with `extension` prefix will only be
emitted in browser extension implementation, for other implementations
`useExtensionComponentsPanelVisibility` will return constant `true`
value.

Before:


https://github.com/user-attachments/assets/82667c16-d495-4346-af0a-7ed22ff89cfc


After:


https://github.com/user-attachments/assets/a5d223fd-0328-44f0-af68-5c3863f1efee
2025-01-09 18:38:49 +00:00
Ruslan Lesiutin
f2813ee33d [DevTools] feat[Tree]: set initial scroll offset when inspected element index is set (#31968)
Stacked on https://github.com/facebook/react/pull/31956. See [commit on
top](ecb8df4175).

Use `initialScrollOffset` prop for `FixedSizeList` from `react-window`.
This happens when user selects an element in built-in Elements panel in
DevTools, and then opens Components panel from React DevTools - elements
will be synced and corresponding React Element will be pre-selected, we
just have to scroll to its position now.
2025-01-09 18:21:55 +00:00
Ruslan Lesiutin
d634548243 DevTools: merge element fields in TreeStateContext (#31956)
Stacked on https://github.com/facebook/react/pull/31892, see commit on
top.

For some reason, there were 2 fields different fields for essentially
same thing: `selectedElementID` and `inspectedElementID`. Basically, the
change is:
```
selectedElementID -> inspectedElementID
selectedElementIndex -> inspectedElementIndex
```

I have a theory that it was due to previously used async approach around
element inspection, and the whole `InspectedElementView` was wrapped in
`Suspense`.
2025-01-09 18:13:24 +00:00
Ruslan Lesiutin
54cfa95d3a DevTools: fix initial host instance selection (#31892)
Related: https://github.com/facebook/react/pull/31342

This fixes RDT behaviour when some DOM element was pre-selected in
built-in browser's Elements panel, and then Components panel of React
DevTools was opened for the first time. With this change, React DevTools
will correctly display the initial state of the Components Tree with the
corresponding React Element (if possible) pre-selected.

Previously, we would only subscribe listener when `TreeContext` is
mounted, but this only happens when user opens one of React DevTools
panels for the first time. With this change, we keep state inside
`Store`, which is created when Browser DevTools are opened. Later,
`TreeContext` will use it for initial state value.

Planned next changes:
1. Merge `inspectedElementID` and `selectedElementID`, I have no idea
why we need both.
2. Fix issue with `AutoSizer` rendering a blank container.
2025-01-09 18:01:07 +00:00
Ruslan Lesiutin
d5f3c50f58 chore[DevTools/Tree]: don't pre-select root element and remove unused code (#32015)
In this PR:
1. Removed unused code in `Tree.js`
2. Removed logic for pre-selecting first element in the tree by default.
This is a bit clowny, because it steals focus and resets scroll, when
user attempts to expand / collapse some subtree.
3. Updated comments around
1c381c588a.

To expand on 3-rd point, for someone who might be reading this in the
future:
We can't guarantee focus of RDT browser extension panels, because they
are hosted in an `iframe`. Attempting to fire any events won't have any
result, user action with the corresponding `iframe` is required in order
for this `iframe` to obtain focus.

The only reason why built-in Elements panel in Chrome works correctly is
because it is supported natively somewhere in Chrome / Chrome DevTools.
Also, when you select an element on the application page, Chrome will
make sure that Elements panel opened, which technically guarantees focus
inside DevTools window and Elements panel subview.

As of today, we can't navigate user to third-party extensions panels,
there is no API for this, hence no ability to guarantee focused RDT
panels.
2025-01-09 18:00:30 +00:00
Ruslan Lesiutin
79dcc47191 chore[DevTools/TraceUpdates]: display names by default (#32019)
Feature was added in https://github.com/facebook/react/pull/31577, lets
enable it by default. Note: for gradual rollout with React Native, we
will continue to emit different event, requires some changes on React
Native side to support this.

I have plans to make this feature to be accessible via browser context
menu, which has really limited API. In order to minimize potential
divergence, lets make this the default state for the feature.
2025-01-09 18:00:09 +00:00
Piotr Tomczewski
a7b829524b [DevTools] Show component names while highlighting renders (#31577)
## Summary
This PR improves the Trace Updates feature by letting developers see
component names directly on the update overlay. Before this change, the
overlay only highlighted updated regions, leaving it unclear which
components were involved. With this update, you can now match visual
updates to their corresponding components, making it much easier to
debug rendering performance.

### New Feature: Show component names while highlighting
When the new **"Show component names while highlighting"** setting is
enabled, the update overlay display the names of affected components
above the rectangles, along with the update count. This gives immediate
context about what’s rendering and why. The preference is stored in
local storage and synced with the backend, so it’s remembered across
sessions.

### Improvements to Drawing Logic
The drawing logic has been updated to make the overlay sharper and
easier to read. Overlay now respect device pixel ratios, so they look
great on high-DPI screens. Outlines have also been made crisper, which
makes it easier to spot exactly where updates are happening.

> [!NOTE]
> **Grouping Logic and Limitations**
> Updates are grouped by their screen position `(left, top coordinates)`
to combine overlapping or nearby regions into a single group. Groups are
sorted by the highest update count within each group, making the most
frequently updated components stand out.
> Overlapping labels may still occur when multiple updates involve
components that overlap but are not in the exact same position. This is
intentional, as the logic aims to maintain a straightforward mapping
between update regions and component names without introducing
unnecessary complexity.

### Testing
This PR also adds tests for the new `groupAndSortNodes` utility, which
handles the logic for grouping and sorting updates. The tests ensure the
behavior is reliable across different scenarios.

## Before & After


https://github.com/user-attachments/assets/6ea0fe3e-9354-44fa-95f3-9a867554f74c


https://github.com/user-attachments/assets/32af4d98-92a5-47dd-a732-f05c2293e41b

---------

Co-authored-by: Ruslan Lesiutin <rdlesyutin@gmail.com>
2024-12-13 11:53:05 +00:00
Sam Zhou
45804af18d [flow] Eliminate usage of more than 1-arg React.AbstractComponent in React codebase (#31314)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

In order to adopt react 19's ref-as-prop model, Flow needs to eliminate
all the places where they are treated differently.
`React.AbstractComponent` is the worst example of this, and we need to
eliminate it.

This PR eliminates them from the react repo, and only keeps the one that
has 1 argument of props.

## How did you test this change?

yarn flow
2024-10-21 16:17:41 -07:00
Ruslan Lesiutin
d5bba18b5d fix[react-devtools]: record timeline data only when supported (#31154)
Stacked on https://github.com/facebook/react/pull/31132. See last
commit.

There are 2 issues:
1. We've been recording timeline events, even if Timeline Profiler was
not supported by the Host. We've been doing this for React Native, for
example, which would significantly regress perf of recording a profiling
session, but we were not even using this data.
2. Currently, we are generating component stack for every state update
event. This is extremely expensive, and we should not be doing this.

We can't currently fix the second one, because we would still need to
generate all these stacks, and this would still take quite a lot of
time. As of right now, we can't generate a component stack lazily
without relying on the fact that reference to the Fiber is not stale.
With `enableOwnerStacks` we could populate component stacks in some
collection, which would be cached at the Backend, and then returned only
once Frontend asks for it. This approach also eliminates the need for
keeping a reference to a Fiber.
2024-10-09 15:27:04 +01:00
Ruslan Lesiutin
dbf80c8d7a fix[react-devtools]: update profiling status before receiving response from backend (#31117)
We can't wait for a response from Backend, because it might take some
time to actually finish profiling.

We should keep a flag on the frontend side, so user can quickly see the
feedback in the UI.
2024-10-09 13:23:23 +01:00
Ruslan Lesiutin
bf0c054649 fix[react-devtools]: wrap key string in preformatted text html element (#31153)
Fixes https://github.com/facebook/react/issues/28984.
2024-10-09 09:54:34 +01:00
Edmond Chui
f8024b0686 refactor: allow custom impl of backend realod-to-profile support check (#31048)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

In preparation to support reload-to-profile in Fusebox (#31021), we need
a way to check capability of different backends, e.g. web vs React
Native.

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

* Default, e.g. existing web impl = no-op
* Custom impl: is called
2024-09-26 12:39:28 +01:00
Ruslan Lesiutin
a15bbe1475 refactor: data source for errors and warnings tracking is now in Store (#31010)
Stacked on https://github.com/facebook/react/pull/31009.

1. Instead of keeping `showInlineWarningsAndErrors` in `Settings`
context (which was removed in
https://github.com/facebook/react/pull/30610), `Store` will now have a
boolean flag, which controls if the UI should be displaying information
about errors and warnings.
2. The errors and warnings counters in the Tree view are now counting
only unique errors. This makes more sense, because it is part of the
Elements Tree view, so ideally it should be showing number of components
with errors and number of components of warnings. Consider this example:
2.1. Warning for element `A` was emitted once and warning for element
`B` was emitted twice.
2.2. With previous implementation, we would show `3 ⚠️`, because in
total there were 3 warnings in total. If user tries to iterate through
these, it will only take 2 steps to do the full cycle, because there are
only 2 elements with warnings (with one having same warning, which was
emitted twice).
2.3 With current implementation, we would show `2 ⚠️`. Inspecting the
element with doubled warning will still show the warning counter (2)
before the warning message.

With these changes, the feature correctly works.
https://fburl.com/a7fw92m4
2024-09-24 19:51:21 +01:00
Edmond Chui
5d19e1c8d1 Fix: profiling crashes #30661 #28838 (#31024)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Profiling fails sometimes because `onProfilingStatus` is called
repeatedly on some occasions, e.g. multiple calls to
`getProfilingStatus`.

Subsequent calls should be a no-op if the profiling status hasn't
changed.

Reported via #30661 #28838.

> [!TIP]
> Hide whitespace changes on this PR

<img width="328" alt="screenshot showing the UI controls for hiding
whitespace changes on GitHub"
src="https://github.com/user-attachments/assets/036385cf-2610-4e69-a717-17c05d7ef047">


## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

Tested as part of Fusebox implementation of reload-to-profile.

https://github.com/facebook/react/pull/31021?#discussion_r1770589753
2024-09-23 13:25:44 +01:00
Ruslan Lesiutin
e740d4b14b chore: remove using local storage for persisting console settings on the frontend (#31002)
After https://github.com/facebook/react/pull/30636 and
https://github.com/facebook/react/pull/30986 we no longer store settings
on the Frontend side via `localStorage`.

This PR removes all occurrences of it from
`react-devtools-core/standalone` and `react-devtools-inline`.
2024-09-19 15:47:25 +01:00
Ruslan Lesiutin
f37c7bc653 feat[react-devtools/extension]: use chrome.storage to persist settings across sessions (#30636)
Stacked on https://github.com/facebook/react/pull/30610 and whats under
it. See [last
commit](248ddba186).

Now, we are using
[`chrome.storage`](https://developer.chrome.com/docs/extensions/reference/api/storage)
to persist settings for the browser extension across different sessions.
Once settings are updated from the UI, the `Store` will emit
`settingsUpdated` event, and we are going to persist them via
`chrome.storage.local.set` in `main/index.js`.

When hook is being injected, we are going to pass a `Promise`, which is
going to be resolved after the settings are read from the storage via
`chrome.storage.local.get` in `hookSettingsInjector.js`.
2024-09-18 18:26:39 +01:00
Ruslan Lesiutin
e33acfd67f refactor[react-devtools]: propagate settings from global hook object to frontend (#30610)
Stacked on https://github.com/facebook/react/pull/30597 and whats under
it. See [this
commit](59b4efa723).

With this change, the initial values for console patching settings are
propagated from hook (which is the source of truth now, because of
https://github.com/facebook/react/pull/30596) to the UI. Instead of
reading from `localStorage` the frontend is now requesting it from the
hook. This happens when settings modal is rendered, and wrapped in a
transition. Also, this is happening even if settings modal is not opened
yet, so we have enough time to fetch this data without displaying loader
or similar UI.
2024-09-18 18:19:01 +01:00
Ruslan Lesiutin
b521ef8a2a refactor[react-devtools]: remove browserTheme from ConsolePatchSettings (#30566)
Stacked on https://github.com/facebook/react/pull/30564.

We are no longer using browser theme in our console patching, this was
removed in unification of console patching for strict mode, we started
using ansi escape symbols and forking based on browser theme is no
longer required - https://github.com/facebook/react/pull/29869

The real browser theme initialization for frontend is happening at the
other place and is not affected:

40be968257/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js (L117-L120)
2024-09-18 18:02:13 +01:00
Ruslan Lesiutin
bb6b86ed59 refactor[react-devtools]: initialize renderer interface early (#30946)
The current state is that `rendererInterface`, which contains all the
backend logic, like generating component stack or attaching errors to
fibers, or traversing the Fiber tree, ..., is only mounted after the
Frontend is created.

For browser extension, this means that we don't patch console or track
errors and warnings before Chrome DevTools is opened.

With these changes, `rendererInterface` is created right after
`renderer` is injected from React via global hook object (e. g.
`__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(...)`.

Because of the current implementation, in case of multiple Reacts on the
page, all of them will patch the console independently. This will be
fixed in one of the next PRs, where I am moving console patching to the
global Hook.

This change of course makes `hook.js` script bigger, but I think it is a
reasonable trade-off for better DevX. We later can add more heuristics
to optimize the performance (if necessary) of `rendererInterface` for
cases when Frontend was connected late and Backend is attempting to
flush out too many recorded operations.

This essentially reverts https://github.com/facebook/react/pull/26563.
2024-09-12 13:59:29 +01:00
Ruslan Lesiutin
344bc8128b refactor[Agent/Store]: Store to send messages only after Agent is initialized (#30945)
Both for browser extension, and for React Native (as part of
`react-devtools-core`) `Store` is initialized before the Backend (and
`Agent` as a part of it):

bac33d1f82/packages/react-devtools-extensions/src/main/index.js (L111-L113)

Any messages that we send from `Store`'s constructor are ignored,
because there is nothing on the other end yet. With these changes,
`Agent` will send `backendInitialized` message to `Store`, after which
`getBackendVersion` and other events will be sent.

Note that `isBackendStorageAPISupported` and `isSynchronousXHRSupported`
are still sent from `Agent`'s constructor, because we don't explicitly
ask for it from `Store`, but these are used.

This the pre-requisite for fetching settings and unsupported renderers
reliably from the Frontend.
2024-09-11 15:10:13 +01:00
Sebastian Markbåge
d160aa0fbb [DevTools] Use Unicode Atom Symbol instead of Atom Emoji (#30832)
This reverts #19603.

Before:
<img width="724" alt="Screenshot 2024-08-28 at 12 07 29 AM"
src="https://github.com/user-attachments/assets/0613088f-c013-4f1c-92c3-fbdae8c1f109">

After:
<img width="771" alt="Screenshot 2024-08-28 at 12 08 13 AM"
src="https://github.com/user-attachments/assets/eef21bee-d11f-4f0a-9147-053a163f720f">

Consensus seems to be that while the purple on is a bit clearer and
easier to read. The purple is not on brand so it doesn't look like
React. It looks ugly. It's distracting (too eye catching). Taking away
attention from other tabs in an unfair way.

It also gets worse with more tabs added. We plan on both adding another
tab and panes inside other tabs (elements/sources) soon. Each needs to
be marked somehow as part of React but spelling it out is too long.
Putting inside a second tab means two clicks and takes away real-estate
from our extension and doesn't solve the problem with extension panes in
other tabs. We also plan on adding multiple different tracks to the
Performance tab which also needs a name other than just React and
spelling out React as a prefix is too long. The Emoji is too
distracting. So it seems best to uniformly apply the symbol - albeit it
might just look like a dot to many.

Dark mode looks close to on brand:

<img width="1089" alt="Screenshot 2024-08-28 at 12 32 50 AM"
src="https://github.com/user-attachments/assets/7175a540-4241-4c26-9e4d-4d367873af57">
2024-09-10 00:09:42 -04:00
Sam Zhou
e210d08180 [flow] Upgrade Flow to 0.245.2 (#30919)
## Summary

This PR bumps Flow all the way to the latest 0.245.2. 

Most of the suppressions comes from Flow v0.239.0's change to include
undefined in the return of `Array.pop`.

I also enabled `react.custom_jsx_typing=true` and added custom jsx
typing to match the old behavior that `React.createElement` is
effectively any typed. This is necessary since various builtin
components like `React.Fragment` is actually symbol in the React repo
instead of `React.AbstractComponent<...>`. It can be made more accurate
by customizing the `React$CustomJSXFactory` type, but I will leave it to
the React team to decide.

## How did you test this change?

`yarn flow` for all the renderers
2024-09-09 08:41:44 -07:00
Sebastian Markbåge
a06cd9e1d1 [DevTools] Refactor Forcing Fallback / Error of Suspense / Error Boundaries (#30870)
First, this basically reverts
1f3892ef8c
to use a Map/Set to track what is forced to suspend/error again instead
of flags on the Instance. The difference is that now the key in the
Fiber itself instead of the ID. Critically this avoids the
fiberToFiberInstance map to look up whether or not a Fiber should be
forced to suspend when asked by the renderer.

This also allows us to force suspend/error on filtered instances. It's a
bit unclear what should happen when you try to Suspend or Error a child
but its parent boundary is filtered. It was also inconsistent between
Suspense and Error due to how they were implemented.

I think conceptually you're trying to simulate what would happen if that
Component errored or suspended so it would be misleading if we triggered
a different boundary than would happen in real life. So I think we
should trigger the nearest unfiltered Fiber, not the nearest Instance.
The consequence of this however is that if this instance was filtered,
there's no way to undo it without refreshing or removing the filter.
This is an edge case though since it's unusual you'd filter these in the
first place.

It used to be that Suspense walked the store in the frontend and Error
walked the Fibers in the backend. They also did this somewhat eagerly.
This simplifies and unifies the model by passing the id of what you
clicked in the frontend and then we walk the Fiber tree from there in
the backend to lazily find the boundary. However I also eagerly walk the
tree at first to find whether we have any Suspense or Error boundary
parents at all so we can hide the buttons if not.

This also implements it to work with VirtualInstances using #30865. I
find the nearest Fiber Instance downwards filtered or otherwise. Then
from its parent we find the nearest Error or Suspense boundary. That's
because VirtualInstance will always have their inner Fiber as an
Instance but they might not have their parent since it might be
filtered. Which would potentially cause us to skip over a filtered
parent Suspense boundary.
2024-09-05 15:48:17 -04:00
Sebastian Markbåge
04ec50efa9 [DevTools] Add Filtering of Environment Names (#30850)
Stacked on #30842.

This adds a filter to be able to exclude Components from a certain
environment. Default to Client or Server.

The available options are computed into a dropdown based on the names
that are currently used on the page (or an option that were previously
used). In addition to the hardcoded "Client". Meaning that if you have
Server Components on the page you see "Server" or "Client" as possible
options but it can be anything if there are multiple RSC environments on
the page.

"Client" in this case means Function and Class Components in Fiber -
excluding built-ins.

If a Server Component has two environments (primary and secondary) then
both have to be filtered to exclude it.

We don't show the option at all if there are no Server Components used
in the page to avoid confusing existing users that are just using Client
Components and wouldn't know the difference between Server vs Client.

<img width="815" alt="Screenshot 2024-08-30 at 12 56 42 AM"
src="https://github.com/user-attachments/assets/e06b225a-e85d-4cdc-8707-d4630fede19e">
2024-09-03 12:29:15 -04:00
Sebastian Markbåge
1a8f92a869 [DevTools] Track Tree Base Duration of Virtual Instances (#30817)
These don't have their own time since they don't take up any time to
render but they show up in the tree for context. However they never
render themselves. Their base tree time is the base time of their
children. This way they take up the same space as their combined
children in the Profiler tree. (Instead of leaving a blank line which
they did before this PR.)

The frontend doesn't track the difference between a virtual instance and
a Fiber that didn't render this update. This might be a bit confusing as
to why it didn't render. I add the word "client" to make it a bit
clearer and works for both. We should probably have different verbiage
here based on it is a Server Component or something else.

<img width="1103" alt="Screenshot 2024-08-26 at 5 00 47 PM"
src="https://github.com/user-attachments/assets/87b811d4-7024-466a-845d-542493ed3ca2">

I also took the opportunity to remove idToTreeBaseDurationMap and
idToRootMap maps. Cloning the Map isn't really all that super fast
anyway and it means we have to maintain the map continuously as we
render. Instead, we can track it on the instances and then walk the
instances to create a snapshot when starting to profile. This isn't as
fast but really fast too and requires less bookkeeping while rendering
instead which is more sensitive than that one snapshot in the beginning.
2024-08-27 12:05:10 -04:00
Sebastian Markbåge
36c04348d7 [DevTools] Make Functions Clickable to Jump to Definition (#30769)
Currently you can jump to definition of a function by right clicking
through the context menu. However, it's pretty difficult to discover.
This makes the functions clickable to jump to definition - like links.

This uses the same styling as we do for links (which are btw only
clickable if they're not editable). Including cursor: pointer.

I added a background on hover which follows the same pattern as the
owners list.

I also dropped the ƒ prefix when displaying functions. This is a cute
short cut and there's precedence in how Chrome prints functions in the
console *if* the function's toString would've had a function prefix like
if it was a function declaration or expression. It does not do this for
arrow functions or object methods.

Elsewhere in the JS ecosystem this isn't really used anywhere. It
invites more questions than it answers.

The parenthesis and curlies are enough. There's no ambiguity here since
strings have quotations. It looks better with just its object method
form. Keeping it simple seems best. To my eyes this flows better because
I'm used to looking at function syntax but not weird "f"s.

Before:

<img width="433" alt="Screenshot 2024-08-20 at 11 55 09 PM"
src="https://github.com/user-attachments/assets/9dd50da6-598f-4291-9e24-1cdc7200dc9e">


After:
<img width="388" alt="Screenshot 2024-08-20 at 11 46 01 PM"
src="https://github.com/user-attachments/assets/dd988e14-412e-4deb-8c8c-26a54be8331f">


After (Hover):
<img width="389" alt="Screenshot 2024-08-20 at 11 46 31 PM"
src="https://github.com/user-attachments/assets/6fb4ebed-5dc1-448a-8e4d-b6d4f3903329">
2024-08-22 12:35:49 -04:00
Sebastian Markbåge
fca5d655d7 [DevTools] Hide props section if it is null (#30696)
We use null as a marker that we don't know what the props are as opposed
to knowing that they're empty.
2024-08-14 15:48:05 -04:00
Ruslan Lesiutin
fbfe08de61 fix[react-devtools/InspectedElement]: fixed border stylings when some of the panels are not rendered (#30676)
Alternative to https://github.com/facebook/react/pull/30667.

Basically wrap every section in a `div` with the same class, and only
apply `border-bottom` for every instance, except for the last child. We
are paying some cost by having more divs, but thats more explicit.
2024-08-14 13:35:06 +01:00
Sebastian Markbåge
b4c38015d0 [DevTools] Remove lodash.throttle (#30657)
Same principle as #30555. We shouldn't be throttling the UI to make it
feel less snappy. Instead, we should use back-pressure to handle it.
Normally the browser handles it automatically with frame aligned events.
E.g. if the thread can't keep up with sync updates it doesn't send each
event but the next one. E.g. pointermove or resize.

However, it is possible that we end up queuing too many events if the
frontend can't keep up but the solution to this is the same as mentioned
in #30555. I.e. to track the last message and only send after we get a
response.

I still keep the throttle to persist the selection since that affects
the disk usage and doesn't have direct UX effects.

The main motivation for this change though is that lodash throttle
doesn't rely on timers but Date.now which makes it incompatible with
most jest helpers which means I can't write tests against these
functions properly.
2024-08-12 12:32:55 -04:00
Sebastian Markbåge
06d0b89e8d [DevTools] Enable pointEvents while scrolling (#30560)
[`react-window` disables `pointerEvents` while scrolling meaning you
can't click anything while
scrolling.](https://github.com/bvaughn/react-window/issues/128).

This means that the first click when you stop the scroll with inertial
scrolling doesn't get registered. This is suuuper annoying. This might
make sense when you click to stop on a more intentional UI but it
doesn't makes sense in a list like this because we eagerly click things
even on mousedown.

This PR just override that to re-enable pointer events.

Supposedly this is done for performance but that might be outdated
knowledge. I haven't observed any difference so far.

If we discover that it's a perf problem, there's another technique we
can use where we call `ownerDocument.elementFromPoint(e.pageX, e.pageY)`
and then dispatch the event against that element. But let's try the
simplest approach first?
2024-08-01 11:34:38 -04:00
Sebastian Markbåge
4ea12a11d1 [DevTools] Make Element Inspection Feel Snappy (#30555)
There's two problems. The biggest one is that it turns out that Chrome
is throttling looping timers that we're using both while polling and for
batching bridge traffic. This means that bridge traffic a lot of the
time just slows down to 1 second at a time. No wonder it feels sluggish.
The only solution is to not use timers for this.

Even when it doesn't like in Firefox the batching into 100ms still feels
too sluggish.

The fix I use is to batch using a microtask instead so we can still
batch multiple commands sent in a single event but we never artificially
slow down an interaction.

I don't think we've reevaluated this for a long time since this was in
the initial commit of DevTools to this repo. If it causes other issues
we can follow up on those.

We really shouldn't use timers for debouncing and such. In fact, React
itself recommends against it because we have a better technique with
scheduling in Concurrent Mode. The correct way to implement this in the
bridge is using a form of back-pressure where we don't keep sending
messages until we get a message back and only send the last one that
matters. E.g. when moving the cursor over a the elements tab we
shouldn't let the backend one-by-one move the DOM node to each one we
have ever passed. We should just move to the last one we're currently
hovering over. But this can't be done at the bridge layer since it
doesn't know if it's a last-one-wins or imperative operation where each
one needs to be sent. It needs to be done higher. I'm not currently
seeing any perf problems with this new approach but I'm curious on React
Native or some thing. RN might need the back-pressure approach. That can
be a follow up if we ever find a test case.

Finally, the other problem is that we use a Suspense boundary around the
Element Inspection. Suspense boundaries are for things that are expected
to take a long time to load. This shows a loading state immediately. To
avoid flashing when it ends up being fast, React throttles the reveal to
200ms. This means that we take a minimum of 200ms to show the props. The
way to show fast async data in React is using a Transition (either using
startTransition or useDeferredValue). This lets the old value remaining
in place while we're loading the next one.

We already implement this using `inspectedElementID` which is the async
one. It would be more idiomatic to implement this with useDeferredValue
rather than the reducer we have now but same principle. We were just
using the wrong ID in a few places so when it synchronously updated they
suspended. So I just made them use the inspectedElementID instead.

Then I can simply remove the Suspense boundary. Now the selection
updates in the tree view synchronously and the sidebar lags a frame or
two but it feels instant. It doesn't flash to white between which is
key.
2024-08-01 11:04:56 -04:00
Sebastian Markbåge
33e54fa252 [DevTools] Rename NativeElement to HostInstance in the Bridge (#30491)
Stacked on #30490.

This is in the same spirit but to clarify the difference between what is
React Native vs part of any generic Host. We used to use "Native" to
mean three different concepts. Now "Native" just means React Native.

E.g. from the frontend's perspective the Host can be
Highlighted/Inspected. However, that in turn can then be implemented as
either direct DOM manipulation or commands to React Native. So frontend
-> backend is "Host" but backend -> React Native is "Native" while
backend -> DOM is "Web".

Rename NativeElementsPanel to BuiltinElementsPanel. This isn't a React
Native panel but one part of the surrounding DevTools. We refer to Host
more as the thing running React itself. I.e. where the backend lives.
The runtime you're inspecting. The DevTools itself needs a third term.
So I went with "Builtin".
2024-07-30 09:12:12 -04:00
Sebastian Markbåge
ec98d36c3a [DevTools] Rename Fiber to Element in the Bridge Protocol and RendererInterface (#30490)
I need to start clarifying where things are really actually Fibers and
where they're not since I'm adding Server Components as a separate type
of component instance which is not backed by a Fiber.

Nothing in the front end should really know anything about what kind of
renderer implementation we're inspecting and indeed it's already not
always a "Fiber" in the legacy renderer.

We typically refer to this as a "Component Instance" but the front end
currently refers to it as an Element as it historically grew from the
browser DevTools Elements tab.

I also moved the renderer.js implementation into the `backend/fiber`
folder. These are at the same level as `backend/legacy`. This clarifies
that anything outside of this folder ideally shouldn't refer to a
"Fiber".

console.js and profilingHooks.js unfortunately use Fibers a lot which
needs further refactoring. The profiler frontend also uses the term
alot.
2024-07-29 14:29:52 -04:00
Jan Kassens
b7e7f1a3fa [BE] upgrade prettier to 3.3.3 (#30420)
Mostly just changes in ternary formatting.
2024-07-22 16:09:01 -04:00
Ruslan Lesiutin
ad59ddf272 chore[react-devtools/ui]: fix strict mode badge styles (#30159)
## Summary

Just a minor UI fix to strict mode badge layout and component name text
overflow

## How did you test this change?
| Before | After |
| --- | --- |
| ![Screenshot 2024-06-30 at 23 35
19](https://github.com/facebook/react/assets/28902667/dbe62322-07f3-4291-808d-ecd2b0fba8cc)
| ![Screenshot 2024-06-30 at 23 31
06](https://github.com/facebook/react/assets/28902667/863b2f49-942f-4522-b815-5509a77b3b24)|
2024-07-01 15:27:15 +01:00
Vitali Zaidman
d9a5b6393a fix[react-devtools] divided inspecting elements between inspecting do… (#29885)
# **before**
* nav to dom element from devtools
* nav to devtools element from page
are enabled on extension and disabled on the rest of the flavors.

## extension:
* nav to dom element from devtools **enabled** and working
* nav to devtools element from page **enabled** and working
![Screenshot 2024-06-13 at 11 15
11](https://github.com/facebook/react/assets/5188459/fef78b70-d22c-4405-8871-8b0449b51937)

## inline:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **disabled**

![before-inline](https://github.com/facebook/react/assets/5188459/24020dc2-baec-4d0a-84d4-45c96d653843)

## standalone:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **disabled**

![before-standalone](https://github.com/facebook/react/assets/5188459/19b4cb34-9d1f-412e-baea-59ea85f99d04)

## fusebox:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **disabled**

![before-fusebox](https://github.com/facebook/react/assets/5188459/1a18fda4-04b8-40f4-ae8b-e059889fca93)

# **after**
same:
* nav to dom element from devtools
* nav to devtools element from page
are enabled on extension and disabled on inline.

change:
standalone and fusebox can nav to devtools element from page

## extension:
* nav to dom element from devtools **enabled** and working
* nav to devtools element from page **enabled** and working
![Screenshot 2024-06-13 at 10 50
25](https://github.com/facebook/react/assets/5188459/f4679c72-b211-43d6-b3ea-6380e0d1edf0)

## inline:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **disabled**

![after-inline](https://github.com/facebook/react/assets/5188459/fdfdd87b-9bc3-47f3-b1e0-730239f6485d)

## standalone:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **enabled** and working

![after-standalone](https://github.com/facebook/react/assets/5188459/b25e3c63-a697-4b0c-8ad2-0e12ec5c3e9c)

## fusebox:
* nav to dom element from devtools **disabled**
* nav to devtools element from page **enabled** and working

![after-fusebox](https://github.com/facebook/react/assets/5188459/f14147d8-9831-4909-a164-52f892c875e5)
2024-06-13 15:37:51 +01:00
Vitali Zaidman
fb3b9ead47 fix[react-devtools] component badge in light mode is now not invisible (#29852)
In light mode, component badges in profiler were invisible due to using
the alpha channel to produce the grayish color. Instead I'm using the
equivalent RGB without that alpha.

**before**
In light mode, component badges in profiler were invisible (forwardRef
badge in this case):
![Screenshot 2024-06-11 at 13 15
16](https://github.com/facebook/react/assets/5188459/385b80a5-d385-4eac-bac6-7fec6938d78f)

**after**
Profiler tooltip fix. also notice there's no regression on the right
menu:
![Screenshot 2024-06-11 at 13 04
41](https://github.com/facebook/react/assets/5188459/64aa27f7-35b8-4e3b-91d4-c12d5a97330e)

also works in dark mode:
![Screenshot 2024-06-11 at 13 12
17](https://github.com/facebook/react/assets/5188459/99aa55fd-23ee-4881-9676-63eca86bf761)

Still visible in components in light mode, whether selected or not:
![Screenshot 2024-06-11 at 13 04
49](https://github.com/facebook/react/assets/5188459/59c0bf63-d9b0-4d46-a145-f615249d1da0)

components dark mode:
![Screenshot 2024-06-11 at 13 10
25](https://github.com/facebook/react/assets/5188459/16e61acc-c431-4bfa-8a65-ca6701f4379b)
2024-06-11 14:32:39 +01:00