fix(coding-agent): toggle summary entries on click
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Box, Markdown, type MarkdownTheme, Spacer, Text } from "@earendil-works/pi-tui";
|
||||
import { Box, Container, Markdown, type MarkdownTheme, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
|
||||
import type { BranchSummaryMessage } from "../../../core/messages.ts";
|
||||
import { getMarkdownTheme, theme } from "../theme/theme.ts";
|
||||
import { keyText } from "./keybinding-hints.ts";
|
||||
@@ -31,20 +31,21 @@ export class BranchSummaryMessageComponent extends Box {
|
||||
|
||||
private updateDisplay(): void {
|
||||
this.clear();
|
||||
const content = new Container();
|
||||
|
||||
const label = theme.fg("customMessageLabel", `\x1b[1m[branch]\x1b[22m`);
|
||||
this.addChild(new Text(label, 0, 0));
|
||||
this.addChild(new Spacer(1));
|
||||
content.addChild(new Text(label, 0, 0));
|
||||
content.addChild(new Spacer(1));
|
||||
|
||||
if (this.expanded) {
|
||||
const header = "**Branch Summary**\n\n";
|
||||
this.addChild(
|
||||
content.addChild(
|
||||
new Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {
|
||||
color: (text: string) => theme.fg("customMessageText", text),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
this.addChild(
|
||||
content.addChild(
|
||||
new Text(
|
||||
theme.fg("customMessageText", "Branch summary (") +
|
||||
theme.fg("dim", keyText("app.tools.expand")) +
|
||||
@@ -54,5 +55,13 @@ export class BranchSummaryMessageComponent extends Box {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
this.addChild(
|
||||
new MouseRegion(content, (event) => {
|
||||
if (event.type !== "click" || event.button !== "left") return undefined;
|
||||
this.setExpanded(!this.expanded);
|
||||
return { handled: true };
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -1,4 +1,4 @@
|
||||
import { Box, Markdown, type MarkdownTheme, Spacer, Text } from "@earendil-works/pi-tui";
|
||||
import { Box, Container, Markdown, type MarkdownTheme, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
|
||||
import type { CompactionSummaryMessage } from "../../../core/messages.ts";
|
||||
import { getMarkdownTheme, theme } from "../theme/theme.ts";
|
||||
import { keyText } from "./keybinding-hints.ts";
|
||||
@@ -31,21 +31,22 @@ export class CompactionSummaryMessageComponent extends Box {
|
||||
|
||||
private updateDisplay(): void {
|
||||
this.clear();
|
||||
const content = new Container();
|
||||
|
||||
const tokenStr = this.message.tokensBefore.toLocaleString();
|
||||
const label = theme.fg("customMessageLabel", `\x1b[1m[compaction]\x1b[22m`);
|
||||
this.addChild(new Text(label, 0, 0));
|
||||
this.addChild(new Spacer(1));
|
||||
content.addChild(new Text(label, 0, 0));
|
||||
content.addChild(new Spacer(1));
|
||||
|
||||
if (this.expanded) {
|
||||
const header = `**Compacted from ${tokenStr} tokens**\n\n`;
|
||||
this.addChild(
|
||||
content.addChild(
|
||||
new Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {
|
||||
color: (text: string) => theme.fg("customMessageText", text),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
this.addChild(
|
||||
content.addChild(
|
||||
new Text(
|
||||
theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (`) +
|
||||
theme.fg("dim", keyText("app.tools.expand")) +
|
||||
@@ -55,5 +56,13 @@ export class CompactionSummaryMessageComponent extends Box {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
this.addChild(
|
||||
new MouseRegion(content, (event) => {
|
||||
if (event.type !== "click" || event.button !== "left") return undefined;
|
||||
this.setExpanded(!this.expanded);
|
||||
return { handled: true };
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Markdown, type MarkdownTheme, Text } from "@earendil-works/pi-tui";
|
||||
import { Box, Container, Markdown, type MarkdownTheme, MouseRegion, Text } from "@earendil-works/pi-tui";
|
||||
import type { ParsedSkillBlock } from "../../../core/agent-session.ts";
|
||||
import { getMarkdownTheme, theme } from "../theme/theme.ts";
|
||||
import { keyText } from "./keybinding-hints.ts";
|
||||
@@ -32,13 +32,14 @@ export class SkillInvocationMessageComponent extends Box {
|
||||
|
||||
private updateDisplay(): void {
|
||||
this.clear();
|
||||
const content = new Container();
|
||||
|
||||
if (this.expanded) {
|
||||
// Expanded: label + skill name header + full content
|
||||
const label = theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m`);
|
||||
this.addChild(new Text(label, 0, 0));
|
||||
content.addChild(new Text(label, 0, 0));
|
||||
const header = `**${this.skillBlock.name}**\n\n`;
|
||||
this.addChild(
|
||||
content.addChild(
|
||||
new Markdown(header + this.skillBlock.content, 0, 0, this.markdownTheme, {
|
||||
color: (text: string) => theme.fg("customMessageText", text),
|
||||
}),
|
||||
@@ -49,7 +50,15 @@ export class SkillInvocationMessageComponent extends Box {
|
||||
theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m `) +
|
||||
theme.fg("customMessageText", this.skillBlock.name) +
|
||||
theme.fg("dim", ` (${keyText("app.tools.expand")} to expand)`);
|
||||
this.addChild(new Text(line, 0, 0));
|
||||
content.addChild(new Text(line, 0, 0));
|
||||
}
|
||||
|
||||
this.addChild(
|
||||
new MouseRegion(content, (event) => {
|
||||
if (event.type !== "click" || event.button !== "left") return undefined;
|
||||
this.setExpanded(!this.expanded);
|
||||
return { handled: true };
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { Box, TuiMouseEvent } from "@earendil-works/pi-tui";
|
||||
import { beforeAll, describe, expect, test } from "vitest";
|
||||
import { BranchSummaryMessageComponent } from "../src/modes/interactive/components/branch-summary-message.ts";
|
||||
import { CompactionSummaryMessageComponent } from "../src/modes/interactive/components/compaction-summary-message.ts";
|
||||
import { SkillInvocationMessageComponent } from "../src/modes/interactive/components/skill-invocation-message.ts";
|
||||
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { stripAnsi } from "../src/utils/ansi.ts";
|
||||
|
||||
const WIDTH = 80;
|
||||
|
||||
function renderText(component: Box): string {
|
||||
return stripAnsi(component.render(WIDTH).join("\n"));
|
||||
}
|
||||
|
||||
function clickRow(component: Box, marker: string): void {
|
||||
const lines = component.render(WIDTH);
|
||||
const row = lines.findIndex((line) => stripAnsi(line).includes(marker));
|
||||
expect(row).toBeGreaterThanOrEqual(0);
|
||||
const event: TuiMouseEvent = {
|
||||
type: "click",
|
||||
button: "left",
|
||||
x: 2,
|
||||
y: row,
|
||||
screenX: 2,
|
||||
screenY: row,
|
||||
width: WIDTH,
|
||||
height: lines.length,
|
||||
shift: false,
|
||||
alt: false,
|
||||
ctrl: false,
|
||||
clickCount: 1,
|
||||
};
|
||||
expect(component.handleMouse(event)?.handled).toBe(true);
|
||||
}
|
||||
|
||||
describe("collapsible message components", () => {
|
||||
beforeAll(() => initTheme("dark"));
|
||||
|
||||
test("toggles a compaction summary when clicked", () => {
|
||||
const component = new CompactionSummaryMessageComponent({
|
||||
role: "compactionSummary",
|
||||
summary: "compaction details",
|
||||
tokensBefore: 1234,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
expect(renderText(component)).not.toContain("compaction details");
|
||||
clickRow(component, "[compaction]");
|
||||
expect(renderText(component)).toContain("compaction details");
|
||||
clickRow(component, "[compaction]");
|
||||
expect(renderText(component)).not.toContain("compaction details");
|
||||
});
|
||||
|
||||
test("toggles a branch summary when clicked", () => {
|
||||
const component = new BranchSummaryMessageComponent({
|
||||
role: "branchSummary",
|
||||
summary: "branch details",
|
||||
fromId: "entry-1",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
expect(renderText(component)).not.toContain("branch details");
|
||||
clickRow(component, "[branch]");
|
||||
expect(renderText(component)).toContain("branch details");
|
||||
clickRow(component, "[branch]");
|
||||
expect(renderText(component)).not.toContain("branch details");
|
||||
});
|
||||
|
||||
test("toggles a skill invocation when clicked", () => {
|
||||
const component = new SkillInvocationMessageComponent({
|
||||
name: "example-skill",
|
||||
location: "/tmp/example-skill.md",
|
||||
content: "skill details",
|
||||
userMessage: undefined,
|
||||
});
|
||||
|
||||
expect(renderText(component)).not.toContain("skill details");
|
||||
clickRow(component, "[skill]");
|
||||
expect(renderText(component)).toContain("skill details");
|
||||
clickRow(component, "[skill]");
|
||||
expect(renderText(component)).not.toContain("skill details");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user