@travetto/terminal
Version:
General terminal support
43 lines (37 loc) • 1.86 kB
text/typescript
import { StyleUtil, TermStyleFn, TermStyleInput } from './style.ts';
import { Terminal, WAIT_TOKEN } from './terminal.ts';
type ProgressEvent<T> = { total?: number, idx: number, value: T };
type ProgressStyle = { complete: TermStyleFn, incomplete?: TermStyleFn };
export class TerminalUtil {
/**
* Create a progress bar updater, suitable for streaming to the bottom of the screen
*/
static progressBarUpdater(
term: Terminal,
cfg?: {
withWaiting?: boolean;
style?: { complete: TermStyleInput, incomplete?: TermStyleInput } | (() => ProgressStyle);
}
): (ev: ProgressEvent<string>) => string {
const styleBase = typeof cfg?.style !== 'function' ? {
complete: StyleUtil.getStyle(cfg?.style?.complete ?? { background: '#248613', text: '#ffffff' }),
incomplete: cfg?.style?.incomplete ? StyleUtil.getStyle(cfg.style.incomplete) : undefined,
} : undefined;
const style = typeof cfg?.style === 'function' ? cfg.style : (): ProgressStyle => styleBase!;
let width: number;
return ev => {
const text = ev.value ?? (ev.total ? '%idx/%total' : '%idx');
const pct = ev.total === undefined ? 0 : (ev.idx / ev.total);
if (ev.total) {
width ??= Math.trunc(Math.ceil(Math.log10(ev.total ?? 10000)));
}
const state: Record<string, string> = { total: `${ev.total}`, idx: `${ev.idx}`.padStart(width ?? 0), pct: `${Math.trunc(pct * 100)}` };
const line = ` ${text.replace(/[%](idx|total|pct)/g, (_, k) => state[k])} `;
const full = term.writer.padToWidth(line, cfg?.withWaiting ? 2 : 0);
const mid = Math.trunc(pct * term.width);
const [l, r] = [full.substring(0, mid), full.substring(mid)];
const { complete, incomplete } = style();
return `${cfg?.withWaiting ? `${WAIT_TOKEN} ` : ''}${complete(l)}${incomplete?.(r) ?? r}`;
};
}
}