Commit Graph

7004 Commits

Author SHA1 Message Date
Felipe Cardozo
bf1afade8d [react-dom/server] Fix hanging on Deno (#35235) 2025-12-04 06:50:27 +01:00
Sebastian Markbåge
7dc903cd29 Patch FlightReplyServer with fixes from ReactFlightClient (#35277)
FlightReplyServer are for client->server and ReactFlightClient is for
server->client. They're not 100% symmetrical.

We did a number of refactors to ReactFlightClient in PRs like #29823 and
#33664 to change the structure of the resolution. This PR brings those
changes to synchronize the two approaches. Which addresses deep
resolution of cycles and deferred error handling.

This also fixes a critical security vulnerability.
2025-12-03 10:41:19 -05:00
Sebastian "Sebbie" Silbermann
6875c3eab4 [test] Only run tests overriding fallback and error states in supported versions (#35234) 2025-11-27 16:15:14 +01:00
Sebastian "Sebbie" Silbermann
74fa1667a7 [DevTools] Move "Back to full tree view" into Activity list item (#35164) 2025-11-26 10:42:18 +01:00
emily8rown
fd524fe02a [DevTools] hotkey to start/stop profiling (#35160)
## Summary

The built-in browser profiler supports starting/stopping with Cmd+E. For
Symmetry this adds the same hotkey for react devtools profiler.

## How did you test this change?
yarn build:\<browser name\> 
yarn run test:\<browser name\>

<img width="483" height="135" alt="Screenshot 2025-11-17 at 14 30 34"
src="https://github.com/user-attachments/assets/426939aa-15da-4c21-87a4-e949e6949482"
/>

firefox:

https://github.com/user-attachments/assets/6f225b90-828f-4e79-a364-59d6bc942f83

edge:

https://github.com/user-attachments/assets/5b2e9242-f0e8-481b-99a2-2dd78099f3ac

chrome:

https://github.com/user-attachments/assets/790aab02-2867-4499-aec1-e32e38c763f9

---------

Co-authored-by: Ruslan Lesiutin <28902667+hoxyq@users.noreply.github.com>
2025-11-21 11:37:10 -05:00
Joseph Savona
50e7ec8a69 [compiler] Deprecate noEmit, add outputMode (#35112)
This deprecates the `noEmit: boolean` flag and adds `outputMode:
'client' | 'client-no-memo' | 'ssr' | 'lint'` as the replacement.
OutputMode defaults to null and takes precedence if specified, otherwise
we use 'client' mode for noEmit=false and 'lint' mode for noEmit=true.

Key points:
* Retrying failed compilation switches from 'client' mode to
'client-no-memo'
* Validations are enabled behind
Environment.proto.shouldEnableValidations, enabled for all modes except
'client-no-memo'. Similar for dropping manual memoization.
* OptimizeSSR is now gated by the outputMode==='ssr', not a feature flag
* Creation of reactive scopes, and related codegen logic, is now gated
by outputMode==='client'
2025-11-20 15:12:40 -08:00
Sebastian "Sebbie" Silbermann
8ac5f4eb36 Fix form status reset when component state is updated (#34075)
Co-authored-by: Vordgi <sasha2822222@gmail.com>
2025-11-19 18:22:07 +01:00
Sebastian Markbåge
eb89912ee5 Add expertimental optimisticKey behind a flag (#35162)
When dealing with optimistic state, a common problem is not knowing the
id of the thing we're waiting on. Items in lists need keys (and single
items should often have keys too to reset their state). As a result you
have to generate fake keys. It's a pain to manage those and when the
real item comes in, you often end up rendering that with a different
`key` which resets the state of the component tree. That in turns works
against the grain of React and a lot of negatives fall out of it.

This adds a special `optimisticKey` symbol that can be used in place of
a `string` key.

```js
import {optimisticKey} from 'react';
...
const [optimisticItems, setOptimisticItems] = useOptimistic([]);
const children = savedItems.concat(
  optimisticItems.map(item =>
    <Item key={optimisticKey} item={item} />
  )
);
return <div>{children}</div>;
```

The semantics of this `optimisticKey` is that the assumption is that the
newly saved item will be rendered in the same slot as the previous
optimistic items. State is transferred into whatever real key ends up in
the same slot.

This might lead to some incorrect transferring of state in some cases
where things don't end up lining up - but it's worth it for simplicity
in many cases since dealing with true matching of optimistic state is
often very complex for something that only lasts a blink of an eye.

If a new item matches a `key` elsewhere in the set, then that's favored
over reconciling against the old slot.

One quirk with the current algorithm is if the `savedItems` has items
removed, then the slots won't line up by index anymore and will be
skewed. We might be able to add something where the optimistic set is
always reconciled against the end. However, it's probably better to just
assume that the set will line up perfectly and otherwise it's just best
effort that can lead to weird artifacts.

An `optimisticKey` will match itself for updates to the same slot, but
it will not match any existing slot that is not an `optimisticKey`. So
it's not an `any`, which I originally called it, because it doesn't
match existing real keys against new optimistic keys. Only one
direction.
2025-11-18 16:29:18 -05:00
Sebastian "Sebbie" Silbermann
194c12d949 [DevTools] Name root "Transition" when focusing on Activity (#35108) 2025-11-18 10:16:58 +01:00
Sebastian "Sebbie" Silbermann
7f1a085b28 [DevTools] Show list of named Activities in Suspense tab (#35092) 2025-11-18 09:52:44 +01:00
Sebastian "Sebbie" Silbermann
7df96b0c1a [Flight] Complete list of Node.js' internal Promise awaits (#35161) 2025-11-17 19:28:26 +01:00
Sebastian "Sebbie" Silbermann
45bc3c9f04 [Flight] Reduce risk of maximum call stack exceeded when emitting async sequence (#35159) 2025-11-17 18:54:13 +01:00
Hendrik Liebau
fb2177c153 [Flight] Fix pending chunks count for streams & async iterables in DEV (#35143)
In DEV, we need to prevent the response from being GC'd while there are
still pending chunks for ReadableSteams or pending results for
AsyncIterables.

Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-11-14 23:52:11 +01:00
Hendrik Liebau
93fc57400b [Flight] Fix broken byte stream parsing caused by buffer detachment (#35127)
This PR fixes a critical bug where `ReadableStream({type: 'bytes'})`
instances passed through React Server Components (RSC) would stall after
reading only the first chunk or the first few chunks in the client. This
issue was masked by using `web-streams-polyfill` in tests, but manifests
with native Web Streams implementations.

The root cause is that when a chunk is enqueued to a
`ReadableByteStreamController`, the spec requires the underlying
ArrayBuffer to be synchronously transferred/detached. In the React
Flight Client's chunk parsing, embedded byte stream chunks are created
as views into the incoming RSC stream chunk buffer using `new
Uint8Array(chunk.buffer, offset, length)`. When embedded byte stream
chunks are enqueued, they can detach the shared buffer, leaving the RSC
stream parsing in a broken state.

The fix is to copy embedded byte stream chunks before enqueueing them,
preventing buffer detachment from affecting subsequent parsing. To not
affect performance too much, we use a zero-copy optimization: when a
chunk ends exactly at the end of the RSC stream chunk, or when the row
spans into the next RSC chunk, no further parsing will access that
buffer, so we can safely enqueue the view directly without copying.

We now also enqueue embedded byte stream chunks immediately as they are
parsed, without waiting for the full row to complete.

To simplify the logic in the client, we introduce a new `'b'` protocol
tag specifically for byte stream chunks. The server now emits `'b'`
instead of `'o'` for `Uint8Array` chunks from byte streams (detected via
`supportsBYOB`). This allows the client to recognize byte stream chunks
without needing to track stream IDs.

Tests now use the proper Jest environment with native Web Streams
instead of polyfills, exposing and validating the fix for this issue.
2025-11-13 21:23:02 +01:00
Sebastian "Sebbie" Silbermann
093b3246e1 [react-dom] Batch updates from resize until next frame (#35117) 2025-11-13 13:30:21 +01:00
Ricky
bbe3f4d322 [flags] disableLegacyMode in native-fb (#35120)
this is failing test too because we need the legacy mode in the react
package until we fix the tests
2025-11-12 15:38:58 -05:00
Sebastian "Sebbie" Silbermann
1ea46df8ba [DevTools] Batch updates when updating component filters (#35093) 2025-11-11 23:20:22 +01:00
Sebastian "Sebbie" Silbermann
8c15edd57c [DevTools] Send root unmount as a regular removal operation (#35107) 2025-11-11 23:08:54 +01:00
Ricky
04ee54cd12 [tests] add more portal activity tests (#35095)
I copied some tests from
[`Activity-test.js`](1d68bce19c/packages/react-reconciler/src/__tests__/Activity-test.js)
and made them portal specific just to confirm my understanding of how
Portals + Activity interact is correct. Seems good to include them.
2025-11-11 12:47:56 -05:00
Sebastian "Sebbie" Silbermann
ce4054ebdd [DevTools] Measure when reconnecting Suspense (#35098) 2025-11-10 20:55:31 +01:00
Sebastian "Sebbie" Silbermann
21c1d51acb [DevTools] Don't attempt to draw bounding box if inspected element is not a Suspense (#35097) 2025-11-10 20:01:59 +01:00
Facebook Community Bot
be48396dbd Remove Dead Code in WWW JS
Differential Revision: D86593830

Pull Request resolved: https://github.com/facebook/react/pull/35085
2025-11-10 16:34:01 +00:00
Andrew Clark
5268492536 Fix: Activity should hide portal contents (#35091)
This PR updates the behavior of Activity so that when it is hidden, it
hides the contents of any portals contained within it.

Previously we had intentionally chosen not to implement this behavior,
because it was thought that this concern should be left to the userspace
code that manages the portal, e.g. by adding or removing the portal
container from the DOM. Depending on the use case for the portal, this
is often desirable anyway because the portal container itself is not
controlled by React.

However, React does own the _contents_ of the portal, and we can hide
those elements regardless of what the user chooses to do with the
container. This makes the hiding/unhiding behavior of portals with
Activity automatic in the majority of cases, and also benefits from
aligning the DOM mutations with the rest of the React's commit phase
lifecycle.

The reason we have to special case this at all is because usually we
only hide the direct DOM children of the Activity boundary. There's no
reason to go deeper than that, because hiding a parent DOM element
effectively hides everything inside of it. Portals are the exception,
because they don't exist in the normal DOM hierarchy; we can't assume
that just because a portal has a parent in the React tree that it will
also have that parent in the actual DOM.

So, whenever an Activity boundary is hidden, we must search for and hide
_any_ portal that is contained within it, and recursively hide its
direct children, too.

To optimize this search, we use a new subtree flag, PortalStatic, that
is set only on fiber paths that contain a HostPortal. This lets us skip
over any subtree that does not contain a portal.
2025-11-10 10:42:26 -05:00
Sebastian Markbåge
c83be7da9f [Fizz] Simplify createSuspenseBoundary path (#35087)
Small follow up to #35068.

Since this is now a single argument we can simplify the creation
branching a bit and make sure it's const.
2025-11-09 15:19:43 -05:00
Sebastian Markbåge
6362b5c711 [DevTools] Special case the selected root outline (#35071)
When I moved the outline to above all other rects, I thought it was
clever to unify with the root so that the outline was also used for the
root selection. But the root outline is not drawn like the other rects.
It's outside the padding and doesn't have the 1px adjustment which leads
the overlay to be slightly inside the other rect instead of above it.

This goes back to just having the selected root be drawn by the root
element.

Before:

<img width="652" height="253" alt="Screenshot 2025-11-07 at 11 39 28 AM"
src="https://github.com/user-attachments/assets/334237d1-f190-4995-94cc-9690ec0f7ce1"
/>

After:

<img width="674" height="220" alt="Screenshot 2025-11-07 at 11 44 01 AM"
src="https://github.com/user-attachments/assets/afaa86d8-942a-44d8-a1a5-67c7fb642c0d"
/>
2025-11-09 15:03:31 -05:00
Sebastian "Sebbie" Silbermann
5a9921b839 [DevTools] Apply Activity slice filter when double clicking Activity (#34908) 2025-11-08 18:09:44 +01:00
Andrew Clark
717e70843e Fix: Errors should not escape a hidden Activity (#35074)
If an error is thrown inside a hidden Activity, it should not escape
into the visible part of the UI. Conceptually, a hidden Activity
boundary is not part of the current UI; it's the same as an unmounted
tree, except for the fact that the state will be restored if it's later
revealed.

Fixes:
- https://github.com/facebook/react/issues/35073
2025-11-07 18:18:24 -05:00
Stian Jensen
a10ff9c857 Upgrade devtools dependency update-notifier to 5.x (#31655)
## Summary

This PR upgrades the dependency on update-notifier, used in
react-devtools, to 5.x
This is the latest non-ESM version, so upgrading to it should be
unproblematic (while updating to 6.x and beyond will have to wait).

Upgrading means we avoid installing a lot of outdated dependencies (as
can be seen from the diff in yarn.lock), and resolves part of
https://github.com/facebook/react/issues/28058

Changelog:
https://github.com/yeoman/update-notifier/releases

The most relevant breaking change seems to be that the minimum support
node version is increased from v6 to v10, but I couldn't find what is
currently React's official node version support.

## How did you test this change?

I ran the test-suite locally (`yarn test` in root folder), but I'm not
sure if that one actually covers devtools?

I also built and tested this version of devtools with some internal
company projects (both react and react-native based) – following
guidelines from
https://github.com/facebook/react/issues/28058#issuecomment-1943619292.
2025-11-07 18:43:42 +00:00
Sebastian Markbåge
fa50caf5f8 [Fizz] Unify preamble only fields to save a field (#35068)
Stacked on #35067.

Same idea of saving a field on the SuspenseBoundary in the common case.
The case where they can have a preamble is rare.
2025-11-07 09:19:19 -05:00
Sebastian Markbåge
1e986f514f [Fizz] Unify prerender only fields to save a field (#35067)
I need to regain a field because the SuspenseBoundary type is already at
16 fields in prod, after which it deopts v8.

There are two fields that are only used in prerender to track postpones.
These are ripe to be split into an optional object so that they only
take up one field when they're not used.
2025-11-07 09:18:28 -05:00
Sebastian Markbåge
38bdda1ca6 Don't skip content in visible offscreen trees for Gesture View Transitions (#35066)
Follow up to #35063.

I forgot there's another variant of this in the ApplyGesture path.
2025-11-06 20:59:08 -05:00
Jack Pope
a44e750e87 Store instance handles in an internal map behind flag (#35053)
We already append `randomKey` to each handle name to prevent external
libraries from accessing and relying on these internals. But more
libraries recently have been getting around this by simply iterating
over the element properties and using a `startsWith` check.

This flag allows us to experiment with moving these handles to an
internal map.

This PR starts with the two most common internals, the props object and
the fiber. We can consider moving additional properties such as the
container root and others depending on perf results.
2025-11-06 18:17:53 -05:00
Sebastian Markbåge
37b089a59c Don't skip content in visible offscreen trees for View Transitions (#35063)
Also, don't not skip hidden trees.

Memoized state is null when an Offscreen boundary (Suspense or Activity)
is visible.

This logic was inversed in a couple of View Transition checks which
caused pairs to be discovered or not discovered incorrectly for
insertion and deletion of Suspense or Activity boundaries.
2025-11-06 16:03:02 -05:00
Sebastian Markbåge
1a31a814f1 Escape View Transition Name Strings as base64 (#35060)
This is an alternative to #35059.

If the name needs escaping, then instead of escaping it, we just use a
base64 name. This wouldn't allow you to match on an escaped name in your
own CSS like you should be able to if browsers worked properly. But at
least it would provide matching name in current browsers which is
probably sufficient if you're using auto-generated names.

This also covers some cases where `CSS.escape()` isn't sufficient anyway
like when the name ends in a dot.
2025-11-06 16:02:06 -05:00
Sebastian Markbåge
fa767dade6 Remove unstable_expectedLoadTime option (#35051)
Follow up to #35022.

It's now replaced by the `defer` option.

Sounds like nobody is actually using this option, including Meta, so we
can just delete it.
2025-11-05 15:52:21 -05:00
Sebastian Markbåge
0ba2f01f74 Rename <Suspense unstable_expectedLoadTime> to <Suspense defer> and implement in SSR (#35022)
We've long had the CPU suspense feature behind a flag under the terrible
API `unstable_expectedLoadTime={arbitraryNumber}`. We've known for a
long time we want it to just be `defer={true}` (or just `<Suspense
defer>` in the short hand syntax). So this adds the new name and warns
for the old name.

For only the new name, I also implemented SSR semantics in Fizz. It has
two effects here.
1) It renders the fallback before the content (similar to prerender)
allowing siblings to complete quicker.
2) It always outlines the result. When streaming this should really
happen naturally but if you defer a prerendered content it also implies
that it's expensive and should be outlined. It gives you a opt-in to
outlining similar to suspensey images and css but let you control it
manually.
2025-11-05 14:12:09 -05:00
Sebastian Markbåge
dd048c3b2d Clean up enablePostpone Experiment (#35048)
We're not shipping this and it's a lot of code to maintain that is
blocking my refactor of Fizz for SuspenseList.
2025-11-05 00:05:59 -05:00
Sebastian Markbåge
c308cb5905 Disable enablePostpone flag in experimental (#31042)
I don't think we're ready to land this yet since we're using it to run
other experiments and our tests. I'm opening this PR to indicate intent
to disable and to ensure tests in other combinations still work. Such as
enableHalt without enablePostpone. I think we'll also need to rewrite
some tests that depend on enablePostpone to preserve some coverage.

The conclusion after this experiment is that try/catch around these are
too likely to block these signals and consider them error. Throwing
works for Hooks and `use()` because the lint rule can ensure that
they're not wrapped in try/catch. Throwing in arbitrary functions not
quite ecosystem compatible. It's also why there's `use()` and not just
throwing a Promise. This might also affect the Catch proposal.

The "prerender" for SSR that's supporting "Partial Prerendering" is
still there. This just disables the `React.postpone()` API for creating
the holes.
2025-11-04 23:23:25 -05:00
Sebastian Markbåge
986323f8c6 [Fiber] SuspenseList with "hidden" tail row should "catch" suspense (#35042)
Normally if you suspend in a SuspenseList row above a Suspense boundary
in that row, it'll suspend the parent. Which can itself delay the commit
or resuspend a parent boundary. That's because SuspenseList mostly just
coordinates the state of the inner boundaries and isn't a boundary
itself.

However, for tail "hidden" and "collapsed" this is not quite the case
because the rows themselves can avoid being rendered.

In the case of "collapsed" we require at least one Suspense boundary
above to have successfully rendered before committing the list because
the idea of this mode is that you should at least always show some
indicator that things are still loading. Since we'd never try the next
one after that at all, this just works. Expect there was an unrelated
bug that meant that "suspend with delay" on a Retry didn't suspend the
commit. This caused a scenario were it'd allow a commit proceed when it
shouldn't. So I fixed that too. The counter intuitive thing here is that
we won't actually show a previous completed row if the loading state of
the next row is still loading.

For tail "hidden" it's a little different because we don't actually
require any loading indicator at all to be shown while it's loading. If
we attempt a row and it suspends, we can just hide it (and the rest) and
move to commit. Therefore this implements a path where if all the rest
of the tail are new mounts (we wouldn't be required to unmount any
existing boundaries) then we can treat the SuspenseList boundary itself
as "catching" the suspense. This is more coherent semantics since any
future row that we didn't attempt also wouldn't resuspend the parent.

This allows simple cases like `<SuspenseList>{list}</SuspenseList>` to
stream in each row without any indicator and no need for Suspense
boundaries.
2025-11-04 22:11:33 -05:00
Jordan Brown
8f8b336734 [eslint] Fix useEffectEvent checks in component syntax (#35041)
We were not recording uEE calls in component/hook syntax. Easy fix.

Added tests matching function component syntax for component syntax +
added one for hooks
2025-11-04 14:59:29 -05:00
Alex Hunt
d000261eef [Tracks] Annotate devtools.performanceIssue for Cascading Updates in DEV (#34961) 2025-11-04 17:07:31 +00:00
Hendrik Liebau
f646e8ffd8 [Flight] Fix hasReadable flag in Node.js clients' debug channel (#35039)
For Edge Flight servers, that use Web Streams, we're defining the
`debugChannel` option as:

```
debugChannel?: {readable?: ReadableStream, writable?: WritableStream, ...}
```

Whereas for Node.js Flight servers, that use Node.js Streams, we're
defining it as:

```
debugChannel?: Readable | Writable | Duplex | WebSocket
```

For the Edge Flight clients, there is currently only one direction of
the debug channel supported, so we define the option as:

```
debugChannel?: {readable?: ReadableStream, ...}
```

Consequently, for the Node.js Flight clients, we define the option as:

```
debugChannel?: Readable
```

The presence of a readable debug channel is passed to the Flight client
internally via the `hasReadable` flag on the internal `debugChannel`
option. For the Node.js clients, that flag was accidentally derived from
the public option `debugChannel.readable`, which is conceptually
incorrect, because `debugChannel` is a `Readable` stream, not an options
object with a `readable` property. However, a `Readable` also has a
`readable` property, which is a boolean that indicates whether the
stream is in a readable state. This meant that the `hasReadable` flag
was incidentally still set correctly. Regardless, this was confusing and
unintentional, so we're now fixing it to always set `hasReadable` to
`true` when a `debugChannel` is provided to the Node.js clients. We'll
revisit this in case we ever add support for writable debug channels in
Node.js (and Edge) clients.
2025-11-04 16:30:08 +01:00
Jack Pope
edd05f181b Add fragment handles to children of FragmentInstances (#34935)
This PR adds a `unstable_reactFragments?: Set<FragmentInstance>`
property to DOM nodes that belong to a Fragment with a ref (top level
host components). This allows you to access a FragmentInstance from a
DOM node.

This is flagged behind `enableFragmentRefsInstanceHandles`.

The primary use case to unblock is reusing IntersectionObserver
instances. A fairly common practice is to cache and reuse
IntersectionObservers that share the same config, with a map of
node->callbacks to run for each entry in the IO callback. Currently this
is not possible with Fragment Ref `observeUsing` because the key in the
cache would have to be the `FragmentInstance` and you can't find it
without a handle from the node. This works now by accessing
`entry.target.fragments`.

This also opens up possibilities to use `FragmentInstance` operations in
other places, such as events. We can do
`event.target.unstable_reactFragments`, then access
`fragmentInstance.getClientRects` for example. In a future PR, we can
assign an event's `currentTarget` as the Fragment Ref for a more direct
handle when the event has been dispatched by the Fragment itself.

The first commit here implemented a handle only on observed elements.
This is awkward because there isn't a good way to document or expose
this temporary property. `element.fragments` is closer to what we would
expect from a DOM API if a standard was implemented here. And by
assigning it to all top-level nodes of a Fragment, it can be used beyond
the cached IntersectionObserver callback.

One tradeoff here is adding extra work during the creation of
FragmentInstances as well as keeping track of adding/removing nodes.
Previously we only track the Fiber on creation but here we add a
traversal which could apply to a large set of top-level host children.
The `element.unstable_reactFragments` Set can also be randomly ordered.
2025-11-03 17:51:00 -05:00
Hendrik Liebau
67f7d47a9b [Flight] Fix debug info filtering to include later resolved I/O (#35036)
In #35019, we excluded debug I/O info from being considered for
enhancing the owner stack if it resolved after the defined `endTime`
option that can be passed to the Flight client. However, we should
include any I/O that was awaited before that end time, even if it
resolved later.
2025-11-03 22:59:40 +01:00
Hendrik Liebau
561ee24d4a [Fizz] Push halted await to the owner stack for late-arriving I/O info (#35019) 2025-11-01 16:03:09 +01:00
Sebastian Markbåge
488d88b018 Render children passed to "backwards" SuspenseList in reverse mount order (#35021)
Some checks failed
(Compiler) Playground / Test playground (push) Has been cancelled
(Compiler) TypeScript / Discover yarn workspaces (push) Has been cancelled
(Compiler) TypeScript / Lint babel-plugin-react-compiler (push) Has been cancelled
(Compiler) TypeScript / Jest babel-plugin-react-compiler (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (6) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (7) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (8) (push) Has been cancelled
(Runtime) ESLint Plugin E2E / ESLint v${{ matrix.eslint_major }} (9) (push) Has been cancelled
(Runtime) Fuzz tests / test_fuzz (push) Has been cancelled
(Shared) Lint / Run prettier (push) Has been cancelled
(Shared) Lint / Run eslint (push) Has been cancelled
(Shared) Lint / Check license (push) Has been cancelled
(Shared) Lint / Test print warnings (push) Has been cancelled
(Compiler) TypeScript / Test ${{ matrix.workspace_name }} (push) Has been cancelled
(Runtime) Publish Prereleases Nightly / Publish to Canary channel (push) Has been cancelled
(Compiler) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
(Runtime) Publish Prereleases Nightly / Publish to Experimental channel (push) Has been cancelled
(Shared) Cleanup Stale Branch Caches / cleanup (push) Has been cancelled
(Shared) Manage stale issues and PRs / stale (push) Has been cancelled
Stacked on #35018.

This mounts the children of SuspenseList backwards. Meaning the first
child is mounted last in the DOM (and effect list). It's like calling
reverse() on the children.

This is meant to set us up for allowing AsyncIterable children where the
unknown number of children streams in at the end (which is the beginning
in a backwards SuspenseList). For consistency we do that with other
children too.

`unstable_legacy-backwards` still exists for the old mode but is meant
to be deprecated.

<img width="100" alt="image"
src="https://github.com/user-attachments/assets/5c2a95d7-34c4-4a4e-b602-3646a834d779"
/>
2025-10-31 13:33:23 -04:00
Sebastian Markbåge
26cf280480 Switch the default revealOrder to "forwards" and tail "hidden" on SuspenseList (#35018)
We have warned about this for a while now so we can make the switch.

Often when you reach for SuspenseList, you mean forwards. It doesn't
make sense to have the default to just be a noop. While "together" is
another useful mode that's more like a Group so isn't so associated with
the default as List. So we're switching it.

However, tail=hidden isn't as obvious of a default it does allow for a
convenient pattern for streaming in list of items by default.

This doesn't yet switch the rendering order of "backwards". That's
coming in a follow up.
2025-10-31 12:58:18 -04:00
Sebastian "Sebbie" Silbermann
c9ddee7e36 [DevTools] Reset forced states when changing component filters (#34929) 2025-10-31 12:57:11 +01:00
Sebastian Markbåge
6fb7754494 [DevTools] Render selected outline on top of every other rect (#35012)
When rects are close together (or overlapping) the outline can end up
being covered up by sibling rects or deeper rects. This renders the
selected outline on top of everything so it's always visible.

<img width="275" height="730" alt="Screenshot 2025-10-29 at 8 43 28 PM"
src="https://github.com/user-attachments/assets/69224883-f548-45ec-ada1-1a04ec17eaf5"
/>
<img width="266" height="737" alt="Screenshot 2025-10-29 at 8 58 53 PM"
src="https://github.com/user-attachments/assets/946f7dde-450d-49fd-9fbd-57487f67f461"
/>

Additionally, this makes it so that it's not part of the translucent
tree when things are hidden by the timeline. That way it's easier to see
what is selected inside a hidden tree.

<img width="498" height="196" alt="Screenshot 2025-10-29 at 8 45 24 PM"
src="https://github.com/user-attachments/assets/723107ab-a92c-42c2-8a7d-a548ac3332d0"
/>
<img width="571" height="735" alt="Screenshot 2025-10-29 at 8 59 06 PM"
src="https://github.com/user-attachments/assets/d653f1a7-4096-45c3-b92a-ef155d4742e6"
/>
2025-10-30 15:26:49 -04:00
Sebastian Markbåge
3a0ab8a7ee [DevTools] Synchronize Scroll Position Between Suspense Tab and Main Document (#34641)
It's annoying to have to try to find where it lines up with no hints.

This way when you hover over something it should be on screen.

The strategy I went with is that it scrolls to a percentage along the
scrollable axis but the two might not be exactly the same. Partially
because they have different aspect ratios but also because suspended
boundaries can shrink the document while the suspense tab needs to still
be able to show the boundaries that are currently invisible.
2025-10-29 21:49:35 -04:00