feat: improve tui status line
This commit is contained in:
@@ -152,6 +152,9 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
let autoMessageSent = false;
|
let autoMessageSent = false;
|
||||||
let sessionInfo: SessionInfo = {};
|
let sessionInfo: SessionInfo = {};
|
||||||
let lastCtrlCAt = 0;
|
let lastCtrlCAt = 0;
|
||||||
|
let activityStatus = "idle";
|
||||||
|
let connectionStatus = "connecting";
|
||||||
|
let statusTimeout: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
const client = new GatewayChatClient({
|
const client = new GatewayChatClient({
|
||||||
url: opts.url,
|
url: opts.url,
|
||||||
@@ -218,6 +221,30 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
status.setText(theme.dim(text));
|
status.setText(theme.dim(text));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderStatus = () => {
|
||||||
|
const text = activityStatus
|
||||||
|
? `${connectionStatus} | ${activityStatus}`
|
||||||
|
: connectionStatus;
|
||||||
|
setStatus(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setConnectionStatus = (text: string, ttlMs?: number) => {
|
||||||
|
connectionStatus = text;
|
||||||
|
renderStatus();
|
||||||
|
if (statusTimeout) clearTimeout(statusTimeout);
|
||||||
|
if (ttlMs && ttlMs > 0) {
|
||||||
|
statusTimeout = setTimeout(() => {
|
||||||
|
connectionStatus = isConnected ? "connected" : "disconnected";
|
||||||
|
renderStatus();
|
||||||
|
}, ttlMs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActivityStatus = (text: string) => {
|
||||||
|
activityStatus = text;
|
||||||
|
renderStatus();
|
||||||
|
};
|
||||||
|
|
||||||
const updateFooter = () => {
|
const updateFooter = () => {
|
||||||
const connection = isConnected ? "connected" : "disconnected";
|
const connection = isConnected ? "connected" : "disconnected";
|
||||||
const sessionKeyLabel = formatSessionKey(currentSessionKey);
|
const sessionKeyLabel = formatSessionKey(currentSessionKey);
|
||||||
@@ -246,7 +273,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
const deliver = deliverDefault ? "on" : "off";
|
const deliver = deliverDefault ? "on" : "off";
|
||||||
footer.setText(
|
footer.setText(
|
||||||
theme.dim(
|
theme.dim(
|
||||||
`${connection} | agent ${agentLabel} | session ${sessionLabel} | model ${modelLabel} | think ${think} | verbose ${verbose}${reasoningLabel ? ` | ${reasoningLabel}` : ""} | ${tokens} | deliver ${deliver}`,
|
`${connection} | agent ${agentLabel} | session ${sessionLabel} | ${modelLabel} | think ${think} | verbose ${verbose}${reasoningLabel ? ` | ${reasoningLabel}` : ""} | ${tokens} | deliver ${deliver}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -440,10 +467,10 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
sessionKey: currentSessionKey,
|
sessionKey: currentSessionKey,
|
||||||
runId: activeChatRunId,
|
runId: activeChatRunId,
|
||||||
});
|
});
|
||||||
setStatus("aborted");
|
setActivityStatus("aborted");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
chatLog.addSystem(`abort failed: ${String(err)}`);
|
chatLog.addSystem(`abort failed: ${String(err)}`);
|
||||||
setStatus("abort failed");
|
setActivityStatus("abort failed");
|
||||||
}
|
}
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
@@ -478,7 +505,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
});
|
});
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
chatLog.updateAssistant(text, evt.runId);
|
chatLog.updateAssistant(text, evt.runId);
|
||||||
setStatus("streaming");
|
setActivityStatus("streaming");
|
||||||
}
|
}
|
||||||
if (evt.state === "final") {
|
if (evt.state === "final") {
|
||||||
const text = extractTextFromMessage(evt.message, {
|
const text = extractTextFromMessage(evt.message, {
|
||||||
@@ -487,17 +514,17 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
chatLog.finalizeAssistant(text || "(no output)", evt.runId);
|
chatLog.finalizeAssistant(text || "(no output)", evt.runId);
|
||||||
noteFinalizedRun(evt.runId);
|
noteFinalizedRun(evt.runId);
|
||||||
activeChatRunId = null;
|
activeChatRunId = null;
|
||||||
setStatus("idle");
|
setActivityStatus("idle");
|
||||||
}
|
}
|
||||||
if (evt.state === "aborted") {
|
if (evt.state === "aborted") {
|
||||||
chatLog.addSystem("run aborted");
|
chatLog.addSystem("run aborted");
|
||||||
activeChatRunId = null;
|
activeChatRunId = null;
|
||||||
setStatus("aborted");
|
setActivityStatus("aborted");
|
||||||
}
|
}
|
||||||
if (evt.state === "error") {
|
if (evt.state === "error") {
|
||||||
chatLog.addSystem(`run error: ${evt.errorMessage ?? "unknown"}`);
|
chatLog.addSystem(`run error: ${evt.errorMessage ?? "unknown"}`);
|
||||||
activeChatRunId = null;
|
activeChatRunId = null;
|
||||||
setStatus("error");
|
setActivityStatus("error");
|
||||||
}
|
}
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
@@ -528,9 +555,9 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
}
|
}
|
||||||
if (evt.stream === "lifecycle") {
|
if (evt.stream === "lifecycle") {
|
||||||
const phase = typeof evt.data?.phase === "string" ? evt.data.phase : "";
|
const phase = typeof evt.data?.phase === "string" ? evt.data.phase : "";
|
||||||
if (phase === "start") setStatus("running");
|
if (phase === "start") setActivityStatus("running");
|
||||||
if (phase === "end") setStatus("idle");
|
if (phase === "end") setActivityStatus("idle");
|
||||||
if (phase === "error") setStatus("error");
|
if (phase === "error") setActivityStatus("error");
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -895,7 +922,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
try {
|
try {
|
||||||
chatLog.addUser(text);
|
chatLog.addUser(text);
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
setStatus("sending");
|
setActivityStatus("sending");
|
||||||
const { runId } = await client.sendChat({
|
const { runId } = await client.sendChat({
|
||||||
sessionKey: currentSessionKey,
|
sessionKey: currentSessionKey,
|
||||||
message: text,
|
message: text,
|
||||||
@@ -904,10 +931,10 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
timeoutMs: opts.timeoutMs,
|
timeoutMs: opts.timeoutMs,
|
||||||
});
|
});
|
||||||
activeChatRunId = runId;
|
activeChatRunId = runId;
|
||||||
setStatus("waiting");
|
setActivityStatus("waiting");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
chatLog.addSystem(`send failed: ${String(err)}`);
|
chatLog.addSystem(`send failed: ${String(err)}`);
|
||||||
setStatus("error");
|
setActivityStatus("error");
|
||||||
}
|
}
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
@@ -933,7 +960,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (editor.getText().trim().length > 0) {
|
if (editor.getText().trim().length > 0) {
|
||||||
editor.setText("");
|
editor.setText("");
|
||||||
setStatus("cleared input");
|
setActivityStatus("cleared input");
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -943,7 +970,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
lastCtrlCAt = now;
|
lastCtrlCAt = now;
|
||||||
setStatus("press ctrl+c again to exit");
|
setActivityStatus("press ctrl+c again to exit");
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
editor.onCtrlD = () => {
|
editor.onCtrlD = () => {
|
||||||
@@ -954,7 +981,7 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
editor.onCtrlO = () => {
|
editor.onCtrlO = () => {
|
||||||
toolsExpanded = !toolsExpanded;
|
toolsExpanded = !toolsExpanded;
|
||||||
chatLog.setToolsExpanded(toolsExpanded);
|
chatLog.setToolsExpanded(toolsExpanded);
|
||||||
setStatus(toolsExpanded ? "tools expanded" : "tools collapsed");
|
setActivityStatus(toolsExpanded ? "tools expanded" : "tools collapsed");
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
editor.onCtrlL = () => {
|
editor.onCtrlL = () => {
|
||||||
@@ -978,20 +1005,20 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
|
|
||||||
client.onConnected = () => {
|
client.onConnected = () => {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
setStatus("connected");
|
setConnectionStatus("connected");
|
||||||
void (async () => {
|
void (async () => {
|
||||||
await refreshAgents();
|
await refreshAgents();
|
||||||
updateHeader();
|
updateHeader();
|
||||||
if (!historyLoaded) {
|
if (!historyLoaded) {
|
||||||
await loadHistory();
|
await loadHistory();
|
||||||
chatLog.addSystem("gateway connected");
|
setConnectionStatus("gateway connected", 4000);
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
if (!autoMessageSent && autoMessage) {
|
if (!autoMessageSent && autoMessage) {
|
||||||
autoMessageSent = true;
|
autoMessageSent = true;
|
||||||
await sendMessage(autoMessage);
|
await sendMessage(autoMessage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chatLog.addSystem("gateway reconnected");
|
setConnectionStatus("gateway reconnected", 4000);
|
||||||
}
|
}
|
||||||
updateFooter();
|
updateFooter();
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
@@ -1000,23 +1027,24 @@ export async function runTui(opts: TuiOptions) {
|
|||||||
|
|
||||||
client.onDisconnected = (reason) => {
|
client.onDisconnected = (reason) => {
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
chatLog.addSystem(`gateway disconnected: ${reason || "closed"}`);
|
const reasonLabel = reason?.trim() ? reason.trim() : "closed";
|
||||||
setStatus("disconnected");
|
setConnectionStatus(`gateway disconnected: ${reasonLabel}`, 5000);
|
||||||
|
setActivityStatus("idle");
|
||||||
updateFooter();
|
updateFooter();
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
|
|
||||||
client.onGap = (info) => {
|
client.onGap = (info) => {
|
||||||
chatLog.addSystem(
|
setConnectionStatus(
|
||||||
`event gap: expected ${info.expected}, got ${info.received}`,
|
`event gap: expected ${info.expected}, got ${info.received}`,
|
||||||
|
5000,
|
||||||
);
|
);
|
||||||
tui.requestRender();
|
tui.requestRender();
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHeader();
|
updateHeader();
|
||||||
setStatus("connecting");
|
setConnectionStatus("connecting");
|
||||||
updateFooter();
|
updateFooter();
|
||||||
chatLog.addSystem("connecting...");
|
|
||||||
tui.start();
|
tui.start();
|
||||||
client.start();
|
client.start();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user