/** * SessionContextPanel - Panel showing all context injections for a session. * Displays CLAUDE.md files, mentioned files, and tool outputs in collapsible sections. */ import React, { useMemo, useState } from 'react'; import { COLOR_BORDER, COLOR_SURFACE, COLOR_SURFACE_OVERLAY, COLOR_TEXT_MUTED, } from '@renderer/constants/cssVariables'; import { sumContextInjectionTokens } from '@renderer/utils/contextMath'; import { ClaudeMdFilesSection } from './components/ClaudeMdFilesSection'; import { FlatInjectionList } from './components/FlatInjectionList'; import { MentionedFilesSection } from './components/MentionedFilesSection'; import { RankedInjectionList } from './components/RankedInjectionList'; import { SessionContextHeader } from './components/SessionContextHeader'; import { TaskCoordinationSection } from './components/TaskCoordinationSection'; import { ThinkingTextSection } from './components/ThinkingTextSection'; import { ToolOutputsSection } from './components/ToolOutputsSection'; import { UserMessagesSection } from './components/UserMessagesSection'; import { SECTION_CLAUDE_MD, SECTION_MENTIONED_FILES, SECTION_TASK_COORDINATION, SECTION_THINKING_TEXT, SECTION_TOOL_OUTPUTS, SECTION_USER_MESSAGES, } from './types'; import type { ContextViewMode, SectionType, SessionContextPanelProps } from './types'; import type { ClaudeMdContextInjection, MentionedFileInjection, TaskCoordinationInjection, ThinkingTextInjection, ToolOutputInjection, UserMessageInjection, } from '@renderer/types/contextInjection'; export const SessionContextPanel = ({ injections, onClose, projectRoot, onNavigateToTurn, onNavigateToTool, onNavigateToUserGroup, totalSessionTokens, sessionMetrics, subagentCostUsd, onViewReport, phaseInfo, selectedPhase, onPhaseChange, }: Readonly): React.ReactElement => { // View mode: category sections or ranked list const [viewMode, setViewMode] = useState('category'); // Flat sub-toggle within "By Size" view const [flatMode, setFlatMode] = useState(false); // Track which main sections are expanded const [expandedSections, setExpandedSections] = useState>( new Set([ SECTION_USER_MESSAGES, SECTION_CLAUDE_MD, SECTION_MENTIONED_FILES, SECTION_TOOL_OUTPUTS, SECTION_TASK_COORDINATION, SECTION_THINKING_TEXT, ]) ); // Separate injections by category const { claudeMdInjections, mentionedFileInjections, toolOutputInjections, thinkingTextInjections, taskCoordinationInjections, userMessageInjections, } = useMemo(() => { const claudeMd: ClaudeMdContextInjection[] = []; const mentionedFiles: MentionedFileInjection[] = []; const toolOutputs: ToolOutputInjection[] = []; const thinkingText: ThinkingTextInjection[] = []; const taskCoordination: TaskCoordinationInjection[] = []; const userMessages: UserMessageInjection[] = []; for (const injection of injections) { switch (injection.category) { case 'claude-md': claudeMd.push(injection); break; case 'mentioned-file': mentionedFiles.push(injection); break; case 'tool-output': toolOutputs.push(injection); break; case 'thinking-text': thinkingText.push(injection); break; case 'task-coordination': taskCoordination.push(injection); break; case 'user-message': userMessages.push(injection); break; } } // Sort mentioned files and tool outputs by tokens descending mentionedFiles.sort((a, b) => b.estimatedTokens - a.estimatedTokens); toolOutputs.sort((a, b) => b.estimatedTokens - a.estimatedTokens); // Sort task coordination by tokens descending taskCoordination.sort((a, b) => b.estimatedTokens - a.estimatedTokens); // Sort thinking-text by turn index ascending thinkingText.sort((a, b) => a.turnIndex - b.turnIndex); // Sort user messages by turn index ascending userMessages.sort((a, b) => a.turnIndex - b.turnIndex); return { claudeMdInjections: claudeMd, mentionedFileInjections: mentionedFiles, toolOutputInjections: toolOutputs, thinkingTextInjections: thinkingText, taskCoordinationInjections: taskCoordination, userMessageInjections: userMessages, }; }, [injections]); // Calculate total tokens const totalTokens = useMemo(() => sumContextInjectionTokens(injections), [injections]); // Section token counts const claudeMdTokens = useMemo( () => claudeMdInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [claudeMdInjections] ); const mentionedFilesTokens = useMemo( () => mentionedFileInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [mentionedFileInjections] ); const toolOutputsTokens = useMemo( () => toolOutputInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [toolOutputInjections] ); const thinkingTextTokens = useMemo( () => thinkingTextInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [thinkingTextInjections] ); const taskCoordinationTokens = useMemo( () => taskCoordinationInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [taskCoordinationInjections] ); const userMessagesTokens = useMemo( () => userMessageInjections.reduce((sum, inj) => sum + inj.estimatedTokens, 0), [userMessageInjections] ); // Toggle section expansion const toggleSection = (section: SectionType): void => { setExpandedSections((prev) => { const next = new Set(prev); if (next.has(section)) { next.delete(section); } else { next.add(section); } return next; }); }; return (
{/* Content */}
{injections.length === 0 ? (
No context injections detected in this session
) : viewMode === 'category' ? ( <> toggleSection(SECTION_USER_MESSAGES)} onNavigateToTurn={onNavigateToTurn} /> toggleSection(SECTION_CLAUDE_MD)} projectRoot={projectRoot ?? ''} onNavigateToTurn={onNavigateToTurn} /> toggleSection(SECTION_MENTIONED_FILES)} projectRoot={projectRoot} onNavigateToTurn={onNavigateToTurn} /> toggleSection(SECTION_TOOL_OUTPUTS)} onNavigateToTurn={onNavigateToTurn} /> toggleSection(SECTION_TASK_COORDINATION)} onNavigateToTurn={onNavigateToTurn} /> toggleSection(SECTION_THINKING_TEXT)} onNavigateToTurn={onNavigateToTurn} /> ) : ( <> {/* Grouped / Flat sub-toggle */}
{flatMode ? ( ) : ( )} )}
); };