@@ -914,28 +914,60 @@ export const useOpeningDrillController = (
914914 const backgroundRunningRef = useRef ( false )
915915 const backgroundCancelledRef = useRef ( false )
916916
917- // The long-lived loop that drains the queue
917+ // Refs to the latest versions of the analysis functions so the long-lived
918+ // loop always calls the current closure without needing to restart.
919+ const ensureMaiaRef = useRef ( ensureMaiaForNode )
920+ const ensureStockfishRef = useRef ( ensureStockfishForNode )
921+ useEffect ( ( ) => {
922+ ensureMaiaRef . current = ensureMaiaForNode
923+ } , [ ensureMaiaForNode ] )
924+ useEffect ( ( ) => {
925+ ensureStockfishRef . current = ensureStockfishForNode
926+ } , [ ensureStockfishForNode ] )
927+
928+ // The long-lived loop that drains the queue.
929+ // Uses refs for everything so it never needs to be recreated.
918930 const runBackgroundLoop = useCallback ( async ( ) => {
919931 if ( backgroundRunningRef . current ) return
920932 backgroundRunningRef . current = true
921933
934+ console . log ( '[bg-analysis] loop started' )
922935 try {
923936 while ( backgroundQueueRef . current . length > 0 ) {
924- if ( backgroundCancelledRef . current ) break
937+ if ( backgroundCancelledRef . current ) {
938+ console . log ( '[bg-analysis] cancelled, stopping' )
939+ break
940+ }
925941 const node = backgroundQueueRef . current [ 0 ]
942+ console . log (
943+ '[bg-analysis] processing node:' ,
944+ node . fen . split ( ' ' ) . slice ( 0 , 2 ) . join ( ' ' ) ,
945+ )
926946
927- await ensureMaiaForNode ( node )
947+ await ensureMaiaRef . current ( node )
928948 if ( backgroundCancelledRef . current ) break
929949
930- await ensureStockfishForNode ( node )
950+ console . log (
951+ '[bg-analysis] maia done, starting stockfish for:' ,
952+ node . fen . split ( ' ' ) . slice ( 0 , 2 ) . join ( ' ' ) ,
953+ )
954+ await ensureStockfishRef . current ( node )
931955
932956 // Only remove from queue after both analyses complete (or if cancelled)
933957 backgroundQueueRef . current . shift ( )
958+ console . log (
959+ '[bg-analysis] node complete, remaining:' ,
960+ backgroundQueueRef . current . length ,
961+ )
934962 }
963+ } catch ( error ) {
964+ console . error ( '[bg-analysis] error:' , error )
935965 } finally {
936966 backgroundRunningRef . current = false
967+ console . log ( '[bg-analysis] loop ended' )
937968 }
938- } , [ ensureMaiaForNode , ensureStockfishForNode ] )
969+ // eslint-disable-next-line react-hooks/exhaustive-deps
970+ } , [ ] )
939971
940972 // Enqueue new drill nodes whenever the tree grows
941973 useEffect ( ( ) => {
@@ -963,6 +995,12 @@ export const useOpeningDrillController = (
963995 } )
964996
965997 if ( newNodes . length > 0 ) {
998+ console . log (
999+ '[bg-analysis] enqueueing' ,
1000+ newNodes . length ,
1001+ 'nodes, loop running:' ,
1002+ backgroundRunningRef . current ,
1003+ )
9661004 backgroundQueueRef . current . push ( ...newNodes )
9671005 runBackgroundLoop ( )
9681006 }
@@ -974,17 +1012,31 @@ export const useOpeningDrillController = (
9741012 treeController . currentNode ,
9751013 ] )
9761014
977- // Cancel background analysis when drill session resets
1015+ // Cancel background analysis when a new drill starts (not on every move).
1016+ // currentDrillGame changes on every move, so we track the drill ID instead.
1017+ const backgroundDrillIdRef = useRef < string | null > ( null )
9781018 useEffect ( ( ) => {
979- backgroundCancelledRef . current = false
980- return ( ) => {
1019+ const drillId = currentDrillGame ?. id ?? null
1020+ if ( drillId !== backgroundDrillIdRef . current ) {
1021+ // New drill or drill cleared — cancel any running background work
9811022 backgroundCancelledRef . current = true
9821023 backgroundQueueRef . current = [ ]
1024+ backgroundRunningRef . current = false
1025+
1026+ backgroundDrillIdRef . current = drillId
1027+ if ( drillId ) {
1028+ // Reset for the new drill
1029+ backgroundCancelledRef . current = false
1030+ }
9831031 }
984- } , [ currentDrillGame ] )
1032+ } , [ currentDrillGame ?. id ] )
9851033
9861034 const ensureDrillAnalysis = useCallback (
9871035 async ( drillGame : OpeningDrillGame ) : Promise < boolean > => {
1036+ // Stop background analysis so it doesn't compete for stockfish
1037+ backgroundCancelledRef . current = true
1038+ backgroundQueueRef . current = [ ]
1039+
9881040 const mainLine = drillGame . tree . getMainLine ( )
9891041 const startingNode = drillGame . openingEndNode || mainLine [ 0 ]
9901042 const startIndex = startingNode
0 commit comments