Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/AppState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventEmitter } from 'node:events';

export type AppPhase = 'idle' | 'sending' | 'thinking' | 'prompting';
export type AppPhase = 'idle' | 'sending' | 'thinking' | 'prompting' | 'asking';

export interface AppStateEvents {
changed: [phase: AppPhase];
Expand Down Expand Up @@ -47,13 +47,24 @@ export class AppState extends EventEmitter<AppStateEvents> {
}, 500);
}

/** A permission or question prompt is active */
/** A permission prompt is active (label updated externally by permission timer) */
public prompting(label: string): void {
this.stopTimer();
this._promptLabel = label;
this.setPhase('prompting');
}

/** A question prompt is active (has its own elapsed timer) */
public asking(label: string): void {
this.stopTimer();
this._sendStartTime = Date.now();
this._promptLabel = label;
this.setPhase('asking');
this._timer = setInterval(() => {
this.emit('changed', this._phase);
}, 500);
}

/** Query is done, back to idle */
public idle(): void {
this.stopTimer();
Expand Down
1 change: 1 addition & 0 deletions src/PromptManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export class PromptManager {
if (!q) {
return;
}
this.appState.asking(`${q.header}: Select [1-${q.options.length + 1}]`);
this.term.log(`\x1b[1m${q.question}\x1b[0m`);
for (let i = 0; i < q.options.length; i++) {
this.term.log(` \x1b[36m${i + 1})\x1b[0m ${q.options[i].label} — ${q.options[i].description}`);
Expand Down
16 changes: 10 additions & 6 deletions src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ export class Terminal {
}
case 'prompting':
return '🔔 ' + this.formatLogLine(this.appState.promptLabel ?? '');
case 'asking': {
const elapsed = this.appState.elapsedSeconds ?? 0;
return '🔔 ' + this.formatLogLine(`(${elapsed}s) ${this.appState.promptLabel ?? ''}`);
}
}
}

private refreshSticky(): void {
private buildSticky(): string {
const columns = process.stdout.columns || 80;
let output = '';

Expand Down Expand Up @@ -101,26 +105,26 @@ export class Terminal {

this.stickyLineCount = statusScreenLines + editorScreenLines;

process.stdout.write(output);
return output;
}

private writeHistory(line: string): void {
let output = '';
output += this.clearStickyZone();
output += line;
output += '\n';
process.stdout.write(output);
this.stickyLineCount = 0;
this.refreshSticky();
output += this.buildSticky();
process.stdout.write(output);
}

/** Call when AppState changes to refresh the sticky zone */
public refresh(): void {
let output = '';
output += this.clearStickyZone();
this.stickyLineCount = 0;
output += this.buildSticky();
process.stdout.write(output);
this.refreshSticky();
}

public log(message: string, ...args: unknown[]): void {
Expand All @@ -138,8 +142,8 @@ export class Terminal {
this.editorContent = prepareEditor(editor, prompt);
this.cursorHidden = hideCursor;
this.stickyLineCount = 0;
output += this.buildSticky();
process.stdout.write(output);
this.refreshSticky();
}

public write(data: string): void {
Expand Down
Loading