chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { centerStringInWidth } from './ui';
|
||||
|
||||
function getProgressBar(current: number, total: number, width = 40): string {
|
||||
const percentage = total > 0 ? current / total : 0;
|
||||
const filled = Math.round(width * percentage);
|
||||
const empty = width - filled;
|
||||
return '█'.repeat(filled) + '░'.repeat(empty);
|
||||
}
|
||||
|
||||
function truncateStart(str: string, maxLen: number): string {
|
||||
if (str.length <= maxLen) return str;
|
||||
return '...' + str.slice(-(maxLen - 3));
|
||||
}
|
||||
|
||||
export class ProgressDisplay {
|
||||
private current = 0;
|
||||
private total: number;
|
||||
private lastFile = '';
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor(total: number) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
insertBefore(str: string) {
|
||||
process.stdout.write('\x1b[J');
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
row(maxWidth: number, minGap: number, ...parts: string[]): string {
|
||||
const totalPartsLength = parts.reduce((sum, part) => sum + part.length, 0);
|
||||
const calculatedGap = (maxWidth - totalPartsLength) / (parts.length - 1);
|
||||
if (calculatedGap < minGap) {
|
||||
const neededReduction = (minGap - calculatedGap) * (parts.length - 1);
|
||||
parts[0] = truncateStart(parts[0], parts[0].length - neededReduction);
|
||||
}
|
||||
return parts.join(' '.repeat(Math.max(calculatedGap, minGap)));
|
||||
}
|
||||
|
||||
update(file: string, message?: string): void {
|
||||
this.current++;
|
||||
this.lastFile = file;
|
||||
|
||||
// Move cursor up 3 lines and clear each line
|
||||
if (this.initialized) {
|
||||
process.stdout.write('\x1b[2F');
|
||||
} else {
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
if (message) {
|
||||
this.insertBefore(message);
|
||||
}
|
||||
this.renderCurrentStatus();
|
||||
}
|
||||
|
||||
private renderCurrentStatus() {
|
||||
const termWidth = process.stdout.columns || 80;
|
||||
const adjustedTermWidth = termWidth - Math.floor(0.2 * termWidth);
|
||||
const progressBar = getProgressBar(
|
||||
this.current,
|
||||
this.total,
|
||||
adjustedTermWidth
|
||||
);
|
||||
|
||||
// Line 1: Most recently copied file
|
||||
process.stdout.write(
|
||||
centerStringInWidth(
|
||||
this.row(
|
||||
adjustedTermWidth,
|
||||
4,
|
||||
this.lastFile,
|
||||
`[${this.current} / ${this.total}]`
|
||||
),
|
||||
termWidth
|
||||
) + '\x1b[K\n'
|
||||
);
|
||||
// Line 2: Progress bar
|
||||
process.stdout.write(
|
||||
centerStringInWidth(progressBar, termWidth) + '\x1b[K\n'
|
||||
);
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
// Add a newline after completion to separate from next output
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user