5d499272a8
Closes #6026
115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
import { type Component, Loader, type TUI } from "@earendil-works/pi-tui";
|
|
import type { WorkingIndicatorOptions } from "../../../core/extensions/index.ts";
|
|
import { theme } from "../theme/theme.ts";
|
|
import { CountdownTimer } from "./countdown-timer.ts";
|
|
import { keyText } from "./keybinding-hints.ts";
|
|
|
|
export type StatusIndicatorKind = "working" | "retry" | "compaction" | "branchSummary";
|
|
|
|
export class StatusIndicator extends Loader {
|
|
readonly kind: StatusIndicatorKind;
|
|
|
|
constructor(
|
|
kind: StatusIndicatorKind,
|
|
ui: TUI,
|
|
spinnerColorFn: (str: string) => string,
|
|
messageColorFn: (str: string) => string,
|
|
message: string,
|
|
indicator?: WorkingIndicatorOptions,
|
|
) {
|
|
super(ui, spinnerColorFn, messageColorFn, message, indicator);
|
|
this.kind = kind;
|
|
}
|
|
|
|
dispose(): void {
|
|
this.stop();
|
|
}
|
|
}
|
|
|
|
export class WorkingStatusIndicator extends StatusIndicator {
|
|
constructor(ui: TUI, message: string, indicator?: WorkingIndicatorOptions) {
|
|
super(
|
|
"working",
|
|
ui,
|
|
(spinner) => theme.fg("accent", spinner),
|
|
(text) => theme.fg("muted", text),
|
|
message,
|
|
indicator,
|
|
);
|
|
}
|
|
}
|
|
|
|
export class RetryStatusIndicator extends StatusIndicator {
|
|
private countdown: CountdownTimer | undefined;
|
|
|
|
constructor(ui: TUI, attempt: number, maxAttempts: number, delayMs: number) {
|
|
const retryMessage = (seconds: number) =>
|
|
`Retrying (${attempt}/${maxAttempts}) in ${seconds}s... (${keyText("app.interrupt")} to cancel)`;
|
|
super(
|
|
"retry",
|
|
ui,
|
|
(spinner) => theme.fg("warning", spinner),
|
|
(text) => theme.fg("muted", text),
|
|
retryMessage(Math.ceil(delayMs / 1000)),
|
|
);
|
|
this.countdown = new CountdownTimer(
|
|
delayMs,
|
|
ui,
|
|
(seconds) => {
|
|
this.setMessage(retryMessage(seconds));
|
|
},
|
|
() => {
|
|
this.countdown = undefined;
|
|
},
|
|
);
|
|
}
|
|
|
|
override dispose(): void {
|
|
this.countdown?.dispose();
|
|
this.countdown = undefined;
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
export type CompactionStatusReason = "manual" | "threshold" | "overflow";
|
|
|
|
export class CompactionStatusIndicator extends StatusIndicator {
|
|
constructor(ui: TUI, reason: CompactionStatusReason) {
|
|
const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
|
|
const label =
|
|
reason === "manual"
|
|
? `Compacting context... ${cancelHint}`
|
|
: `${reason === "overflow" ? "Context overflow detected, " : ""}Auto-compacting... ${cancelHint}`;
|
|
super(
|
|
"compaction",
|
|
ui,
|
|
(spinner) => theme.fg("accent", spinner),
|
|
(text) => theme.fg("muted", text),
|
|
label,
|
|
);
|
|
}
|
|
}
|
|
|
|
export class BranchSummaryStatusIndicator extends StatusIndicator {
|
|
constructor(ui: TUI) {
|
|
super(
|
|
"branchSummary",
|
|
ui,
|
|
(spinner) => theme.fg("accent", spinner),
|
|
(text) => theme.fg("muted", text),
|
|
`Summarizing branch... (${keyText("app.interrupt")} to cancel)`,
|
|
);
|
|
}
|
|
}
|
|
|
|
export class IdleStatus implements Component {
|
|
invalidate(): void {
|
|
// No cached state to invalidate.
|
|
}
|
|
|
|
render(width: number): string[] {
|
|
const emptyLine = " ".repeat(width);
|
|
return [emptyLine, emptyLine];
|
|
}
|
|
}
|