diff --git a/packages/coding-agent/src/core/keybindings.ts b/packages/coding-agent/src/core/keybindings.ts index 9e6ba617..4a339d7f 100644 --- a/packages/coding-agent/src/core/keybindings.ts +++ b/packages/coding-agent/src/core/keybindings.ts @@ -111,9 +111,10 @@ export class KeybindingsManager { const manager = new KeybindingsManager(config); // Set up editor keybindings globally + // Include both editor actions and expandTools (shared between app and editor) const editorConfig: EditorKeybindingsConfig = {}; for (const [action, keys] of Object.entries(config)) { - if (!isAppAction(action)) { + if (!isAppAction(action) || action === "expandTools") { editorConfig[action as EditorAction] = keys; } } diff --git a/packages/coding-agent/src/modes/interactive/components/bash-execution.ts b/packages/coding-agent/src/modes/interactive/components/bash-execution.ts index 2893cce0..00caff8c 100644 --- a/packages/coding-agent/src/modes/interactive/components/bash-execution.ts +++ b/packages/coding-agent/src/modes/interactive/components/bash-execution.ts @@ -2,7 +2,7 @@ * Component for displaying bash command execution with streaming output. */ -import { Container, Loader, Spacer, Text, type TUI } from "@mariozechner/pi-tui"; +import { Container, getEditorKeybindings, Loader, Spacer, Text, type TUI } from "@mariozechner/pi-tui"; import stripAnsi from "strip-ansi"; import { DEFAULT_MAX_BYTES, @@ -166,10 +166,15 @@ export class BashExecutionComponent extends Container { // Show how many lines are hidden (collapsed preview) if (hiddenLineCount > 0) { + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; if (this.expanded) { - statusParts.push(theme.fg("dim", "(ctrl+o to collapse)")); + statusParts.push(`(${theme.fg("dim", expandKey)}${theme.fg("muted", " to collapse")})`); } else { - statusParts.push(theme.fg("dim", `... ${hiddenLineCount} more lines (ctrl+o to expand)`)); + statusParts.push( + theme.fg("muted", `... ${hiddenLineCount} more lines (`) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"), + ); } } 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 index a108b466..74198835 100644 --- a/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts +++ b/packages/coding-agent/src/modes/interactive/components/branch-summary-message.ts @@ -1,4 +1,4 @@ -import { Box, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; +import { Box, getEditorKeybindings, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; import type { BranchSummaryMessage } from "../../../core/messages.js"; import { getMarkdownTheme, theme } from "../theme/theme.js"; @@ -41,7 +41,16 @@ export class BranchSummaryMessageComponent extends Box { }), ); } else { - this.addChild(new Text(theme.fg("customMessageText", "Branch summary (ctrl+o to expand)"), 0, 0)); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + this.addChild( + new Text( + theme.fg("customMessageText", "Branch summary (") + + theme.fg("dim", expandKey) + + theme.fg("customMessageText", " 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 9da74053..54ad755e 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,4 +1,4 @@ -import { Box, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; +import { Box, getEditorKeybindings, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; import type { CompactionSummaryMessage } from "../../../core/messages.js"; import { getMarkdownTheme, theme } from "../theme/theme.js"; @@ -42,8 +42,15 @@ export class CompactionSummaryMessageComponent extends Box { }), ); } else { + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; this.addChild( - new Text(theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (ctrl+o to expand)`), 0, 0), + new Text( + theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (`) + + theme.fg("dim", expandKey) + + theme.fg("customMessageText", " to expand)"), + 0, + 0, + ), ); } } diff --git a/packages/coding-agent/src/modes/interactive/components/tool-execution.ts b/packages/coding-agent/src/modes/interactive/components/tool-execution.ts index bfc6ee4d..4156cc5d 100644 --- a/packages/coding-agent/src/modes/interactive/components/tool-execution.ts +++ b/packages/coding-agent/src/modes/interactive/components/tool-execution.ts @@ -3,6 +3,7 @@ import { Box, Container, getCapabilities, + getEditorKeybindings, getImageDimensions, Image, imageFallback, @@ -374,9 +375,15 @@ export class ToolExecutionComponent extends Container { cachedSkipped = result.skippedCount; cachedWidth = width; } - return cachedSkipped && cachedSkipped > 0 - ? ["", theme.fg("toolOutput", `... (${cachedSkipped} earlier lines)`), ...cachedLines] - : cachedLines; + if (cachedSkipped && cachedSkipped > 0) { + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + const hint = + theme.fg("muted", `... (${cachedSkipped} earlier lines, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); + return ["", hint, ...cachedLines]; + } + return cachedLines; }, invalidate: () => { cachedWidth = undefined; @@ -469,7 +476,11 @@ export class ToolExecutionComponent extends Container { .map((line: string) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line)))) .join("\n"); if (remaining > 0) { - text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + text += + theme.fg("muted", `\n... (${remaining} more lines, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); } const truncation = this.result.details?.truncation; @@ -526,7 +537,11 @@ export class ToolExecutionComponent extends Container { .map((line: string) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line)))) .join("\n"); if (remaining > 0) { - text += theme.fg("toolOutput", `\n... (${remaining} more lines, ${totalLines} total)`); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + text += + theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); } } } else if (this.toolName === "edit") { @@ -584,7 +599,11 @@ export class ToolExecutionComponent extends Container { text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { - text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + text += + theme.fg("muted", `\n... (${remaining} more lines, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); } } @@ -625,7 +644,11 @@ export class ToolExecutionComponent extends Container { text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { - text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + text += + theme.fg("muted", `\n... (${remaining} more lines, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); } } @@ -670,7 +693,11 @@ export class ToolExecutionComponent extends Container { text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { - text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); + const expandKey = getEditorKeybindings().getKeys("expandTools")[0]!; + text += + theme.fg("muted", `\n... (${remaining} more lines, `) + + theme.fg("dim", expandKey) + + theme.fg("muted", " to expand)"); } } diff --git a/packages/tui/src/keybindings.ts b/packages/tui/src/keybindings.ts index 41ec97df..9e440090 100644 --- a/packages/tui/src/keybindings.ts +++ b/packages/tui/src/keybindings.ts @@ -31,7 +31,9 @@ export type EditorAction = | "selectConfirm" | "selectCancel" // Clipboard - | "copy"; + | "copy" + // Tool output + | "expandTools"; // Re-export KeyId from keys.ts export type { KeyId }; @@ -75,6 +77,8 @@ export const DEFAULT_EDITOR_KEYBINDINGS: Required = { selectCancel: ["escape", "ctrl+c"], // Clipboard copy: "ctrl+c", + // Tool output + expandTools: "ctrl+o", }; /**