(panelId: string, isActive: boolean)
| 55 | } |
| 56 | |
| 57 | export function useCodexPanel(panelId: string, isActive: boolean): CodexPanelHook { |
| 58 | const [isProcessing, setIsProcessing] = useState(false); |
| 59 | const [isInitialized, setIsInitialized] = useState(false); |
| 60 | const initializingRef = useRef(false); |
| 61 | const conversationHistoryRef = useRef<ConversationMessage[]>([]); |
| 62 | |
| 63 | // Get session from panel |
| 64 | const panel = usePanelStore(state => { |
| 65 | // state.panels is an object keyed by sessionId, we need to flatten it |
| 66 | if (!state.panels) return undefined; |
| 67 | const allPanels = Object.values(state.panels).flat(); |
| 68 | const found = allPanels.find(p => p && p.id === panelId); |
| 69 | return found; |
| 70 | }); |
| 71 | const sessionId = panel?.sessionId; |
| 72 | |
| 73 | // Get session data |
| 74 | const activeSession = useSessionStore(state => { |
| 75 | const session = state.sessions.find(s => s.id === sessionId) || null; |
| 76 | return session; |
| 77 | }); |
| 78 | |
| 79 | // Initialize Codex panel when it becomes active and has a session |
| 80 | useEffect(() => { |
| 81 | if (isActive && sessionId && !isInitialized && !initializingRef.current) { |
| 82 | initializeCodexPanel(); |
| 83 | } |
| 84 | }, [isActive, sessionId, isInitialized]); |
| 85 | |
| 86 | // Listen for Codex events |
| 87 | useEffect(() => { |
| 88 | const handleOutput = (data: CodexPanelEventData) => { |
| 89 | if (data.panelId === panelId) { |
| 90 | |
| 91 | const isCancellationMessage = |
| 92 | data.type === 'json' && |
| 93 | data.data && |
| 94 | typeof data.data === 'object' && |
| 95 | 'type' in data.data && |
| 96 | (data.data as { type?: string; data?: { status?: string } }).type === 'session' && |
| 97 | 'data' in data.data && |
| 98 | (data.data as { type?: string; data?: { status?: string } }).data?.status === 'cancelled'; |
| 99 | |
| 100 | if (isCancellationMessage) { |
| 101 | setIsProcessing(false); |
| 102 | } |
| 103 | |
| 104 | if (data.type === 'json' && data.data && !isCancellationMessage) { |
| 105 | const dataObj = data.data as Record<string, unknown>; |
| 106 | conversationHistoryRef.current.push({ |
| 107 | type: String(dataObj.type || 'unknown'), |
| 108 | content: data.data, |
| 109 | timestamp: String(data.timestamp || new Date().toISOString()) |
| 110 | }); |
| 111 | } |
| 112 | |
| 113 | if (data.type === 'json' && data.data && typeof data.data === 'object' && 'msg' in data.data) { |
| 114 | const codexMessage = data.data as CodexMessage; |
no test coverage detected