From f7254efc5c43ebae57afa32a52d4653878ef23d0 Mon Sep 17 00:00:00 2001 From: Eugene Choi <4eugenechoi@gmail.com> Date: Wed, 1 Oct 2025 21:26:16 -0400 Subject: [PATCH 1/2] [playground] Persist open tabs on compiler error (#34673) This change allows it so that tabs that were open before a compiler error are automatically opened again when the error is resolved. Quality of life change for those especially working with the advanced view of the playground. https://github.com/user-attachments/assets/cd2dc117-e6fc-4f57-a08f-259757c4f5e8 --- .../apps/playground/components/AccordionWindow.tsx | 6 +++++- .../apps/playground/components/Editor/Output.tsx | 13 +++++-------- .../apps/playground/components/TabbedWindow.tsx | 12 ++++++++++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/compiler/apps/playground/components/AccordionWindow.tsx b/compiler/apps/playground/components/AccordionWindow.tsx index db12e76670a91..09614bd505b04 100644 --- a/compiler/apps/playground/components/AccordionWindow.tsx +++ b/compiler/apps/playground/components/AccordionWindow.tsx @@ -23,6 +23,7 @@ export default function AccordionWindow(props: { tabsOpen: Set; setTabsOpen: (newTab: Set) => void; changedPasses: Set; + isFailure: boolean; }): React.ReactElement { return (
@@ -36,6 +37,7 @@ export default function AccordionWindow(props: { tabsOpen={props.tabsOpen} setTabsOpen={props.setTabsOpen} hasChanged={props.changedPasses.has(name)} + isFailure={props.isFailure} /> ); })} @@ -50,15 +52,17 @@ function AccordionWindowItem({ tabsOpen, setTabsOpen, hasChanged, + isFailure, }: { name: string; tabs: TabsRecord; tabsOpen: Set; setTabsOpen: (newTab: Set) => void; hasChanged: boolean; + isFailure: boolean; }): React.ReactElement { const id = useId(); - const isShow = tabsOpen.has(name); + const isShow = isFailure ? name === 'Output' : tabsOpen.has(name); const transitionName = `accordion-window-item-${id}`; diff --git a/compiler/apps/playground/components/Editor/Output.tsx b/compiler/apps/playground/components/Editor/Output.tsx index a54cc3c3d78fa..17da512e80fa0 100644 --- a/compiler/apps/playground/components/Editor/Output.tsx +++ b/compiler/apps/playground/components/Editor/Output.tsx @@ -265,14 +265,8 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element { * Update the active tab back to the output or errors tab when the compilation state * changes between success/failure. */ - const [previousOutputKind, setPreviousOutputKind] = useState( - compilerOutput.kind, - ); - if (compilerOutput.kind !== previousOutputKind) { - setPreviousOutputKind(compilerOutput.kind); - setTabsOpen(new Set(['Output'])); - setActiveTab('Output'); - } + + const isFailure = compilerOutput.kind !== 'ok'; const changedPasses: Set = new Set(['Output', 'HIR']); // Initial and final passes should always be bold let lastResult: string = ''; for (const [passName, results] of compilerOutput.results) { @@ -301,6 +295,8 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element { tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} + // Display the Output tab on compilation failure + activeTabOverride={isFailure ? 'Output' : undefined} /> ); @@ -319,6 +315,7 @@ function OutputContent({store, compilerOutput}: Props): JSX.Element { tabsOpen={tabsOpen} tabs={tabs} changedPasses={changedPasses} + isFailure={isFailure} /> ); diff --git a/compiler/apps/playground/components/TabbedWindow.tsx b/compiler/apps/playground/components/TabbedWindow.tsx index c324e3ce4505a..ceeb122dd33c5 100644 --- a/compiler/apps/playground/components/TabbedWindow.tsx +++ b/compiler/apps/playground/components/TabbedWindow.tsx @@ -17,11 +17,15 @@ export default function TabbedWindow({ tabs, activeTab, onTabChange, + activeTabOverride, }: { tabs: Map; activeTab: string; onTabChange: (tab: string) => void; + activeTabOverride?: string; }): React.ReactElement { + const currentActiveTab = activeTabOverride ? activeTabOverride : activeTab; + const id = useId(); const transitionName = `tab-highlight-${id}`; @@ -37,7 +41,7 @@ export default function TabbedWindow({
{Array.from(tabs.keys()).map(tab => { - const isActive = activeTab === tab; + const isActive = currentActiveTab === tab; return (
From d74f061b6908e4841b2eb09c296ca4658dbdd38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Wed, 1 Oct 2025 21:58:13 -0400 Subject: [PATCH 2/2] [Fiber] Clean up ViewTransition when it fails to start (#34676) The View Transition docs were unclear about this but apparently the `finished` promise never settles if the animation never started. So if there's an error that rejects the `ready` promise, we'll never run the clean up which can cause it to stall. Fixes #34662. However, ultimately that is caused by Chrome stalling our default `onDefaultTransitionIndicator` but it should be unblocked after 10 seconds, not a minute. --- .../src/client/ReactFiberConfigDOM.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 7d28dc43ca569..0f75d5f8cd78e 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -2279,6 +2279,11 @@ export function startViewTransition( spawnedWorkCallback(); }; const handleError = (error: mixed) => { + // $FlowFixMe[prop-missing] + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } try { error = customizeViewTransitionError(error, false); if (error !== null) { @@ -2293,6 +2298,9 @@ export function startViewTransition( layoutCallback(); // Skip afterMutationCallback() since we're not animating. spawnedWorkCallback(); + if (enableProfilerTimer) { + finishedAnimation(); + } } }; transition.ready.then(readyCallback, handleError); @@ -2699,6 +2707,11 @@ export function startGestureTransition( ? () => requestAnimationFrame(readyCallback) : readyCallback; const handleError = (error: mixed) => { + // $FlowFixMe[prop-missing] + if (ownerDocument.__reactViewTransition === transition) { + // $FlowFixMe[prop-missing] + ownerDocument.__reactViewTransition = null; + } try { error = customizeViewTransitionError(error, true); if (error !== null) { @@ -2713,6 +2726,9 @@ export function startGestureTransition( // Skip readyCallback() and go straight to animateCallbck() since we're not animating. // animateCallback() is still required to restore states. animateCallback(); + if (enableProfilerTimer) { + finishedAnimation(); + } } }; transition.ready.then(readyForAnimations, handleError);