60086d8a49
* fix(coding-agent): restore ask timeout auto-select behavior Fixes #266 * fix(coding-agent): derive ask timeout from ui timeout events * fix(coding-agent): enforce ask timeout during custom input * fix(ask): preserve sub-second timeout precision in interactive flow * fix(ask): reset hook input timeout on user activity * fix(ask): keep expiration timeout when refreshing tick interval * fix(ask): auto-select deterministic default on timeout Fixes #266 --------- Co-authored-by: Vũ Anh Nguyễn <vu.anh.nguyen@mgm-tp.com> Co-authored-by: can1357 <me@can.ac>
76 lines
1.8 KiB
TypeScript
76 lines
1.8 KiB
TypeScript
/**
|
|
* Reusable countdown timer for dialog components.
|
|
*/
|
|
import type { TUI } from "@oh-my-pi/pi-tui";
|
|
|
|
export class CountdownTimer {
|
|
#intervalId: NodeJS.Timeout | undefined;
|
|
#expireTimeoutId: NodeJS.Timeout | undefined;
|
|
#remainingSeconds: number;
|
|
#deadlineMs = 0;
|
|
readonly #initialMs: number;
|
|
|
|
constructor(
|
|
timeoutMs: number,
|
|
private tui: TUI | undefined,
|
|
private onTick: (seconds: number) => void,
|
|
private onExpire: () => void,
|
|
) {
|
|
this.#initialMs = timeoutMs;
|
|
this.#remainingSeconds = Math.ceil(timeoutMs / 1000);
|
|
this.#start();
|
|
}
|
|
|
|
#calculateRemainingSeconds(now = Date.now()): number {
|
|
const remainingMs = Math.max(0, this.#deadlineMs - now);
|
|
return Math.ceil(remainingMs / 1000);
|
|
}
|
|
|
|
#start(): void {
|
|
const now = Date.now();
|
|
this.#deadlineMs = now + this.#initialMs;
|
|
this.#remainingSeconds = this.#calculateRemainingSeconds(now);
|
|
this.onTick(this.#remainingSeconds);
|
|
this.tui?.requestRender();
|
|
|
|
this.#expireTimeoutId = setTimeout(() => {
|
|
this.dispose();
|
|
this.onExpire();
|
|
}, this.#initialMs);
|
|
|
|
this.#startInterval();
|
|
}
|
|
|
|
#startInterval(): void {
|
|
if (this.#intervalId) {
|
|
clearInterval(this.#intervalId);
|
|
this.#intervalId = undefined;
|
|
}
|
|
this.#intervalId = setInterval(() => {
|
|
const remainingSeconds = this.#calculateRemainingSeconds();
|
|
if (remainingSeconds !== this.#remainingSeconds) {
|
|
this.#remainingSeconds = remainingSeconds;
|
|
this.onTick(this.#remainingSeconds);
|
|
}
|
|
this.tui?.requestRender();
|
|
}, 1000);
|
|
}
|
|
|
|
/** Reset the countdown to its initial value */
|
|
reset(): void {
|
|
this.dispose();
|
|
this.#start();
|
|
}
|
|
|
|
dispose(): void {
|
|
if (this.#intervalId) {
|
|
clearInterval(this.#intervalId);
|
|
this.#intervalId = undefined;
|
|
}
|
|
if (this.#expireTimeoutId) {
|
|
clearTimeout(this.#expireTimeoutId);
|
|
this.#expireTimeoutId = undefined;
|
|
}
|
|
}
|
|
}
|