diff --git a/index.ts b/index.ts index 81340c0..d6f214f 100644 --- a/index.ts +++ b/index.ts @@ -1020,10 +1020,13 @@ export default function piIntercomExtension(pi: ExtensionAPI) { } }); - pi.registerMessageRenderer("intercom_message", (message, _options, theme) => { + pi.registerMessageRenderer("intercom_message", (message, options, theme) => { const details = message.details as { from: SessionInfo; message: Message; replyCommand?: string; bodyText?: string } | undefined; if (!details) return undefined; - return new InlineMessageComponent(details.from, details.message, theme, details.replyCommand, details.bodyText); + // CustomMessageComponent calls our renderer with { expanded } on setExpanded/rebuild. + // Collapse intercom when tool outputs are collapsed, expand when tool outputs expand. + const collapsed = !options.expanded; + return new InlineMessageComponent(details.from, details.message, theme, details.replyCommand, details.bodyText, collapsed); }); const childOrchestratorMetadata = readChildOrchestratorMetadata(); diff --git a/ui/inline-message.ts b/ui/inline-message.ts index 2a9a7f1..e330b1b 100644 --- a/ui/inline-message.ts +++ b/ui/inline-message.ts @@ -3,32 +3,62 @@ import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@mariozechner/p import type { Theme } from "@mariozechner/pi-coding-agent"; import type { SessionInfo, Message } from "../types.js"; +const CTRL_O_HINT = "ctrl+o"; + export class InlineMessageComponent implements Component { private from: SessionInfo; private message: Message; private theme: Theme; private replyCommand?: string; private bodyText?: string; + private collapsed: boolean; - constructor(from: SessionInfo, message: Message, theme: Theme, replyCommand?: string, bodyText?: string) { + constructor(from: SessionInfo, message: Message, theme: Theme, replyCommand?: string, bodyText?: string, collapsed = false) { this.from = from; this.message = message; this.theme = theme; this.replyCommand = replyCommand; this.bodyText = bodyText; + this.collapsed = collapsed; } invalidate(): void {} render(width: number): string[] { + const senderName = this.from.name || this.from.id.slice(0, 8); + const bodyWidth = Math.max(1, width - 2); + + if (this.collapsed) { + // Three-line collapsed view: matches expanded box pattern + const borderChar = "─"; + const headerLabel = ` 📨 From: ${senderName} (${this.from.cwd}) `; + const hint = ` (${CTRL_O_HINT}) `; + const headerText = truncateToWidth(headerLabel, bodyWidth - visibleWidth(hint), ""); + const headerPad = Math.max(0, bodyWidth - visibleWidth(headerText) - visibleWidth(hint)); + const topLine = `╭${headerText}${borderChar.repeat(headerPad)}${hint}╮`; + + // Middle line: follows same pattern as expanded — │${text}${padding}│ + const rawPreview = (this.bodyText || this.message.content.text).replace(/\n/g, " "); + const preview = truncateToWidth(rawPreview, bodyWidth - 2, "..."); + const previewContent = ` ${preview}`; + const midPadding = Math.max(0, bodyWidth - visibleWidth(previewContent)); + const midLine = `│${previewContent}${" ".repeat(midPadding)}│`; + + const bottomLine = `╰${borderChar.repeat(bodyWidth)}╯`; + + return [ + this.theme.fg("accent", topLine), + this.theme.fg("accent", midLine), + this.theme.fg("accent", bottomLine), + ]; + } + const lines: string[] = []; const borderChar = "─"; if (width < 3) { - return [truncateToWidth(`From ${this.from.name || this.from.id.slice(0, 8)}`, width)]; + return [truncateToWidth(`From ${senderName}`, width)]; } - const bodyWidth = Math.max(1, width - 2); - const senderName = this.from.name || this.from.id.slice(0, 8); const header = ` 📨 From: ${senderName} (${this.from.cwd}) `; const headerText = truncateToWidth(header, bodyWidth, ""); const headerPadding = Math.max(0, bodyWidth - visibleWidth(headerText));