Skip to content

Commit 9b1deed

Browse files
authored
fix: eliminate flicker in bmj watch and show job elapsed time (#167)
- Overwrite lines in-place instead of clearing the entire screen each frame, which eliminates flickering and allows text selection to work. - Show elapsed time since the benchmark job was created rather than since the watch command was launched. NOTE: there are some test failures here which appear to be unrelated. I am cleaning those up in a separate PR.
1 parent d5354be commit 9b1deed

1 file changed

Lines changed: 24 additions & 15 deletions

File tree

src/commands/benchmark-job/watch.ts

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,26 @@ function exitFullScreen(): void {
5656
process.stdout.write(ANSI.exitAltScreen);
5757
}
5858

59-
// Render content at top of screen, truncating to fit terminal height
59+
// Track how many lines the last render wrote so we can clear stale lines
60+
let lastRenderedLineCount = 0;
61+
62+
// Render content at top of screen by overwriting lines in place.
63+
// This avoids the flicker caused by clearing the entire screen each frame.
6064
function renderScreen(lines: string[]): void {
61-
process.stdout.write(ANSI.moveTo(1, 1));
62-
process.stdout.write(ANSI.clearScreen);
63-
const maxLines = (process.stdout.rows || 24) - 1; // Leave 1 line buffer
65+
const maxLines = (process.stdout.rows || 24) - 1;
6466
const truncatedLines = lines.slice(0, maxLines);
67+
68+
let buf = ANSI.moveTo(1, 1);
6569
for (const line of truncatedLines) {
66-
console.log(line);
70+
buf += ANSI.clearLine + line + "\n";
6771
}
72+
// Clear any leftover lines from the previous (longer) render
73+
for (let i = truncatedLines.length; i < lastRenderedLineCount; i++) {
74+
buf += ANSI.clearLine + "\n";
75+
}
76+
lastRenderedLineCount = truncatedLines.length;
77+
78+
process.stdout.write(buf);
6879
}
6980

7081
// Format percentage
@@ -509,7 +520,7 @@ export async function watchBenchmarkJob(id: string) {
509520
}
510521

511522
const jobName = job.name || job.id;
512-
const startTime = Date.now();
523+
const jobStartMs = job.create_time_ms;
513524

514525
// Enter full-screen mode and set up cleanup
515526
enterFullScreen();
@@ -542,7 +553,7 @@ export async function watchBenchmarkJob(id: string) {
542553
const SPINNER_INTERVAL_MS = 100;
543554
const UPDATES_PER_POLL = Math.floor(POLL_INTERVAL_MS / SPINNER_INTERVAL_MS);
544555

545-
// Handle terminal resize - clear screen to prevent artifacts
556+
// Handle terminal resize - force a full redraw to clear stale content
546557
let needsFullRedraw = false;
547558
const handleResize = () => {
548559
needsFullRedraw = true;
@@ -555,7 +566,7 @@ export async function watchBenchmarkJob(id: string) {
555566

556567
while (!COMPLETED_STATES.includes(job.state || "")) {
557568
// Check timeout
558-
if (Date.now() - startTime > MAX_WAIT_MS) {
569+
if (Date.now() - jobStartMs > MAX_WAIT_MS) {
559570
cleanup();
560571
outputError(
561572
`Timeout waiting for job completion after ${MAX_WAIT_MS / 1000 / 60} minutes`,
@@ -566,8 +577,7 @@ export async function watchBenchmarkJob(id: string) {
566577
const progressLines = formatWatchProgress(progressList, tick);
567578

568579
// Build screen content
569-
const elapsed = Math.floor((Date.now() - startTime) / 1000);
570-
const elapsedStr = `${Math.floor(elapsed / 60)}m ${elapsed % 60}s`;
580+
const elapsedStr = formatDuration(Date.now() - jobStartMs);
571581

572582
const screenLines: string[] = [];
573583
screenLines.push(
@@ -588,9 +598,9 @@ export async function watchBenchmarkJob(id: string) {
588598
screenLines.push("");
589599
screenLines.push(chalk.dim("Press Ctrl+C to exit"));
590600

591-
// Force full clear on resize to prevent artifacts
601+
// On resize, bump the line count so renderScreen clears the full area
592602
if (needsFullRedraw) {
593-
process.stdout.write(ANSI.clearScreen);
603+
lastRenderedLineCount = process.stdout.rows || 24;
594604
needsFullRedraw = false;
595605
}
596606

@@ -614,9 +624,8 @@ export async function watchBenchmarkJob(id: string) {
614624
cleanup();
615625
}
616626

617-
// Calculate total elapsed time
618-
const totalElapsed = Date.now() - startTime;
619-
const totalElapsedStr = formatDuration(totalElapsed);
627+
// Calculate total elapsed time from job creation
628+
const totalElapsedStr = formatDuration(Date.now() - jobStartMs);
620629

621630
// Show completion message
622631
console.log(chalk.green.bold("Benchmark job completed!"));

0 commit comments

Comments
 (0)