11"use client" ;
22
3- import { useCallback , useEffect , useRef , useState } from "react" ;
3+ import {
4+ useCallback ,
5+ useEffect ,
6+ useLayoutEffect ,
7+ useRef ,
8+ useState ,
9+ } from "react" ;
410import {
511 subscribeConnectionStatus ,
612 type ConnectionStatus ,
@@ -24,6 +30,11 @@ import packageJson from "../../package.json";
2430
2531const VERSION_LABEL = `v${ packageJson . version } ` ;
2632
33+ // Run the scroll-restore synchronously after the index re-mounts so the
34+ // previously-open row is centered before paint (no flash of scroll-top).
35+ const useIsoLayoutEffect =
36+ typeof window !== "undefined" ? useLayoutEffect : useEffect ;
37+
2738// Stable empty map so a view that does not own the current results re-renders
2839// with an empty object rather than a fresh `{}` each render.
2940const EMPTY_RESULTS : Record < string , TestEntry > = { } ;
@@ -136,6 +147,8 @@ function urlForSelection(selection: Selection): string {
136147export default function PlaygroundPage ( ) {
137148 const [ status , setStatus ] = useState < ConnectionStatus | null > ( null ) ;
138149 const [ selection , setSelection ] = useState < Selection > ( null ) ;
150+ // The last method viewed, kept highlighted in the index after "← INDEX".
151+ const [ lastViewed , setLastViewed ] = useState < Selection > ( null ) ;
139152 const [ paletteOpen , setPaletteOpen ] = useState ( false ) ;
140153 const [ testResults , setTestResults ] = useState < Record < string , TestEntry > > ( { } ) ;
141154 const [ isTestRunning , setIsTestRunning ] = useState ( false ) ;
@@ -145,6 +158,9 @@ export default function PlaygroundPage() {
145158 "autotest" | "diagnosis" | null
146159 > ( null ) ;
147160 const abortRef = useRef < AbortController | null > ( null ) ;
161+ // The method open when "← INDEX" was clicked, so the index can re-center on
162+ // it instead of jumping to the top.
163+ const pendingScrollRef = useRef < Selection > ( null ) ;
148164
149165 // Hydrate selection from the URL on mount and respond to back/forward.
150166 useEffect ( ( ) => {
@@ -190,6 +206,28 @@ export default function PlaygroundPage() {
190206 setPaletteOpen ( false ) ;
191207 } , [ ] ) ;
192208
209+ // Going back to the index: remember the open method so the index can scroll
210+ // it into the center of view rather than resetting to the top.
211+ const handleBack = useCallback ( ( ) => {
212+ pendingScrollRef . current = selection ;
213+ setLastViewed ( selection ) ;
214+ setSelection ( null ) ;
215+ } , [ selection ] ) ;
216+
217+ useIsoLayoutEffect ( ( ) => {
218+ if ( selection !== null ) return ;
219+ const target = pendingScrollRef . current ;
220+ pendingScrollRef . current = null ;
221+ if ( ! target ?. method ) return ;
222+ const el = document . querySelector (
223+ `[data-testid="method-${ target . service } -${ target . method } "]` ,
224+ ) ;
225+ if ( el instanceof HTMLElement ) {
226+ el . scrollIntoView ( { block : "center" } ) ;
227+ el . focus ( { preventScroll : true } ) ;
228+ }
229+ } , [ selection ] ) ;
230+
193231 const handleRunTests = useCallback (
194232 async ( mode : "all" | "safe" ) => {
195233 if ( isTestRunning ) return ;
@@ -305,7 +343,7 @@ export default function PlaygroundPage() {
305343 </ p >
306344 < ServiceTable
307345 services = { services }
308- activeMethod = { selection }
346+ activeMethod = { selection ?? lastViewed }
309347 testResults = { testResults }
310348 onSelect = { ( s , m ) => setSelection ( { service : s , method : m } ) }
311349 />
@@ -318,7 +356,7 @@ export default function PlaygroundPage() {
318356 isRunning = { isTestRunning }
319357 onRun = { handleRunDiagnosis }
320358 onStop = { handleStopTests }
321- onBack = { ( ) => setSelection ( null ) }
359+ onBack = { handleBack }
322360 />
323361 ) : isAutoTest ? (
324362 < AutoTestView
@@ -328,13 +366,13 @@ export default function PlaygroundPage() {
328366 onRun = { handleRunTests }
329367 onStop = { handleStopTests }
330368 onRetry = { handleRetryTest }
331- onBack = { ( ) => setSelection ( null ) }
369+ onBack = { handleBack }
332370 />
333371 ) : selection ? (
334372 < MethodView
335373 service = { selection . service }
336374 method = { selection . method }
337- onBack = { ( ) => setSelection ( null ) }
375+ onBack = { handleBack }
338376 />
339377 ) : (
340378 < div className = "empty-state" >
0 commit comments