UNPKG

capsule-ai-cli

Version:

The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing

263 lines 9.18 kB
import ora from 'ora'; import chalk from 'chalk'; import { terminalController } from './terminal-controller.js'; export class AnimationController { spinner = null; startTime = 0; tokenCount = 0; customMessage = null; messageIndex = 0; updateInterval = null; renderInterval = null; currentProvider = 'default'; currentModel = ''; renderPosition = null; providerThemes = { openai: { color: chalk.hex('#10A37F'), messages: ['Thinking...', 'Processing...', 'Analyzing...', 'Computing...'] }, anthropic: { color: chalk.hex('#D97706'), messages: ['Pondering...', 'Contemplating...', 'Reasoning...', 'Reflecting...'] }, google: { color: chalk.hex('#4285F4'), messages: ['Searching...', 'Organizing...', 'Synthesizing...', 'Assembling...'] }, cohere: { color: chalk.hex('#FF6B6B'), messages: ['Generating...', 'Creating...', 'Crafting...', 'Building...'] }, mistral: { color: chalk.hex('#FF7000'), messages: ['Formulating...', 'Constructing...', 'Developing...', 'Preparing...'] }, deepseek: { color: chalk.hex('#00D4FF'), messages: ['Diving deep...', 'Exploring...', 'Discovering...', 'Uncovering...'] }, default: { color: chalk.white, messages: ['Loading...', 'Working...', 'Processing...', 'Thinking...'] } }; modelMessages = { 'o3': 'Deep reasoning...', 'o4-mini': 'Quick thinking...', 'gpt-4o': 'Optimizing response...', 'gpt-4.1': 'Advanced processing...' }; setProvider(provider) { this.currentProvider = provider; } setModel(model) { this.currentModel = model; } setRenderPosition(row) { this.renderPosition = { row }; } start(options) { if (this.spinner) { this.stop(); } if (options?.provider) { this.currentProvider = options.provider; } if (options?.model) { this.currentModel = options.model; } if (options?.initialTokens !== undefined) { this.tokenCount = options.initialTokens; } if (options?.customMessage !== undefined) { this.customMessage = options.customMessage; } this.startTime = Date.now(); this.messageIndex = 0; const theme = this.providerThemes[this.currentProvider] || this.providerThemes.default; this.spinner = ora({ spinner: { interval: 200, frames: ['✶', '✷', '✸', '✹'] }, color: 'cyan', hideCursor: false, stream: process.stdout }); if (this.renderPosition) { this.startManualRendering(); } else { this.updateInterval = setInterval(() => { this.updateSpinnerText(); }, 200); this.updateSpinnerText(); this.spinner.start(); } } updateSpinnerText() { if (!this.spinner) return; const theme = this.providerThemes[this.currentProvider] || this.providerThemes.default; const elapsed = Math.floor((Date.now() - this.startTime) / 1000); let message; if (this.customMessage) { message = this.customMessage; } else if (this.modelMessages[this.currentModel]) { message = this.modelMessages[this.currentModel]; } else { const messages = theme.messages; const msgIdx = Math.floor(this.messageIndex / 20) % messages.length; message = messages[msgIdx]; this.messageIndex++; } const text = `${theme.color(message)} ${chalk.dim('(')}${chalk.dim(`${elapsed}s`)} ${chalk.dim('•')} ${chalk.dim(`${this.tokenCount} tokens in`)} ${chalk.dim('• esc to interrupt')}${chalk.dim(')')}`; this.spinner.text = text; } updateMessage(message) { this.customMessage = message; if (this.spinner) { this.updateSpinnerText(); } } updateTokens(count) { this.tokenCount = count; if (this.spinner) { this.updateSpinnerText(); } } startManualRendering() { if (!this.renderPosition || !this.spinner) return; if (this.renderInterval) { clearInterval(this.renderInterval); } this.updateInterval = setInterval(() => { this.messageIndex++; }, 200); this.renderInterval = setInterval(() => { if (!this.spinner || !this.renderPosition) { if (this.renderInterval) { clearInterval(this.renderInterval); this.renderInterval = null; } return; } terminalController.saveCursorPosition(); terminalController.moveCursorTo(1, this.renderPosition.row); terminalController.clearLine(); const frames = ['✶', '✷', '✸', '✹']; const frameIndex = Math.floor((Date.now() - this.startTime) / 200) % frames.length; const theme = this.providerThemes[this.currentProvider] || this.providerThemes.default; const elapsed = Math.floor((Date.now() - this.startTime) / 1000); let message; if (this.customMessage) { message = this.customMessage; } else if (this.modelMessages[this.currentModel]) { message = this.modelMessages[this.currentModel]; } else { const messages = theme.messages; const msgIdx = Math.floor(this.messageIndex / 20) % messages.length; message = messages[msgIdx]; } const animationText = ` ${theme.color(frames[frameIndex])} ${theme.color(message)} ${chalk.dim('(')}${chalk.dim(`${elapsed}s`)} ${chalk.dim('•')} ${chalk.dim(`${this.tokenCount} tokens in`)} ${chalk.dim('• esc to interrupt')}${chalk.dim(')')}`; process.stdout.write(animationText); terminalController.restoreCursorPosition(); }, 200); } stop() { if (this.updateInterval) { clearInterval(this.updateInterval); this.updateInterval = null; } if (this.renderInterval) { clearInterval(this.renderInterval); this.renderInterval = null; } if (this.renderPosition && this.spinner) { terminalController.saveCursorPosition(); terminalController.moveCursorTo(1, this.renderPosition.row); terminalController.clearLine(); terminalController.restoreCursorPosition(); } if (this.spinner) { if (!this.renderPosition) { this.spinner.stop(); } this.spinner = null; } this.customMessage = null; this.tokenCount = 0; } isRunning() { if (this.renderPosition && this.renderInterval) { return true; } return this.spinner !== null && this.spinner.isSpinning; } getStatus() { if (!this.spinner || !this.spinner.isSpinning) { return null; } const theme = this.providerThemes[this.currentProvider] || this.providerThemes.default; const elapsed = Math.floor((Date.now() - this.startTime) / 1000); let message; if (this.customMessage) { message = this.customMessage; } else if (this.modelMessages[this.currentModel]) { message = this.modelMessages[this.currentModel]; } else { const messages = theme.messages; const msgIdx = Math.floor(this.messageIndex / 20) % messages.length; message = messages[msgIdx]; } const frames = ['✶', '✷', '✸', '✹']; const frameIndex = Math.floor((Date.now() - this.startTime) / 200) % frames.length; return { star: theme.color(frames[frameIndex]), message: theme.color(message), elapsed: `${elapsed}s`, tokens: `${this.tokenCount} tokens in` }; } simpleLoading(text) { return ora({ text, spinner: 'dots', hideCursor: true }).start(); } succeed(text) { if (this.spinner) { this.spinner.succeed(text); this.stop(); } } fail(text) { if (this.spinner) { this.spinner.fail(text); this.stop(); } } warn(text) { if (this.spinner) { this.spinner.warn(text); this.stop(); } } info(text) { if (this.spinner) { this.spinner.info(text); this.stop(); } } } export const animationController = new AnimationController(); //# sourceMappingURL=animation-controller.js.map