/** * @license * Copyright 2026 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { Box, Text } from 'ink'; import { ThemedGradient } from './ThemedGradient.js'; import { theme } from '../semantic-colors.js'; import { formatDuration } from '../utils/formatters.js'; import { useSessionStats, type ModelMetrics, } from '../contexts/SessionContext.js'; import { getStatusColor, TOOL_SUCCESS_RATE_HIGH, TOOL_SUCCESS_RATE_MEDIUM, USER_AGREEMENT_RATE_HIGH, USER_AGREEMENT_RATE_MEDIUM, } from '../utils/displayUtils.js'; import { computeSessionStats } from '../utils/computeStats.js'; import { useSettings } from '../contexts/SettingsContext.js'; import type { QuotaStats } from '../types.js'; // A more flexible and powerful StatRow component interface StatRowProps { title: string; children: React.ReactNode; // Use children to allow for complex, colored values } const StatRow: React.FC = ({ title, children }) => ( {/* Fixed width for the label creates a clean "gutter" for alignment */} {title} {children} ); // A SubStatRow for indented, secondary information interface SubStatRowProps { title: string; children: React.ReactNode; } const SubStatRow: React.FC = ({ title, children }) => ( {/* Adjust width for the "» " prefix */} » {title} {children} ); // A Section component to group related stats interface SectionProps { title: string; children: React.ReactNode; } const Section: React.FC = ({ title, children }) => ( {title} {children} ); // Logic for building the unified list of table rows interface ModelUsageTableProps { models: Record; } const ModelUsageTable: React.FC = ({ models }) => { const nameWidth = 28; const requestsWidth = 8; const inputTokensWidth = 14; const cacheReadsWidth = 14; const outputTokensWidth = 14; return ( Model Usage Use /model to view model quota information {/* Header */} Model Reqs Input Tokens Cache Reads Output Tokens {/* Rows */} {Object.entries(models).map(([name, modelMetrics]) => ( {name} {modelMetrics.api.totalRequests} {modelMetrics.tokens.prompt.toLocaleString()} {modelMetrics.tokens.cached.toLocaleString()} {modelMetrics.tokens.candidates.toLocaleString()} ))} ); }; interface StatsDisplayProps { duration: string; title?: string; footer?: string; selectedAuthType?: string; userEmail?: string; tier?: string; currentModel?: string; quotaStats?: QuotaStats; creditBalance?: number; } export const StatsDisplay: React.FC = ({ duration, title, footer, selectedAuthType, userEmail, tier, creditBalance, }) => { const { stats } = useSessionStats(); const { metrics } = stats; const { tools, files, models } = metrics; const computed = computeSessionStats(metrics); const settings = useSettings(); const showUserIdentity = settings.merged.ui.showUserIdentity; const successThresholds = { green: TOOL_SUCCESS_RATE_HIGH, yellow: TOOL_SUCCESS_RATE_MEDIUM, }; const agreementThresholds = { green: USER_AGREEMENT_RATE_HIGH, yellow: USER_AGREEMENT_RATE_MEDIUM, }; const successColor = getStatusColor(computed.successRate, successThresholds); const agreementColor = getStatusColor( computed.agreementRate, agreementThresholds, ); const renderTitle = () => { if (title) { return {title}; } return ( Session Stats ); }; const renderFooter = () => { if (!footer) { return null; } return {footer}; }; return ( {renderTitle()}
{stats.sessionId} {showUserIdentity && selectedAuthType && ( {selectedAuthType.startsWith('oauth') ? userEmail ? `Signed in with Google (${userEmail})` : 'Signed in with Google' : selectedAuthType} )} {showUserIdentity && tier && ( {tier} )} {showUserIdentity && creditBalance != null && creditBalance >= 0 && ( 0 ? theme.text.primary : theme.text.secondary } > {creditBalance.toLocaleString()} )} {tools.totalCalls} ({' '} ✓ {tools.totalSuccess}{' '} x {tools.totalFail} ) {computed.successRate.toFixed(1)}% {computed.totalDecisions > 0 && ( {computed.agreementRate.toFixed(1)}%{' '} ({computed.totalDecisions} reviewed) )} {files && (files.totalLinesAdded > 0 || files.totalLinesRemoved > 0) && ( +{files.totalLinesAdded} {' '} -{files.totalLinesRemoved} )}
{duration} {formatDuration(computed.agentActiveTime)} {formatDuration(computed.totalApiTime)}{' '} ({computed.apiTimePercent.toFixed(1)}%) {formatDuration(computed.totalToolTime)}{' '} ({computed.toolTimePercent.toFixed(1)}%)
{Object.keys(models).length > 0 && } {renderFooter()}
); };