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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user