import React from 'react';
import stripAnsi from 'strip-ansi';
import { Box, Text } from '@anthropic/ink';
import { formatFileSize } from '../../utils/format.js';
import { MessageResponse } from '../MessageResponse.js';
import { OffscreenFreeze } from '../OffscreenFreeze.js';
import { ShellTimeDisplay } from './ShellTimeDisplay.js';
type Props = {
output: string;
fullOutput: string;
elapsedTimeSeconds?: number;
totalLines?: number;
totalBytes?: number;
timeoutMs?: number;
taskId?: string;
verbose: boolean;
};
export function ShellProgressMessage({
output,
fullOutput,
elapsedTimeSeconds,
totalLines,
totalBytes,
timeoutMs,
verbose,
}: Props): React.ReactNode {
const strippedFullOutput = stripAnsi(fullOutput.trim());
const strippedOutput = stripAnsi(output.trim());
const lines = strippedOutput.split('\n').filter(line => line);
const displayLines = verbose ? strippedFullOutput : lines.slice(-5).join('\n');
// OffscreenFreeze: BashTool yields progress (elapsedTimeSeconds) every second.
// If this line scrolls into scrollback, each tick forces a full terminal reset.
// A foreground `sleep 600` on a 29-row terminal with 4000 rows of history
// produced 507 resets over 10 minutes (go/ccshare/maxk-20260226-190348).
if (!lines.length) {
return (
Running…
);
}
// Not truncated: "+2 lines" (total exceeds displayed 5)
// Truncated: "~2000 lines" (extrapolated estimate from tail sample)
const extraLines = totalLines ? Math.max(0, totalLines - 5) : 0;
let lineStatus = '';
if (!verbose && totalBytes && totalLines) {
lineStatus = `~${totalLines} lines`;
} else if (!verbose && extraLines > 0) {
lineStatus = `+${extraLines} lines`;
}
return (
{displayLines}
{lineStatus ? {lineStatus} : null}
{totalBytes ? {formatFileSize(totalBytes)} : null}
);
}