From 4ff5f61ffc3245a583a57c2d3df793e339984a55 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sun, 28 Dec 2025 14:22:15 +0100 Subject: [PATCH] Add BranchSummaryMessageComponent, unify styling with hook messages - CompactionSummaryMessageComponent now extends Box, uses customMessageBg - New BranchSummaryMessageComponent for branch summaries - Both use same background color as HookMessageComponent for consistency - Added Spacer before compaction/branch components in chat --- .../components/branch-summary-message.ts | 42 +++++++++++++++++++ .../components/compaction-summary-message.ts | 34 +++++++-------- .../src/modes/interactive/interactive-mode.ts | 11 ++--- 3 files changed, 60 insertions(+), 27 deletions(-) create mode 100644 packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts diff --git a/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts b/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts new file mode 100644 index 00000000..d46b2bc5 --- /dev/null +++ b/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts @@ -0,0 +1,42 @@ +import { Box, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; +import type { BranchSummaryMessage } from "../../../core/messages.js"; +import { getMarkdownTheme, theme } from "../theme/theme.js"; + +/** + * Component that renders a branch summary message with collapsed/expanded state. + * Uses same background color as hook messages for visual consistency. + */ +export class BranchSummaryMessageComponent extends Box { + private expanded = false; + private message: BranchSummaryMessage; + + constructor(message: BranchSummaryMessage) { + super(1, 1, (t) => theme.bg("customMessageBg", t)); + this.message = message; + this.updateDisplay(); + } + + setExpanded(expanded: boolean): void { + this.expanded = expanded; + this.updateDisplay(); + } + + private updateDisplay(): void { + this.clear(); + + const label = theme.fg("customMessageLabel", `\x1b[1m[branch]\x1b[22m`); + this.addChild(new Text(label, 0, 0)); + this.addChild(new Spacer(1)); + + if (this.expanded) { + const header = "**Branch Summary**\n\n"; + this.addChild( + new Markdown(header + this.message.summary, 0, 0, getMarkdownTheme(), { + color: (text: string) => theme.fg("customMessageText", text), + }), + ); + } else { + this.addChild(new Text(theme.fg("customMessageText", "Branch summary (ctrl+o to expand)"), 0, 0)); + } + } +} diff --git a/packages/coding-agent/src/modes/interactive/components/compaction-summary-message.ts b/packages/coding-agent/src/modes/interactive/components/compaction-summary-message.ts index 049880a4..dc07d3b5 100644 --- a/packages/coding-agent/src/modes/interactive/components/compaction-summary-message.ts +++ b/packages/coding-agent/src/modes/interactive/components/compaction-summary-message.ts @@ -1,18 +1,17 @@ -import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; -import type { CompactionSummaryMessage } from "packages/coding-agent/src/core/messages.js"; +import { Box, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; +import type { CompactionSummaryMessage } from "../../../core/messages.js"; import { getMarkdownTheme, theme } from "../theme/theme.js"; /** * Component that renders a compaction message with collapsed/expanded state. - * Collapsed: shows "Context compacted from X tokens" - * Expanded: shows the full summary rendered as markdown (like a user message) + * Uses same background color as hook messages for visual consistency. */ -export class CompactionSummaryMessageComponent extends Container { +export class CompactionSummaryMessageComponent extends Box { private expanded = false; private message: CompactionSummaryMessage; constructor(message: CompactionSummaryMessage) { - super(); + super(1, 1, (t) => theme.bg("customMessageBg", t)); this.message = message; this.updateDisplay(); } @@ -25,26 +24,21 @@ export class CompactionSummaryMessageComponent extends Container { private updateDisplay(): void { this.clear(); + 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)); + if (this.expanded) { - // Show header + summary as markdown (like user message) - this.addChild(new Spacer(1)); - const header = `**Context compacted from ${this.message.tokensBefore.toLocaleString()} tokens**\n\n`; + const header = `**Compacted from ${tokenStr} tokens**\n\n`; this.addChild( - new Markdown(header + this.message.summary, 1, 1, getMarkdownTheme(), { - bgColor: (text: string) => theme.bg("userMessageBg", text), - color: (text: string) => theme.fg("userMessageText", text), + new Markdown(header + this.message.summary, 0, 0, getMarkdownTheme(), { + color: (text: string) => theme.fg("customMessageText", text), }), ); - this.addChild(new Spacer(1)); } else { - // Collapsed: simple text in warning color with token count - const tokenStr = this.message.tokensBefore.toLocaleString(); this.addChild( - new Text( - theme.fg("warning", `Earlier messages compacted from ${tokenStr} tokens (ctrl+o to expand)`), - 1, - 1, - ), + new Text(theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (ctrl+o to expand)`), 0, 0), ); } } diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 405bcf23..18149fd4 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -38,6 +38,7 @@ import { copyToClipboard } from "../../utils/clipboard.js"; import { ArminComponent } from "./components/armin.js"; import { AssistantMessageComponent } from "./components/assistant-message.js"; import { BashExecutionComponent } from "./components/bash-execution.js"; +import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js"; import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js"; import { CustomEditor } from "./components/custom-editor.js"; import { DynamicBorder } from "./components/dynamic-border.js"; @@ -1071,19 +1072,15 @@ export class InteractiveMode { break; } case "compactionSummary": { + this.chatContainer.addChild(new Spacer(1)); const component = new CompactionSummaryMessageComponent(message); component.setExpanded(this.toolOutputExpanded); this.chatContainer.addChild(component); break; } case "branchSummary": { - // Branch summaries are rendered as compaction summaries - const component = new CompactionSummaryMessageComponent({ - role: "compactionSummary", - summary: message.summary, - tokensBefore: 0, - timestamp: message.timestamp, - }); + this.chatContainer.addChild(new Spacer(1)); + const component = new BranchSummaryMessageComponent(message); component.setExpanded(this.toolOutputExpanded); this.chatContainer.addChild(component); break;