fix(web): always allow expanding tool execute to reveal full command (#1086)

* fix(web): always allow expanding tool execute to reveal full command

* fix(web): keep tool execute tooltip trigger keyboard-focusable
This commit is contained in:
José Almeida
2026-05-26 18:21:47 +01:00
committed by GitHub
parent 991a6315c3
commit ec8039ca71
@@ -53,7 +53,6 @@ function ExecuteStatusIcon({
type ExecuteOutputProps = {
displayCommand: string;
isCommandLong: boolean;
displayWorkDir: string | null;
workDir: string | undefined;
output: ShellExecOutput | undefined;
@@ -61,18 +60,15 @@ type ExecuteOutputProps = {
function ExecuteOutputContent({
displayCommand,
isCommandLong,
displayWorkDir,
workDir,
output,
}: ExecuteOutputProps) {
return (
<div className="pl-4 border-l-2 border-border/30 space-y-2">
{isCommandLong && (
<pre className="text-xs bg-muted/30 rounded p-2 whitespace-pre-wrap break-all font-mono">
{displayCommand}
</pre>
)}
<pre className="text-xs bg-muted/30 rounded p-2 whitespace-pre-wrap break-all font-mono">
{displayCommand}
</pre>
{displayWorkDir && (
<div className="text-xs text-muted-foreground">
<span className="opacity-60">cwd:</span>{" "}
@@ -95,10 +91,6 @@ function ExecuteOutputContent({
);
}
// Threshold beyond which the command is considered "long" and worth surfacing
// in the expanded view (the header always truncates via CSS regardless).
const LONG_COMMAND_THRESHOLD = 60;
function isExecuteSuccess(
status: ToolExecuteMetadata["status"],
output: ShellExecOutput | undefined,
@@ -113,28 +105,18 @@ function parseExecuteMetadata(comment: Message) {
const shellExec = metadata?.normalized?.shell_exec;
const output = shellExec?.output;
const workDir = shellExec?.work_dir;
const hasOutput = !!(output?.stdout || output?.stderr);
const isSuccess = isExecuteSuccess(status, output);
return { status, output, workDir, hasOutput, isSuccess };
return { status, output, workDir, isSuccess };
}
function CommandHeader({
displayCommand,
isCommandLong,
}: {
displayCommand: string;
isCommandLong: boolean;
}) {
function CommandHeader({ displayCommand }: { displayCommand: string }) {
const className = "font-mono text-xs text-muted-foreground truncate min-w-0 flex-1 text-left";
if (!isCommandLong) {
return <span className={className}>{displayCommand}</span>;
}
return (
<Tooltip>
<TooltipTrigger asChild>
<button type="button" className={`${className} cursor-pointer bg-transparent border-0 p-0`}>
<span className={className} tabIndex={0}>
{displayCommand}
</button>
</span>
</TooltipTrigger>
<TooltipContent
side="bottom"
@@ -151,22 +133,18 @@ export const ToolExecuteMessage = memo(function ToolExecuteMessage({
comment,
worktreePath,
}: ToolExecuteMessageProps) {
const { status, output, workDir, hasOutput, isSuccess } = parseExecuteMetadata(comment);
const { status, output, workDir, isSuccess } = parseExecuteMetadata(comment);
const autoExpanded = status === "running";
const { isExpanded, handleToggle } = useExpandState(status, autoExpanded);
const displayCommand = transformPathsInText(comment.content, worktreePath);
const displayWorkDir = workDir ? transformPathsInText(workDir, worktreePath) : null;
// Measure after path transform so a long absolute path that gets shortened
// to a relative one is correctly treated as short.
const isCommandLong = displayCommand.length > LONG_COMMAND_THRESHOLD;
const hasExpandableContent = hasOutput || !!workDir || isCommandLong;
return (
<ExpandableRow
icon={<IconTerminal className="h-4 w-4 text-muted-foreground" />}
header={
<div className="flex items-center gap-2 text-xs min-w-0">
<CommandHeader displayCommand={displayCommand} isCommandLong={isCommandLong} />
<CommandHeader displayCommand={displayCommand} />
{!isSuccess && (
<span className="shrink-0">
<ExecuteStatusIcon status={status} exitCode={output?.exit_code} />
@@ -174,13 +152,12 @@ export const ToolExecuteMessage = memo(function ToolExecuteMessage({
)}
</div>
}
hasExpandableContent={hasExpandableContent}
hasExpandableContent
isExpanded={isExpanded}
onToggle={handleToggle}
>
<ExecuteOutputContent
displayCommand={displayCommand}
isCommandLong={isCommandLong}
displayWorkDir={displayWorkDir}
workDir={workDir}
output={output}