Archived
Render skill invocations compactly
This commit is contained in:
@@ -47,5 +47,6 @@ export function summarizeChatGroup(messages: ChatLine[]): string {
|
||||
|
||||
function isReadablePart(message: ChatLine, part: ChatPart): boolean {
|
||||
if (message.source === "compaction" || message.source === "branch_summary") return false;
|
||||
if (part.type === "skillInvocation") return true;
|
||||
return part.type === "text" && (message.role === "user" || message.role === "assistant" || message.role === "system" || message.role === "bash");
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ function normalizeMessage(message: unknown): ChatLine[] {
|
||||
if (getString(message, "role") === "bashExecution") return [normalizeBashExecution(message)];
|
||||
const role = normalizeRole(getString(message, "role"));
|
||||
const parts = normalizeContent(getProperty(message, "content"), message);
|
||||
const skillLines = role === "user" ? normalizeSkillInvocation(parts) : undefined;
|
||||
if (skillLines !== undefined) return skillLines;
|
||||
const source = normalizeSource(message, parts);
|
||||
if (role === "tool") return [{ role, parts, ...(source === undefined ? {} : { source }) }];
|
||||
|
||||
@@ -31,6 +33,28 @@ function normalizeMessage(message: unknown): ChatLine[] {
|
||||
return visible.length > 0 ? [{ role, parts: visible, ...(source === undefined ? {} : { source }) }] : [];
|
||||
}
|
||||
|
||||
function normalizeSkillInvocation(parts: ChatPart[]): ChatLine[] | undefined {
|
||||
if (parts.length !== 1 || parts[0]?.type !== "text") return undefined;
|
||||
const skill = parseSkillBlock(parts[0].text);
|
||||
if (skill === undefined) return undefined;
|
||||
return [
|
||||
{ role: "user", parts: [{ type: "skillInvocation", name: skill.name, location: skill.location, content: skill.content }] },
|
||||
...(skill.userMessage === undefined ? [] : [{ role: "user" as const, parts: [{ type: "text" as const, text: skill.userMessage }] }]),
|
||||
];
|
||||
}
|
||||
|
||||
function parseSkillBlock(text: string): { name: string; location: string; content: string; userMessage?: string } | undefined {
|
||||
const match = text.match(/^<skill name="([^"]+)" location="([^"]+)">\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);
|
||||
if (match === null) return undefined;
|
||||
const userMessage = match[4]?.trim();
|
||||
return {
|
||||
name: match[1] ?? "skill",
|
||||
location: match[2] ?? "",
|
||||
content: match[3] ?? "",
|
||||
...(userMessage === undefined || userMessage === "" ? {} : { userMessage }),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeSource(message: unknown, _parts: ChatPart[]): ChatLine["source"] | undefined {
|
||||
const source = getString(message, "source");
|
||||
if (source === "compaction" || source === "branch_summary") return source;
|
||||
|
||||
@@ -129,6 +129,13 @@ export class ChatView extends LitElement {
|
||||
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 === "skillInvocation") return html`
|
||||
<details class="part skill-invocation">
|
||||
<summary><b>[skill]</b> ${part.name}</summary>
|
||||
<small>${part.location}</small>
|
||||
<formatted-text .text=${part.content}></formatted-text>
|
||||
</details>
|
||||
`;
|
||||
if (part.type === "toolCall") return html`<div class="part tool-line">▶ ${part.toolName}<span class="summary">${part.summary}</span></div>`;
|
||||
if (part.type === "toolResult") return html`
|
||||
<details class="part" ?open=${part.isError}>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { css } from "lit";
|
||||
export type ChatPart =
|
||||
| { type: "text"; text: string }
|
||||
| { type: "thinking"; text: string }
|
||||
| { type: "skillInvocation"; name: string; location: string; content: string }
|
||||
| { type: "toolCall"; toolName: string; summary: string }
|
||||
| { type: "toolResult"; toolName: string; text: string; isError: boolean }
|
||||
| { type: "empty" };
|
||||
@@ -75,6 +76,9 @@ export const chatStyles = css`
|
||||
.tool-line { color: #d29922; }
|
||||
.summary { color: #8b949e; margin-left: 6px; }
|
||||
.part:is(details) { border-top: 1px solid #30363d; padding-top: 8px; }
|
||||
.skill-invocation { border: 1px solid #30363d; border-radius: 8px; background: #161b22; padding: 8px 10px; }
|
||||
.skill-invocation > summary { color: #d2a8ff; }
|
||||
.skill-invocation > small { display: block; margin: 6px 0 8px; color: #8b949e; }
|
||||
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; }
|
||||
|
||||
Reference in New Issue
Block a user