mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
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:
+58
@@ -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",
|
||||
)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user