@@ -323,16 +323,12 @@ export function FxSection({
323323 } , [ handBuilt , eqIds . length , showCarve ] ) ;
324324 const [ openEq , setOpenEq ] = useState < string | null > ( null ) ;
325325
326- /**
327- * The parameter a reveal request asked for, held until its row is on screen.
328- *
329- * Set during render rather than in an effect, the way the Motion panel
330- * consumes its own focus request: the surface must open on the SAME commit
331- * the request lands on, or the scroll below runs against a row that has not
332- * mounted yet.
333- */
326+ /** The reveal request held until its row is mounted and scrolled. */
334327 const [ consumedRevealNonce , setConsumedRevealNonce ] = useState < number | null > ( null ) ;
335- const pendingRevealRef = useRef < string | null > ( null ) ;
328+ const [ pendingReveal , setPendingReveal ] = useState < {
329+ nonce : number ;
330+ target : string ;
331+ } | null > ( null ) ;
336332 const rootRef = useRef < HTMLDivElement | null > ( null ) ;
337333 if ( revealNonce != null && revealNonce !== consumedRevealNonce ) {
338334 setConsumedRevealNonce ( revealNonce ) ;
@@ -351,27 +347,26 @@ export function FxSection({
351347 return next ;
352348 } ) ;
353349 }
354- pendingRevealRef . current = revealTarget ?? null ;
355350 }
351+ setPendingReveal ( where && revealTarget ? { nonce : revealNonce , target : revealTarget } : null ) ;
356352 }
357353
358354 /**
359355 * Scroll the revealed parameter into view once its row has actually mounted.
360356 *
361- * Keyed on the surfaces the block above opens, not on the request: the row
362- * appears on the commit AFTER they change, so scrolling in the same pass would
363- * miss it. Cleared once used, so a later re-render does not yank the panel
364- * back to a parameter the author has since scrolled away from.
357+ * The request itself is a dependency so a second click on an already-open
358+ * surface still scrolls. It is cleared once used, so a later unrelated
359+ * re-render does not yank the panel back to an old parameter.
365360 */
366361 useEffect ( ( ) => {
367- const target = pendingRevealRef . current ;
362+ const target = pendingReveal ?. target ;
368363 if ( target && scrollRevealedRowIntoView ( rootRef . current , target , chain ) ) {
369- pendingRevealRef . current = null ;
364+ setPendingReveal ( null ) ;
370365 }
371366 // `chain` is deliberately not a dependency: it changes on every knob edit,
372367 // and re-running then would scroll the panel while the author is dragging.
373368 // eslint-disable-next-line react-hooks/exhaustive-deps
374- } , [ openNode , openEq , carveOpen , collapsedRuns ] ) ;
369+ } , [ openNode , openEq , carveOpen , collapsedRuns , pendingReveal ] ) ;
375370
376371 const addEq = useCallback ( ( ) => {
377372 clearAudition ( ) ;
0 commit comments