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:
+37
-22
@@ -58,6 +58,25 @@ function makeTurnRunner(runtime: WiredRuntime, state: AppStateRest): TurnRunner
|
||||
};
|
||||
}
|
||||
|
||||
/** Append an entry to the bounded live-activity breadcrumb. */
|
||||
function addActivity(
|
||||
state: AppStateRest,
|
||||
entry: { label: string; detail?: string; kind: "tool" | "agent" | "error" },
|
||||
): void {
|
||||
const activity = state.misc.activity;
|
||||
// Dedupe consecutive repeats of the same tool so `read read read` collapses.
|
||||
const last = activity[activity.length - 1];
|
||||
const key = `${entry.label}:${entry.kind}`;
|
||||
if (last && `${last.label}:${last.kind}` === key && last.detail === entry.detail) {
|
||||
last.count = (last.count ?? 1) + 1;
|
||||
} else {
|
||||
activity.push({ ...entry, count: 1 });
|
||||
}
|
||||
// Keep the breadcrumb tight (max 12 entries).
|
||||
while (activity.length > 12) activity.shift();
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
/** Route one live `TurnEvent` into TUI state (transcript, status bar, toasts). */
|
||||
function handleEvent(state: AppStateRest, ev: TurnEvent): void {
|
||||
switch (ev.kind) {
|
||||
@@ -74,38 +93,34 @@ function handleEvent(state: AppStateRest, ev: TurnEvent): void {
|
||||
);
|
||||
break;
|
||||
case "tool_result":
|
||||
pushTranscript(
|
||||
state,
|
||||
makeChatMessage(
|
||||
Roles.Tool,
|
||||
`→ ${ev.tool_name}${ev.is_error ? " (error)" : ""}`,
|
||||
),
|
||||
);
|
||||
// A tool result is a breadcrumb, not a full transcript entry — keep the
|
||||
// transcript clean and fold the tool name into the live activity bar.
|
||||
addActivity(state, {
|
||||
label: ev.tool_name,
|
||||
detail: ev.is_error ? "error" : undefined,
|
||||
kind: ev.is_error ? "error" : "tool",
|
||||
});
|
||||
break;
|
||||
case "usage":
|
||||
state.lastUsage = { tokensIn: ev.tokens_in, tokensOut: ev.tokens_out };
|
||||
state.dirty = true;
|
||||
break;
|
||||
case "agent_progress":
|
||||
pushTranscript(
|
||||
state,
|
||||
makeChatMessage(
|
||||
Roles.Tool,
|
||||
`[${ev.progress.agent_name}] ${ev.progress.status}${ev.progress.error ? ` — ${ev.progress.error}` : ev.progress.current_tool ? ` · ${ev.progress.current_tool}` : ""}`,
|
||||
),
|
||||
);
|
||||
addActivity(state, {
|
||||
label: ev.progress.agent_name,
|
||||
detail: ev.progress.error ?? ev.progress.current_tool ?? undefined,
|
||||
kind: ev.progress.error ? "error" : "agent",
|
||||
});
|
||||
break;
|
||||
case "workflow_agent_update":
|
||||
pushTranscript(
|
||||
state,
|
||||
makeChatMessage(
|
||||
Roles.Tool,
|
||||
`[${ev.agent_name}] ${ev.status}${ev.error ? ` — ${ev.error}` : ""}`,
|
||||
),
|
||||
);
|
||||
addActivity(state, {
|
||||
label: ev.agent_name,
|
||||
detail: ev.error ?? undefined,
|
||||
kind: ev.error ? "error" : "agent",
|
||||
});
|
||||
break;
|
||||
case "error":
|
||||
pushTranscript(state, makeChatMessage(Roles.System, `error: ${ev.message}`));
|
||||
addActivity(state, { label: "error", detail: ev.message, kind: "error" });
|
||||
toastError(state, `Turn error: ${ev.message}`);
|
||||
break;
|
||||
case "compacted":
|
||||
|
||||
Reference in New Issue
Block a user