feat(tui): enhance model selection and improve UI components

- Added tests for handling key events in model selector, including Enter and ArrowDown functionality.
- Updated TUI layout to include a new header, activity bar, and improved status bar with spinner and uptime.
- Refactored transcript rendering to support markdown and improved message display.
- Introduced overlays for model selection, help, and editor with appropriate rendering logic.
- Implemented smoke tests for various TUI states to ensure UI integrity and functionality.
This commit is contained in:
asepharyana
2026-09-03 16:13:29 +07:00
parent 5912a35bbb
commit c53fdfa7c9
6 changed files with 896 additions and 220 deletions
+37 -5
View File
@@ -43,6 +43,8 @@ export const COMMANDS: string[] = [
"/todo",
"/usage",
"/compact",
"/diff",
"/plan",
];
/** The user's input buffer, cursor, history, and autocomplete state. */
@@ -194,6 +196,12 @@ export class InputState {
return result;
}
/** Clear the input buffer and reset the cursor. */
clear(): void {
this.buffer = "";
this.cursor = 0;
}
/** Navigate backward through input history. */
historyUp(): void {
if (this.history.length === 0) return;
@@ -277,6 +285,20 @@ export class EditorState {
}
}
/* ── Turn activity ────────────────────────────────────────────────── */
/** A compact, bounded breadcrumb of agent activity during a turn. */
export interface ActivityEntry {
/** Short label, e.g. a tool name or agent name. */
label: string;
/** Optional detail (current tool, error). */
detail?: string;
/** Dramatic colouring: "tool" | "agent" | "error". */
kind: "tool" | "agent" | "error";
/** Number of consecutive repeats (collapsed into one chip). */
count?: number;
}
/* ── Misc state ───────────────────────────────────────────────────── */
/** The "miscellaneous" slice of app state. */
@@ -296,6 +318,8 @@ export class MiscState {
pendingClipboardCopy: string | null = null;
diffContent = "";
diffScroll = 0;
/** Breadcrumb of the current turn's activity (tools / sub-agents). */
activity: ActivityEntry[] = [];
pushToast(toast: { kind: string; message: string; createdMs: number; ttlMs: number }): void {
this.toasts.push(toast);
@@ -477,6 +501,8 @@ export interface AppStateRest {
turnAbort: AbortController | null;
/** Token usage from the most recent turn (surfaced in the status bar). */
lastUsage: { tokensIn: number; tokensOut: number } | null;
/** Monotonic boot time (ms) used for the status-bar uptime + spinner. */
bootedAt: number;
}
export const DEFAULT_HELP_TEXT = ` Zesdex TUI — Keyboard Shortcuts
@@ -487,6 +513,7 @@ export const DEFAULT_HELP_TEXT = ` Zesdex TUI — Keyboard Shortcuts
Ctrl+Y Copy last assistant message
Esc Abort turn / Close overlay
Tab Autocomplete
Alt+D Show git diff
─── Navigation ───
↑ / ↓ History browse / Overlay navigate
@@ -496,20 +523,24 @@ export const DEFAULT_HELP_TEXT = ` Zesdex TUI — Keyboard Shortcuts
─── Overlays ───
/help Show this help
/settings Open settings overlay
/todo Open tasks (todo) overlay
/usage Open usage statistics
/bash Open bash jobs overlay
/diff Open git diff overlay
/plan Open plan overlay
/mcp Open MCP server management
/model Open model selector
/compact Compact conversation
/clear Clear transcript
/rewind Rewind conversation history
─── Editor Mode ───
/edit <path> Open file for inline editing
Ctrl+S Save changes
Esc Dismiss editor`;
Ctrl+S Save changes (not yet wired)
Esc Dismiss editor
─── Legend ───
● running turn is being executed
● idle waiting for input
in:/out: token usage of the latest turn`;
/** Construct fresh TUI state. */
export function createTuiState(
@@ -549,6 +580,7 @@ export function createTuiState(
turnRunner: null,
turnAbort: null,
lastUsage: null,
bootedAt: Date.now(),
};
}