Skip to content

Commit 38bbcf8

Browse files
committed
Fix early exit with escape
1 parent 8254caf commit 38bbcf8

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ function renderHistory(chatLog: ChatLogComponent, history: AgentRunnerController
122122
} else if (display.completed && display.endEvent?.type === 'tool_error') {
123123
const toolError = display.endEvent as ToolErrorEvent;
124124
component.setError(toolError.error);
125+
} else if (item.status === 'interrupted') {
126+
// Don't start spinner for tools in interrupted items
125127
} else if (display.progressMessage) {
126128
component.setActive(display.progressMessage);
127129
}

src/components/chat-log.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ interface ToolDisplayComponent {
5656
setLimitWarning(warning?: string): void;
5757
setApproval(decision: 'allow-once' | 'allow-session' | 'deny'): void;
5858
setDenied(path: string, tool: string): void;
59+
dispose?(): void;
5960
}
6061

6162
class BrowserSessionComponent extends Container implements ToolDisplayComponent {
@@ -145,6 +146,10 @@ export class ChatLogComponent extends Container {
145146
}
146147

147148
clearAll() {
149+
// Stop any running spinners before clearing
150+
for (const component of this.toolById.values()) {
151+
component.dispose?.();
152+
}
148153
this.clear();
149154
this.toolById.clear();
150155
this.currentBrowserSession = null;

src/components/tool-event.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Container, Spacer, Text } from '@mariozechner/pi-tui';
1+
import { Container, Spacer, Text, type TUI } from '@mariozechner/pi-tui';
22
import type { ApprovalDecision } from '../agent/types.js';
33
import { theme } from '../theme.js';
44

@@ -56,13 +56,19 @@ function approvalLabel(decision: ApprovalDecision): string {
5656
}
5757
}
5858

59+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
60+
5961
export class ToolEventComponent extends Container {
62+
private readonly tui: TUI;
6063
private readonly header: Text;
6164
private completedDetails: Text[] = [];
6265
private activeDetail: Text | null = null;
66+
private spinnerInterval: ReturnType<typeof setInterval> | null = null;
67+
private spinnerFrame: number = 0;
6368

64-
constructor(_tui: unknown, tool: string, args: Record<string, unknown>) {
69+
constructor(tui: TUI, tool: string, args: Record<string, unknown>) {
6570
super();
71+
this.tui = tui;
6672
this.addChild(new Spacer(1));
6773
const title = `${formatToolName(tool)}${args ? `${theme.muted('(')}${formatArgs(tool, args)}${theme.muted(')')}` : ''}`;
6874
this.header = new Text(`⏺ ${title}`, 0, 0);
@@ -72,8 +78,14 @@ export class ToolEventComponent extends Container {
7278
setActive(progressMessage?: string) {
7379
this.clearDetail();
7480
const message = progressMessage || 'Searching...';
75-
this.activeDetail = new Text(`${theme.muted('')}${message}`, 0, 0);
81+
this.activeDetail = new Text(`${theme.muted(`${SPINNER_FRAMES[0]}`)} ${message}`, 0, 0);
7682
this.addChild(this.activeDetail);
83+
this.spinnerFrame = 0;
84+
this.spinnerInterval = setInterval(() => {
85+
this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
86+
this.activeDetail?.setText(`${theme.muted(`⎿ ${SPINNER_FRAMES[this.spinnerFrame]}`)} ${message}`);
87+
this.tui.requestRender();
88+
}, 80);
7789
}
7890

7991
setComplete(summary: string, duration: number) {
@@ -120,7 +132,15 @@ export class ToolEventComponent extends Container {
120132
this.addChild(detail);
121133
}
122134

135+
dispose() {
136+
this.clearDetail();
137+
}
138+
123139
private clearDetail() {
140+
if (this.spinnerInterval) {
141+
clearInterval(this.spinnerInterval);
142+
this.spinnerInterval = null;
143+
}
124144
if (this.activeDetail) {
125145
this.removeChild(this.activeDetail);
126146
this.activeDetail = null;

0 commit comments

Comments
 (0)