or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdmulti-bar.mdpresets-formatting.mdsingle-bar.md
tile.json

multi-bar.mddocs/

Multi Progress Bar

Container for managing multiple progress bars simultaneously with coordinated rendering, event-driven updates, and logging capabilities. The MultiBar class enables complex progress visualization scenarios with multiple concurrent tasks.

Capabilities

MultiBar Constructor

Creates a new multi-progress bar container. Individual bars must be added using the create method.

/**
 * Creates a new multi-progress bar container
 * Note: MultiBar automatically disables synchronousUpdate regardless of options
 * @param options - Global configuration options applied to all bars
 * @param preset - Preset configuration to merge with options
 */
class MultiBar extends EventEmitter {
  constructor(options?: ProgressBarOptions, preset?: PresetConfiguration);
}

Usage Examples:

const cliProgress = require('cli-progress');

// Basic multi-bar container
const multibar = new cliProgress.MultiBar({
  clearOnComplete: false,
  hideCursor: true
});

// Multi-bar with custom format and preset
const multibar2 = new cliProgress.MultiBar({
  format: ' {bar} | {filename} | {value}/{total}',
  hideCursor: true,
  clearOnComplete: false
}, cliProgress.Presets.shades_grey);

Create Progress Bar

Adds a new progress bar to the container and starts it immediately. Returns a SingleBar instance for individual control.

/**
 * Creates and adds a new progress bar to the container
 * @param total - Maximum value for the progress bar
 * @param startValue - Initial progress value (default: 0)
 * @param payload - Custom data for token replacement
 * @param barOptions - Per-bar options to override global settings
 * @returns SingleBar instance for individual control
 */
create(total: number, startValue?: number, payload?: object, barOptions?: object): SingleBar;

Usage Examples:

// Create basic progress bars
const bar1 = multibar.create(100, 0);
const bar2 = multibar.create(200, 0);

// Create with initial payload
const bar3 = multibar.create(150, 0, {
  filename: 'data.csv',
  status: 'processing'
});

// Create with custom options for this bar only
const bar4 = multibar.create(300, 0, { task: 'upload' }, {
  format: 'Uploading |{bar}| {percentage}% | {task}',
  barCompleteChar: '▓',
  barIncompleteChar: '░'
});

Remove Progress Bar

Removes a specific progress bar from the container and updates the display.

/**
 * Removes a progress bar from the container
 * @param bar - The SingleBar instance to remove
 * @returns true if successfully removed, false if not found
 */
remove(bar: SingleBar): boolean;

Usage Examples:

const bar1 = multibar.create(100, 0);
const bar2 = multibar.create(200, 0);

// Complete and remove first bar
bar1.update(100);
const removed = multibar.remove(bar1);
console.log(`Bar removed: ${removed}`); // true

// Continue with remaining bars
bar2.update(150);

Stop All Progress Bars

Stops all progress bars in the container and performs cleanup.

/**
 * Stops all progress bars and performs cleanup
 */
stop(): void;

Usage Examples:

// Stop all bars when complete
multibar.stop();

// Emergency stop (e.g., on error)
try {
  // ... processing
} catch (error) {
  multibar.stop();
  throw error;
}

Log Output

Outputs buffered content above the progress bars during operation, useful for logging events or status messages.

/**
 * Outputs content above progress bars
 * @param message - Message to display (must include newline)
 */
log(message: string): void;

Usage Examples:

// Log individual events
multibar.log('Started processing batch 1\n');
multibar.log('Warning: File not found, skipping\n');
multibar.log('Completed batch 1 successfully\n');

// Log with timestamp
const timestamp = new Date().toISOString();
multibar.log(`[${timestamp}] Processing started\n`);

Events

MultiBar extends EventEmitter and emits comprehensive events for monitoring progress lifecycle:

// Event: 'start' - Emitted when first bar is created
multibar.on('start', () => {
  console.log('Multi-bar progress started');
});

// Event: 'update-pre' - Emitted before each update cycle
multibar.on('update-pre', () => {
  // Pre-update operations
});

// Event: 'redraw-pre' - Emitted before redrawing bars
multibar.on('redraw-pre', () => {
  // Pre-redraw operations
});

// Event: 'redraw-post' - Emitted after redrawing bars
multibar.on('redraw-post', () => {
  // Post-redraw operations
});

// Event: 'update-post' - Emitted after each update cycle
multibar.on('update-post', () => {
  // Post-update operations
});

