refactor(coding-agent): restructured loading animation lifecycle into ensureLoadingAnimation

- Extracted loading animation lifecycle management into centralized ensureLoadingAnimation() method.
- Consolidated duplicate loading animation setup across event-controller and interactive-mode into single reusable method.
- Updated showError() to properly clean up loading animation state when errors occur.
- Added comprehensive test coverage for InputController escape key behavior with optimistic submission.
This commit is contained in:
can1357
2026-03-10 05:51:09 +01:00
parent 8fe602173b
commit e019a3e352
6 changed files with 192 additions and 16 deletions
@@ -5,8 +5,8 @@
import * as path from "node:path";
import { type Agent, type AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
import type { AssistantMessage, ImageContent, Message, Model, UsageReport } from "@oh-my-pi/pi-ai";
import type { Component, Loader, SlashCommand } from "@oh-my-pi/pi-tui";
import { Container, Markdown, ProcessTerminal, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
import type { Component, SlashCommand } from "@oh-my-pi/pi-tui";
import { Container, Loader, Markdown, ProcessTerminal, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
import { APP_NAME, getProjectDir, hsvToRgb, isEnoent, logger, postmortem } from "@oh-my-pi/pi-utils";
import chalk from "chalk";
import { KeybindingsManager } from "../config/keybindings";
@@ -46,7 +46,14 @@ import { SSHCommandController } from "./controllers/ssh-command-controller";
import { OAuthManualInputManager } from "./oauth-manual-input";
import { setMermaidRenderCallback } from "./theme/mermaid-cache";
import type { Theme } from "./theme/theme";
import { getEditorTheme, getMarkdownTheme, onTerminalAppearanceChange, onThemeChange, theme } from "./theme/theme";
import {
getEditorTheme,
getMarkdownTheme,
getSymbolTheme,
onTerminalAppearanceChange,
onThemeChange,
theme,
} from "./theme/theme";
import type { CompactionQueuedMessage, InteractiveModeContext, TodoItem, TodoPhase } from "./types";
import { UiHelpers } from "./utils/ui-helpers";
@@ -849,6 +856,12 @@ export class InteractiveMode implements InteractiveModeContext {
showError(message: string): void {
this.optimisticUserMessageSignature = undefined;
this.#pendingWorkingMessage = undefined;
if (this.loadingAnimation) {
this.loadingAnimation.stop();
this.loadingAnimation = undefined;
this.statusContainer.clear();
}
this.#uiHelpers.showError(message);
}
@@ -856,6 +869,22 @@ export class InteractiveMode implements InteractiveModeContext {
this.#uiHelpers.showWarning(message);
}
ensureLoadingAnimation(): void {
if (!this.loadingAnimation) {
this.statusContainer.clear();
this.loadingAnimation = new Loader(
this.ui,
spinner => theme.fg("accent", spinner),
text => theme.fg("muted", text),
this.#defaultWorkingMessage,
getSymbolTheme().spinnerFrames,
);
this.statusContainer.addChild(this.loadingAnimation);
}
this.applyPendingWorkingMessage();
}
setWorkingMessage(message?: string): void {
if (message === undefined) {
this.#pendingWorkingMessage = undefined;