research-cli
Version:
AI-powered research assistant with web search capabilities and beautiful terminal UI
281 lines • 7.4 kB
JavaScript
/**
* Professional progress bars for research-cli using cli-progress
*/
import * as cliProgress from "cli-progress";
/**
* Research CLI themed progress bar configuration
*/
export const RESEARCH_CLI_THEME = {
format: "🔍 {operation} |{bar}| {percentage}% | {value}/{total} | {stage}",
barCompleteChar: "█",
barIncompleteChar: "░",
hideCursor: true,
clearOnComplete: false,
barsize: 30,
gracefulExit: true,
fps: 10,
// Use stderr to avoid interfering with actual output
stream: process.stderr,
etaBuffer: 100,
};
/**
* Compact theme for tool operations
*/
export const TOOL_THEME = {
format: "🔧 {operation} |{bar}| {percentage}%",
barCompleteChar: "▓",
barIncompleteChar: "▒",
hideCursor: true,
clearOnComplete: true,
barsize: 20,
gracefulExit: true,
fps: 5,
stream: process.stderr,
};
/**
* Multi-operation theme for concurrent tasks
*/
export const MULTI_THEME = {
format: "{operation} |{bar}| {percentage}% | {stage}",
barCompleteChar: "█",
barIncompleteChar: "░",
hideCursor: true,
clearOnComplete: false,
barsize: 25,
gracefulExit: true,
fps: 10,
stream: process.stderr,
};
/**
* Single progress bar wrapper with research-cli branding
*/
export class ResearchProgressBar {
bar;
isActive = false;
constructor(options = {}) {
// Merge theme options, filtering out incompatible types
const { format: _, ...filteredTheme } = RESEARCH_CLI_THEME;
const cleanedOptions = {
...filteredTheme,
...Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== null)),
};
this.bar = new cliProgress.SingleBar(cleanedOptions);
}
/**
* Start the progress bar
*/
start(total, startValue = 0, payload = {}) {
if (this.isActive) {
this.stop();
}
const defaultPayload = {
operation: "Processing",
stage: "Starting...",
...payload,
};
this.bar.start(total, startValue, defaultPayload);
this.isActive = true;
}
/**
* Update progress value and/or payload
*/
update(value, payload) {
if (!this.isActive)
return;
if (value !== null && value !== undefined) {
this.bar.update(value, payload);
}
else if (payload) {
this.bar.update(payload);
}
}
/**
* Increment progress by specified amount
*/
increment(delta = 1, payload) {
if (!this.isActive)
return;
this.bar.increment(delta, payload);
}
/**
* Update the operation description
*/
setOperation(operation) {
this.update(null, { operation });
}
/**
* Update the current stage
*/
setStage(stage) {
this.update(null, { stage });
}
/**
* Set the total value while active
*/
setTotal(total) {
if (!this.isActive)
return;
this.bar.setTotal(total);
}
/**
* Complete the progress bar with final stage
*/
complete(finalStage = "Complete") {
if (!this.isActive)
return;
this.update(null, { stage: finalStage });
this.stop();
}
/**
* Stop the progress bar
*/
stop() {
if (this.isActive) {
this.bar.stop();
this.isActive = false;
}
}
/**
* Check if progress bar is currently active
*/
get active() {
return this.isActive;
}
}
/**
* Multi-progress bar manager for concurrent operations
*/
export class ResearchMultiProgress {
multibar;
bars = new Map();
isActive = false;
constructor(options = {}) {
// Merge theme options, filtering out incompatible types
const { format: _, ...filteredTheme } = MULTI_THEME;
const cleanedOptions = {
...filteredTheme,
...Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== null)),
};
this.multibar = new cliProgress.MultiBar(cleanedOptions);
}
/**
* Create a new progress bar
*/
createBar(id, total, startValue = 0, payload = {}, barOptions) {
if (!this.isActive) {
this.isActive = true;
}
// Remove existing bar with same ID
this.removeBar(id);
const defaultPayload = {
operation: id,
stage: "Starting...",
...payload,
};
const bar = this.multibar.create(total, startValue, defaultPayload, barOptions);
this.bars.set(id, bar);
}
/**
* Update a specific progress bar
*/
updateBar(id, value, payload) {
const bar = this.bars.get(id);
if (!bar)
return;
if (value !== null && value !== undefined) {
bar.update(value, payload);
}
else if (payload) {
bar.update(payload);
}
}
/**
* Increment a specific progress bar
*/
incrementBar(id, delta = 1, payload) {
const bar = this.bars.get(id);
if (!bar)
return;
bar.increment(delta, payload);
}
/**
* Complete a specific progress bar
*/
completeBar(id, finalStage = "Complete") {
this.updateBar(id, null, { stage: finalStage });
this.removeBar(id);
}
/**
* Remove a specific progress bar
*/
removeBar(id) {
const bar = this.bars.get(id);
if (bar) {
this.multibar.remove(bar);
this.bars.delete(id);
}
}
/**
* Log a message above the progress bars
*/
log(message) {
if (this.isActive) {
this.multibar.log(`${message}\n`);
}
else {
// Fallback to stderr if multibar not active
process.stderr.write(`${message}\n`);
}
}
/**
* Stop all progress bars
*/
stop() {
if (this.isActive) {
this.multibar.stop();
this.bars.clear();
this.isActive = false;
}
}
/**
* Get list of active bar IDs
*/
getActiveBars() {
return Array.from(this.bars.keys());
}
/**
* Check if multibar is currently active
*/
get active() {
return this.isActive;
}
}
/**
* Utility function to create a themed progress bar
*/
export function createProgressBar(theme = "research", options = {}) {
let baseTheme;
switch (theme) {
case "tool":
baseTheme = TOOL_THEME;
break;
default:
baseTheme = RESEARCH_CLI_THEME;
break;
}
// Filter out format property from baseTheme to avoid type conflicts
const { format: _, ...filteredTheme } = baseTheme;
// Clean options to remove null values and ensure compatibility with cli-progress
const cleanedOptions = Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== null && value !== undefined));
return new ResearchProgressBar({
...filteredTheme,
...cleanedOptions,
});
}
/**
* Utility function to create a multi-progress manager
*/
export function createMultiProgress(options = {}) {
return new ResearchMultiProgress(options);
}
//# sourceMappingURL=progress.js.map