Improve shell output rendering

This commit is contained in:
Federico Jaramillo Martinez
2026-05-07 14:23:40 +02:00
parent a68b7b036a
commit 5430a44561
4 changed files with 14 additions and 7 deletions
+1 -2
View File
@@ -31,13 +31,12 @@ function normalizeMessage(message: any): ChatLine[] {
}
function normalizeBashExecution(message: any): ChatLine {
const lines = [`$ ${message.command ?? ""}`];
const lines = message.excludeFromContext ? ["excluded from context", "", `$ ${message.command ?? ""}`] : [`$ ${message.command ?? ""}`];
if (message.output) lines.push("", String(message.output));
if (message.exitCode != null) lines.push("", `exit ${message.exitCode}`);
if (message.cancelled) lines.push("", "cancelled");
if (message.truncated) lines.push("", "output truncated");
if (message.fullOutputPath) lines.push("", `full output: ${message.fullOutputPath}`);
if (message.excludeFromContext) lines.push("", "excluded from context");
return { role: "bash", parts: [{ type: "text", text: lines.join("\n") }] };
}
+4 -3
View File
@@ -44,7 +44,7 @@ export class ChatView extends LitElement {
return html`
<article class="msg ${message.role}" data-index=${index}>
<b class="label">${message.role}</b>
${message.parts.map((part) => this.renderPart(part))}
${message.parts.map((part) => this.renderPart(part, message))}
</article>
`;
}
@@ -61,7 +61,7 @@ export class ChatView extends LitElement {
${messages.map((message) => html`
<section class="group-msg ${message.role}">
<b class="label">${message.role}</b>
${message.parts.map((part) => this.renderPart(part))}
${message.parts.map((part) => this.renderPart(part, message))}
</section>
`)}
</div>
@@ -69,7 +69,8 @@ export class ChatView extends LitElement {
`;
}
private renderPart(part: ChatPart) {
private renderPart(part: ChatPart, message?: ChatLine) {
if (part.type === "text" && message?.role === "bash") return html`<pre class="part shell-output">${part.text}</pre>`;
if (part.type === "text") return html`<formatted-text class="part" .text=${part.text}></formatted-text>`;
if (part.type === "thinking") return html`<details class="part"><summary>thinking</summary><formatted-text .text=${part.text}></formatted-text></details>`;
if (part.type === "toolCall") return html`<div class="part tool-line">▶ ${part.toolName}<span class="summary">${part.summary}</span></div>`;
+1
View File
@@ -72,6 +72,7 @@ export const chatStyles = css`
.part:is(details) { border-top: 1px solid #30363d; padding-top: 8px; }
summary { cursor: pointer; color: #8b949e; }
pre { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
.shell-output { color: #e6edf3; font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; }
`;
export const formattedTextStyles = css`
+8 -2
View File
@@ -3,14 +3,14 @@ import type { ChatLine } from "./components/shared";
import type { SessionUiEvent } from "./sessionSocket";
export function shellStartMessage(command: string, excludeFromContext?: boolean): ChatLine {
return textMessage("bash", `$ ${command}${excludeFromContext ? "\n\nexcluded from context" : ""}`);
return textMessage("bash", `${excludeFromContext ? "excluded from context\n\n" : ""}$ ${command}`);
}
export function appendShellChunk(messages: ChatLine[], chunk: string): ChatLine[] {
const last = messages.at(-1);
const lastPart = last?.parts.at(-1);
if (last?.role !== "bash" || lastPart?.type !== "text") return [...messages, textMessage("bash", chunk)];
const separator = lastPart.text.includes("\n\n") ? "" : "\n\n";
const separator = hasShellOutput(lastPart.text) ? "" : "\n\n";
return [...messages.slice(0, -1), { ...last, parts: [...last.parts.slice(0, -1), { ...lastPart, text: lastPart.text + separator + chunk }] }];
}
@@ -28,3 +28,9 @@ export function finalizeShellMessage(messages: ChatLine[], event: Extract<Sessio
if (!notes.length) return messages;
return [...messages.slice(0, -1), { ...last, parts: [...last.parts.slice(0, -1), { ...lastPart, text: `${lastPart.text}\n\n${notes.join("\n")}` }] }];
}
function hasShellOutput(text: string): boolean {
const outputStart = text.lastIndexOf("\n\n");
const promptStart = text.lastIndexOf("$ ");
return outputStart > promptStart;
}