fix(site/src/pages/AgentsPage): align thinking disclosure (#24743)

The agent chat thinking disclosure used a smaller label with the caret
on the left, which made collapsed and in-progress thinking look
different from collapsible tool calls.

Align the thinking disclosure with the shared tool-call row treatment by
using the same label size, trailing caret placement, and hover color
while preserving the streaming shimmer. Adds a Storybook story that
renders collapsed thinking next to a tool call.

<details>
<summary>Storybook screenshots</summary>

Captured from Storybook:

-
`pages/AgentsPage/ChatConversation/ConversationTimeline/ThinkingBlockWithToolCall`
-
`pages/AgentsPage/ChatConversation/ConversationTimeline/ThinkingBlockWithToolCall`
hovered
-
`pages/AgentsPage/ChatConversation/StreamingOutput/ThinkingDuringStreamingWithToolCalls`

Screenshots are attached in the Coder task.

</details>

Generated by Coder Agents.
This commit is contained in:
Kyle Carberry
2026-04-27 12:59:26 -07:00
committed by GitHub
parent adea1fa28f
commit ad3095106d
4 changed files with 76 additions and 15 deletions
@@ -1768,6 +1768,64 @@ export const ThinkingBlockAlwaysCollapsed: Story = {
},
};
/** Collapsed thinking should visually align with adjacent tool calls. */
export const ThinkingBlockWithToolCall: Story = {
parameters: {
queries: [
{
key: ["me", "preferences"],
data: {
task_notification_alert_dismissed: false,
thinking_display_mode: "always_collapsed" as const,
},
},
],
},
args: {
...defaultArgs,
parsedMessages: buildMessages([
{
...baseMessage,
id: 1,
role: "assistant",
content: [
{
type: "reasoning",
text: "I need to inspect the package metadata before answering.",
},
{
type: "tool-call",
tool_call_id: "tool-1",
tool_name: "read_file",
args: { path: "package.json" },
},
],
},
{
...baseMessage,
id: 2,
role: "tool",
content: [
{
type: "tool-result",
tool_call_id: "tool-1",
result: { content: '{"name":"coder"}' },
},
],
},
]),
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(
canvas.getByRole("button", { name: /thinking/i }),
).toBeInTheDocument();
expect(
canvas.getByRole("button", { name: /read package\.json/i }),
).toBeInTheDocument();
},
};
/**
* A completed thinking block with auto mode should be collapsed
* (non-streaming state means auto collapses).
@@ -154,31 +154,31 @@ const ReasoningDisclosure = memo<{
>
<CollapsibleTrigger
className={cn(
"border-0 bg-transparent p-0 m-0 font-[inherit] text-left",
"flex w-full items-center gap-1.5 cursor-pointer",
"border-0 bg-transparent p-0 m-0 font-[inherit] text-[inherit] text-left",
"flex w-full items-center gap-2 cursor-pointer",
"text-content-secondary transition-colors hover:text-content-primary",
)}
>
<ChevronDownIcon
className={cn(
"size-icon-sm shrink-0 transition-transform",
expanded ? "rotate-0" : "-rotate-90",
)}
/>
{isStreaming ? (
<Shimmer as="span" className="text-xs">
<Shimmer as="span" className="text-sm">
Thinking
</Shimmer>
) : (
<span className="text-xs">Thinking</span>
<span className="text-sm">Thinking</span>
)}
<ChevronDownIcon
className={cn(
"ml-auto h-3 w-3 shrink-0 text-current transition-transform",
expanded ? "rotate-0" : "-rotate-90",
)}
/>
</CollapsibleTrigger>
{hasText && (
<CollapsibleContent>
<div
ref={previewScrollRef}
className={cn(
"mt-1 pl-5",
"mt-1.5",
isPreviewConstrained && "max-h-24 overflow-y-auto",
)}
>
@@ -33,9 +33,11 @@ const hasTextOrReasoningBlock = (blocks: readonly RenderBlock[]): boolean =>
* collapsible thinking disclosure label.
*/
const StreamingThinkingPlaceholder: FC = () => (
<Shimmer as="span" className="text-xs text-content-secondary">
Thinking
</Shimmer>
<div className="flex w-full items-center gap-2 py-0.5 text-content-secondary">
<Shimmer as="span" className="text-sm">
Thinking
</Shimmer>
</div>
);
export const StreamingOutput: FC<{
@@ -31,13 +31,14 @@ export const ToolCollapsible: FC<ToolCollapsibleProps> = ({
className={cn(
"border-0 bg-transparent p-0 m-0 font-[inherit] text-[inherit] text-left",
"flex w-full items-center gap-2 cursor-pointer",
"text-content-secondary transition-colors hover:text-content-primary",
headerClassName,
)}
>
{header}
<ChevronDownIcon
className={cn(
"h-3 w-3 shrink-0 text-content-secondary transition-transform",
"ml-auto h-3 w-3 shrink-0 text-current transition-transform",
expanded ? "rotate-0" : "-rotate-90",
)}
/>