UNPKG

claude-monitor

Version:

Real-time terminal monitoring tool for Claude AI token usage

145 lines 5.96 kB
import chalk from 'chalk'; import { calculateSessionMetrics, formatTimeUntil, formatPredictedTime, getBurnRateStatus, formatBurnRateStatus } from '../core/session-metrics.js'; import { formatLargeNumber } from '../core/calculations.js'; import { formatTime } from '../utils/time.js'; import { getSessionBillableTokens } from '../types/session-tokens.js'; const ANSI = { clear: '\x1b[2J\x1b[H', hide: '\x1b[?25l', show: '\x1b[?25h', save: '\x1b[s', restore: '\x1b[u', }; export class RichDisplay { lastUpdate = 0; minUpdateInterval = 100; display(data) { const now = Date.now(); if (now - this.lastUpdate < this.minUpdateInterval) { return; } this.lastUpdate = now; process.stdout.write(ANSI.clear + ANSI.hide); this.displayHeader(); if (data.activeSession) { this.displayActiveSession(data.activeSession, data.tokenLimit, data.settings); } else { this.displayNoSession(); } this.displayFooter(data.settings.timeFormat); process.stdout.write(ANSI.show); } displayHeader() { const sparkles = chalk.cyan('✦ ✧ ✦ ✧'); const title = chalk.bold.cyan('CLAUDE TOKEN MONITOR'); const line = chalk.cyan('═'.repeat(60)); console.log(`${sparkles} ${title} ${sparkles}`); console.log(line); console.log(); } displayActiveSession(session, tokenLimit, settings) { const metrics = calculateSessionMetrics(session, tokenLimit); const billableTokens = getSessionBillableTokens(session); console.log(chalk.bold('📊 Token Usage:') + ' ' + this.createProgressBar(metrics.usagePercentage)); console.log(chalk.bold('⏰ Time to Reset:') + ' ' + this.createTimeProgressBar(metrics.timePercentage, metrics.remainingMinutes)); console.log(); const usedFormatted = formatLargeNumber(billableTokens); const limitFormatted = formatLargeNumber(tokenLimit); const remainingFormatted = formatLargeNumber(metrics.tokensRemaining); console.log(chalk.bold('🎯 Tokens:') + ' ' + `${usedFormatted} / ~${limitFormatted} ` + chalk.cyan(`(${remainingFormatted} left)`)); if (session.burnRate) { const burnRateFormatted = Math.round(session.burnRate.tokensPerMinute).toLocaleString(); console.log(chalk.bold('🔥 Burn Rate:') + ' ' + `${burnRateFormatted} tokens/min`); } console.log(); console.log(chalk.bold('🏁 Predicted End:') + ' ' + formatPredictedTime(metrics.predictedEndTime, settings.timeFormat)); console.log(chalk.bold('🔄 Token Reset:') + ' ' + formatTime(metrics.resetTime, settings.timeFormat)); console.log(); const uptime = this.formatUptime(metrics.elapsedMinutes); const status = getBurnRateStatus(session.burnRate, metrics.tokensRemaining, metrics.remainingMinutes); const statusMsg = formatBurnRateStatus(status); console.log(chalk.gray(`🐶 ${uptime} ${statusMsg} | Ctrl+C to exit 📦`)); } createProgressBar(percentage) { const width = 50; const displayPercentage = Math.min(percentage, 100); const filled = Math.round((displayPercentage / 100) * width); const empty = Math.max(0, width - filled); let color; let emoji; if (percentage >= 90) { color = chalk.red; emoji = '🔴'; } else if (percentage >= 70) { color = chalk.yellow; emoji = '🟡'; } else { color = chalk.green; emoji = '🟢'; } const filledBar = color('█'.repeat(filled)); const emptyBar = chalk.red('░'.repeat(empty)); return `${emoji} [${filledBar}${emptyBar}] ${percentage.toFixed(1)}%`; } createTimeProgressBar(percentage, remainingMinutes) { const width = 50; const displayPercentage = Math.min(percentage, 100); const filled = Math.round((displayPercentage / 100) * width); const empty = Math.max(0, width - filled); let color; let emoji; if (percentage >= 90) { color = chalk.red; emoji = '🔴'; } else if (percentage >= 70) { color = chalk.yellow; emoji = '🟡'; } else { color = chalk.green; emoji = '🟢'; } const filledBar = color('█'.repeat(filled)); const emptyBar = chalk.red('░'.repeat(empty)); const timeLeft = formatTimeUntil(remainingMinutes); return `${emoji} [${filledBar}${emptyBar}] ${timeLeft}`; } displayNoSession() { console.log(); console.log(chalk.gray.dim('No active session')); console.log(); console.log(chalk.gray.dim('Waiting for Claude activity...')); console.log(); } displayFooter(timeFormat) { console.log(); console.log(chalk.cyan('─'.repeat(60))); console.log(chalk.gray(`Last updated: ${formatTime(new Date(), timeFormat)}`)); console.log(chalk.gray('Press Ctrl+C to exit')); } formatUptime(minutes) { const hours = Math.floor(minutes / 60); const mins = Math.floor(minutes % 60); const secs = Math.floor((minutes * 60) % 60); return `${hours.toString().padStart(2, '0')}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; } displayError(error) { console.error(chalk.red('❌ Error:'), error.message); } displayWarning(message) { console.warn(chalk.yellow('⚠️ Warning:'), message); } displayInfo(message) { console.info(chalk.blue('ℹ️ Info:'), message); } cleanup() { process.stdout.write(ANSI.show + '\x1b[0m\n'); } } //# sourceMappingURL=rich-display.js.map