// Event: 'stop-pre-clear' - Emitted before clearing on stop
multibar.on('stop-pre-clear', () => {
  // Pre-clear operations
});

// Event: 'stop' - Emitted when all bars are stopped
multibar.on('stop', () => {
  console.log('Multi-bar progress completed');
});

Properties

interface MultiBarProperties {
  readonly bars: SingleBar[];       // Array of managed progress bars
  readonly isActive: boolean;       // Whether any bars are active
  readonly options: ProgressBarOptions; // Global configuration options
}

Advanced Usage Patterns

File Processing with Individual Progress

const cliProgress = require('cli-progress');
const fs = require('fs').promises;

async function processFiles(filePaths) {
  const multibar = new cliProgress.MultiBar({
    format: ' {bar} | {filename} | {percentage}% | {value}/{total} KB',
    hideCursor: true,
    clearOnComplete: false,
    stopOnComplete: true
  }, cliProgress.Presets.shades_classic);

  const progressBars = new Map();

  // Create progress bar for each file
  for (const filePath of filePaths) {
    const stats = await fs.stat(filePath);
    const bar = multibar.create(Math.ceil(stats.size / 1024), 0, {
      filename: path.basename(filePath)
    });
    progressBars.set(filePath, bar);
  }

  // Process files concurrently
  await Promise.all(filePaths.map(async (filePath) => {
    const bar = progressBars.get(filePath);
    await processFile(filePath, (bytesProcessed) => {
      bar.update(Math.ceil(bytesProcessed / 1024));
    });
  }));

  multibar.stop();
}

Dynamic Bar Management

const multibar = new cliProgress.MultiBar({
  format: ' {bar} | Task {id} | {status}',
  hideCursor: true
});

const activeBars = new Map();
let taskId = 0;

function startTask(taskSize) {
  const id = ++taskId;
  const bar = multibar.create(taskSize, 0, {
    id: id,
    status: 'starting'
  });
  
  activeBars.set(id, bar);
  
  // Simulate task progress
  const interval = setInterval(() => {
    const currentValue = bar.value;
    if (currentValue >= taskSize) {
      clearInterval(interval);
      bar.update(taskSize, { status: 'completed' });
      
      // Remove completed bar after delay
      setTimeout(() => {
        multibar.remove(bar);
        activeBars.delete(id);
        
        // Stop if all tasks complete
        if (activeBars.size === 0) {
          multibar.stop();
        }
      }, 1000);
    } else {
      bar.increment(Math.random() * 5, {
        status: 'processing'
      });
    }
  }, 100);
  
  return id;
}

// Start multiple tasks
startTask(100);
startTask(150);
startTask(80);

Error Handling and Logging

const multibar = new cliProgress.MultiBar({
  format: ' {bar} | {filename} | {status}',
  gracefulExit: true
});

async function processWithLogging(files) {
  const bars = new Map();
  
  try {
    // Create bars for each file
    files.forEach((file, index) => {
      const bar = multibar.create(100, 0, {
        filename: file.name,
        status: 'queued'
      });
      bars.set(file.id, bar);
    });

    multibar.log(`Starting processing of ${files.length} files\n`);

    for (const file of files) {
      const bar = bars.get(file.id);
      
      try {
        bar.update(0, { status: 'processing' });
        
        await processFile(file, (progress) => {
          bar.update(progress);
        });
        
        bar.update(100, { status: 'completed' });
        multibar.log(`✓ Completed: ${file.name}\n`);
        
      } catch (error) {
        bar.update(bar.value, { status: 'failed' });
        multibar.log(`✗ Failed: ${file.name} - ${error.message}\n`);
      }
    }

  } finally {
    multibar.stop();
  }
}

Integration with Async Operations

const multibar = new cliProgress.MultiBar({
  format: ' {bar} | {operation} | {percentage}% | ETA: {eta}s'
});

async function performConcurrentOperations() {
  const operations = [
    { name: 'Download', steps: 100 },
    { name: 'Extract', steps: 50 },
    { name: 'Process', steps: 200 }
  ];

  const bars = operations.map(op => 
    multibar.create(op.steps, 0, { operation: op.name })
  );

  // Run operations concurrently
  await Promise.all(operations.map(async (op, index) => {
    const bar = bars[index];
    
    for (let step = 0; step <= op.steps; step++) {
      await new Promise(resolve => setTimeout(resolve, 50));
      bar.update(step);
    }
  }));

  multibar.stop();
}