UNPKG

prprompts-flutter-generator

Version:

AI-powered Flutter development with full automation + official extension support - Generate 32 security-audited guides & auto-implement in 2-3 hours. NEW v5.1: Official Claude Code plugin with hooks, Gemini TOML commands, Qwen MCP settings. Features: Comp

249 lines (218 loc) 5.66 kB
/** * ProgressBar - Terminal progress bar visualization * Shows conversion progress with ETA and current file */ const readline = require('readline'); class ProgressBar { constructor(options = {}) { this.total = options.total || 100; this.current = 0; this.format = options.format || 'Progress... {bar} {percentage}% ({value}/{total})'; this.barLength = options.barLength || 30; this.startTime = Date.now(); this.updateInterval = options.updateInterval || 100; // ms this.lastUpdate = 0; this.stream = options.stream || process.stdout; this.clearOnComplete = options.clearOnComplete || false; } /** * Increment progress */ increment(delta = 1) { this.current = Math.min(this.current + delta, this.total); this.render(); } /** * Set progress to specific value */ update(value, metadata = {}) { this.current = Math.min(value, this.total); this.metadata = metadata; this.render(); } /** * Render progress bar */ render() { // Throttle updates const now = Date.now(); if (now - this.lastUpdate < this.updateInterval && this.current < this.total) { return; } this.lastUpdate = now; const percentage = Math.floor((this.current / this.total) * 100); const filled = Math.floor((this.current / this.total) * this.barLength); const empty = this.barLength - filled; const bar = '█'.repeat(filled) + '░'.repeat(empty); // Calculate ETA const elapsed = (now - this.startTime) / 1000; const rate = this.current / elapsed; const remaining = this.total - this.current; const eta = remaining > 0 ? Math.ceil(remaining / rate) : 0; // Format output let output = this.format .replace('{bar}', bar) .replace('{percentage}', percentage) .replace('{value}', this.current) .replace('{total}', this.total) .replace('{eta}', eta) .replace('{elapsed}', Math.floor(elapsed)); // Add metadata if present if (this.metadata) { if (this.metadata.currentFile) { output += ` [${this.metadata.currentFile}]`; } if (this.metadata.status) { output += ` ${this.metadata.status}`; } } // Clear line and write if (this.stream.isTTY) { readline.clearLine(this.stream, 0); readline.cursorTo(this.stream, 0); } this.stream.write(output); } /** * Complete progress bar */ complete(message) { this.current = this.total; this.render(); if (this.clearOnComplete && this.stream.isTTY) { readline.clearLine(this.stream, 0); readline.cursorTo(this.stream, 0); } else { this.stream.write('\n'); } if (message) { this.stream.write(message + '\n'); } } /** * Stop progress bar */ stop() { if (!this.clearOnComplete) { this.stream.write('\n'); } } } /** * Spinner for indeterminate progress */ class Spinner { constructor(options = {}) { this.text = options.text || 'Loading...'; this.frames = options.frames || ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; this.interval = options.interval || 80; this.stream = options.stream || process.stdout; this.frameIndex = 0; this.timer = null; } /** * Start spinner */ start(text) { if (text) { this.text = text; } this.timer = setInterval(() => { const frame = this.frames[this.frameIndex]; if (this.stream.isTTY) { readline.clearLine(this.stream, 0); readline.cursorTo(this.stream, 0); } this.stream.write(`${frame} ${this.text}`); this.frameIndex = (this.frameIndex + 1) % this.frames.length; }, this.interval); } /** * Update spinner text */ update(text) { this.text = text; } /** * Stop spinner with success */ succeed(message) { this.stop(); this.stream.write(`✅ ${message || this.text}\n`); } /** * Stop spinner with failure */ fail(message) { this.stop(); this.stream.write(`❌ ${message || this.text}\n`); } /** * Stop spinner with warning */ warn(message) { this.stop(); this.stream.write(`⚠️ ${message || this.text}\n`); } /** * Stop spinner with info */ info(message) { this.stop(); this.stream.write(`ℹ️ ${message || this.text}\n`); } /** * Stop spinner */ stop() { if (this.timer) { clearInterval(this.timer); this.timer = null; } if (this.stream.isTTY) { readline.clearLine(this.stream, 0); readline.cursorTo(this.stream, 0); } } } /** * Multi-line progress tracker */ class MultiProgress { constructor() { this.bars = []; this.stream = process.stdout; } /** * Create a new progress bar */ create(options) { const bar = new ProgressBar({ ...options, stream: this.stream }); this.bars.push(bar); return bar; } /** * Render all bars */ render() { // Clear previous output this.stream.moveCursor(0, -this.bars.length); // Render each bar this.bars.forEach(bar => { bar.render(); this.stream.write('\n'); }); } /** * Stop all bars */ stop() { this.bars.forEach(bar => bar.stop()); } } module.exports = ProgressBar; module.exports.Spinner = Spinner; module.exports.MultiProgress = MultiProgress;