source dump of claude code
at main 68 lines 2.5 kB view raw
1import { useEffect, useSyncExternalStore } from 'react' 2import type { QueuedCommand } from '../types/textInputTypes.js' 3import { 4 getCommandQueueSnapshot, 5 subscribeToCommandQueue, 6} from '../utils/messageQueueManager.js' 7import type { QueryGuard } from '../utils/QueryGuard.js' 8import { processQueueIfReady } from '../utils/queueProcessor.js' 9 10type UseQueueProcessorParams = { 11 executeQueuedInput: (commands: QueuedCommand[]) => Promise<void> 12 hasActiveLocalJsxUI: boolean 13 queryGuard: QueryGuard 14} 15 16/** 17 * Hook that processes queued commands when conditions are met. 18 * 19 * Uses a single unified command queue (module-level store). Priority determines 20 * processing order: 'now' > 'next' (user input) > 'later' (task notifications). 21 * The dequeue() function handles priority ordering automatically. 22 * 23 * Processing triggers when: 24 * - No query active (queryGuard — reactive via useSyncExternalStore) 25 * - Queue has items 26 * - No active local JSX UI blocking input 27 */ 28export function useQueueProcessor({ 29 executeQueuedInput, 30 hasActiveLocalJsxUI, 31 queryGuard, 32}: UseQueueProcessorParams): void { 33 // Subscribe to the query guard. Re-renders when a query starts or ends 34 // (or when reserve/cancelReservation transitions dispatching state). 35 const isQueryActive = useSyncExternalStore( 36 queryGuard.subscribe, 37 queryGuard.getSnapshot, 38 ) 39 40 // Subscribe to the unified command queue via useSyncExternalStore. 41 // This guarantees re-render when the store changes, bypassing 42 // React context propagation delays that cause missed notifications in Ink. 43 const queueSnapshot = useSyncExternalStore( 44 subscribeToCommandQueue, 45 getCommandQueueSnapshot, 46 ) 47 48 useEffect(() => { 49 if (isQueryActive) return 50 if (hasActiveLocalJsxUI) return 51 if (queueSnapshot.length === 0) return 52 53 // Reservation is now owned by handlePromptSubmit (inside executeUserInput's 54 // try block). The sync chain executeQueuedInput → handlePromptSubmit → 55 // executeUserInput → queryGuard.reserve() runs before the first real await, 56 // so by the time React re-runs this effect (due to the dequeue-triggered 57 // snapshot change), isQueryActive is already true (dispatching) and the 58 // guard above returns early. handlePromptSubmit's finally releases the 59 // reservation via cancelReservation() (no-op if onQuery already ran end()). 60 processQueueIfReady({ executeInput: executeQueuedInput }) 61 }, [ 62 queueSnapshot, 63 isQueryActive, 64 executeQueuedInput, 65 hasActiveLocalJsxUI, 66 queryGuard, 67 ]) 68}