fix(chat): make the working status row swap seamlessly into the turn footer

The streaming status line and the finished turn footer are the same visual
line, but the swap used to jump: different font/color, a 2px left inset, a
20px row vs the footer's 32px (its h-8 action buttons set the height), and
the bottom-anchored chat pulling the line up because the finished message
carries more structure below its footer than the status row had.

Match the footer exactly: text-sm at muted-foreground/60, no left inset,
h-8 row, and mb-6 reserving the missing space below. Verified against the
live DOM: the footer appears at the exact pixel position the status row
occupied.
This commit is contained in:
Bohdan Triapitsyn
2026-08-23 00:41:40 +03:00
parent 46426e8495
commit 3195fb1190
2 changed files with 17 additions and 7 deletions
+11 -5
View File
@@ -306,13 +306,19 @@ export const StatusRow: React.FC<StatusRowProps> = ({
return (
<div
// Mobile: breathing room between the last message and the agent status
// line — without it the "<model> is running…" row sits flush against
// the message above.
className={cn("mb-1", isMobile && "mt-2", !hasLeftAccessory && "chat-column")}
// This row must land exactly where the assistant turn footer (mt-2
// inside the message) appears when the turn completes. Measured against
// the live DOM: the gap ABOVE already matches (message pb-2 = footer
// mt-2 = 8px), but the chat is bottom-anchored and the finished message
// carries ~12px more structure BELOW its footer than this row has — so
// the swap used to lift the line up. mb-6 (24px) reserves that space
// under this row instead (verified: row top 636 == footer top 636).
className={cn("mb-6", !hasLeftAccessory && "chat-column")}
style={STATUS_ROW_CONTAINER_STYLE}
>
<div className={cn("flex items-center justify-between py-0.5 gap-2 h-[1.2rem]", hasLeftAccessory && "px-0.5")}>
{/* h-8 matches the turn footer's real row height: its h-8 action
buttons define the footer line, with the meta text centered in it. */}
<div className={cn("flex items-center justify-between gap-2 h-8", hasLeftAccessory && "px-0.5")}>
{/* Left: Abort status | Working placeholder | leftAccessory */}
<div className={cn("flex-1 flex items-center min-w-0 gap-2", hasLeftAccessory ? "pl-1.5" : "overflow-x-hidden")}>
{showAssistantStatus && showAbortStatus ? (
@@ -229,15 +229,19 @@ export function WorkingPlaceholder({
return (
<div
// Styled to mirror the turn footer's model row (text-sm,
// muted-foreground/60, no left inset): when the turn completes this row
// disappears and the footer appears in the same visual spot, so the two
// must read as the same line swapping its text.
className={
'flex h-full items-center text-muted-foreground pl-0.5'
'flex h-full items-center text-muted-foreground/60'
}
role="status"
aria-live={displayedPermission ? 'assertive' : 'polite'}
aria-label={label}
data-waiting={displayedPermission ? 'true' : undefined}
>
<span className="typography-ui-header">
<span className="text-sm">
{hasProviderLogo && providerLogoSrc ? (
<img
src={providerLogoSrc}