Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.9 kB · 158 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159/** * Hook for managing session backgrounding (Ctrl+B to background/foreground sessions). * * Handles: * - Calling onBackgroundQuery to spawn a background task for the current query * - Re-backgrounding foregrounded tasks * - Syncing foregrounded task messages/state to main view */
import { useCallback, useEffect, useRef } from 'react'import { useAppState, useSetAppState } from '../state/AppState.js'import type { Message } from '../types/message.js'
type UseSessionBackgroundingProps = { setMessages: (messages: Message[] | ((prev: Message[]) => Message[])) => void setIsLoading: (loading: boolean) => void resetLoadingState: () => void setAbortController: (controller: AbortController | null) => void onBackgroundQuery: () => void}
type UseSessionBackgroundingResult = { /** Call when user wants to background (Ctrl+B) */ handleBackgroundSession: () => void}
export function useSessionBackgrounding({ setMessages, setIsLoading, resetLoadingState, setAbortController, onBackgroundQuery,}: UseSessionBackgroundingProps): UseSessionBackgroundingResult { const foregroundedTaskId = useAppState(s => s.foregroundedTaskId) const foregroundedTask = useAppState(s => s.foregroundedTaskId ? s.tasks[s.foregroundedTaskId] : undefined, ) const setAppState = useSetAppState() const lastSyncedMessagesLengthRef = useRef<number>(0)
const handleBackgroundSession = useCallback(() => { if (foregroundedTaskId) { // Re-background the foregrounded task setAppState(prev => { const taskId = prev.foregroundedTaskId if (!taskId) return prev const task = prev.tasks[taskId] if (!task) { return { ...prev, foregroundedTaskId: undefined } } return { ...prev, foregroundedTaskId: undefined, tasks: { ...prev.tasks, [taskId]: { ...task, isBackgrounded: true }, }, } }) setMessages([]) resetLoadingState() setAbortController(null) return }
onBackgroundQuery() }, [ foregroundedTaskId, setAppState, setMessages, resetLoadingState, setAbortController, onBackgroundQuery, ])
// Sync foregrounded task's messages and loading state to the main view useEffect(() => { if (!foregroundedTaskId) { // Reset when no foregrounded task lastSyncedMessagesLengthRef.current = 0 return }
if (!foregroundedTask || foregroundedTask.type !== 'local_agent') { setAppState(prev => ({ ...prev, foregroundedTaskId: undefined })) resetLoadingState() lastSyncedMessagesLengthRef.current = 0 return }
// Sync messages from background task to main view // Only update if messages have actually changed to avoid redundant renders const taskMessages = foregroundedTask.messages ?? [] if (taskMessages.length !== lastSyncedMessagesLengthRef.current) { lastSyncedMessagesLengthRef.current = taskMessages.length setMessages([...taskMessages]) }
if (foregroundedTask.status === 'running') { // Check if the task was aborted (user pressed Escape) const taskAbortController = foregroundedTask.abortController if (taskAbortController?.signal.aborted) { // Task was aborted - clear foregrounded state immediately setAppState(prev => { if (!prev.foregroundedTaskId) return prev const task = prev.tasks[prev.foregroundedTaskId] if (!task) return { ...prev, foregroundedTaskId: undefined } return { ...prev, foregroundedTaskId: undefined, tasks: { ...prev.tasks, [prev.foregroundedTaskId]: { ...task, isBackgrounded: true }, }, } }) resetLoadingState() setAbortController(null) lastSyncedMessagesLengthRef.current = 0 return }
setIsLoading(true) // Set abort controller to the foregrounded task's controller for Escape handling if (taskAbortController) { setAbortController(taskAbortController) } } else { // Task completed - restore to background and clear foregrounded view setAppState(prev => { const taskId = prev.foregroundedTaskId if (!taskId) return prev const task = prev.tasks[taskId] if (!task) return { ...prev, foregroundedTaskId: undefined } return { ...prev, foregroundedTaskId: undefined, tasks: { ...prev.tasks, [taskId]: { ...task, isBackgrounded: true } }, } }) resetLoadingState() setAbortController(null) lastSyncedMessagesLengthRef.current = 0 } }, [ foregroundedTaskId, foregroundedTask, setAppState, setMessages, setIsLoading, resetLoadingState, setAbortController, ])
return { handleBackgroundSession, }}