From 11d5a75c79df17e0662817e2bfacafb36fe350c8 Mon Sep 17 00:00:00 2001 From: claude_code Date: Mon, 22 Jun 2026 19:54:17 +0300 Subject: [PATCH] fix(ai-chat): improve error banner layout The AI chat error Alert stranded the warning icon in the top-left while the detail text hung indented under the heading, wrapping to 3 narrow lines with empty space below. Switch to a "full-width detail" layout (icon + bold heading on the first row, detail spanning full width below) and extract the markup, previously duplicated in ChatThread and MessageItem, into a single shared ChatErrorAlert component. - add apps/client/src/features/ai-chat/components/chat-error-alert.tsx - use it for the live stream error in chat-thread.tsx (mb="xs") - use it for the persisted history error in message-item.tsx (mt={4}) - heading/icon use the adaptive --mantine-color-red-light-color so the banner stays correct in dark mode Co-Authored-By: Claude Opus 4.8 --- .../ai-chat/components/chat-error-alert.tsx | 38 +++++++++++++++++++ .../ai-chat/components/chat-thread.tsx | 17 ++++----- .../ai-chat/components/message-item.tsx | 16 +++----- 3 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 apps/client/src/features/ai-chat/components/chat-error-alert.tsx diff --git a/apps/client/src/features/ai-chat/components/chat-error-alert.tsx b/apps/client/src/features/ai-chat/components/chat-error-alert.tsx new file mode 100644 index 00000000..e8fd2c5c --- /dev/null +++ b/apps/client/src/features/ai-chat/components/chat-error-alert.tsx @@ -0,0 +1,38 @@ +import { Alert, Group, Text, type AlertProps } from "@mantine/core"; +import { IconAlertTriangle } from "@tabler/icons-react"; + +/** + * A classified AI chat error banner: a warning icon + bold heading on the first + * row, with the detail text spanning the full width below. Rendered for BOTH the + * live stream error (ChatThread) and a persisted assistant error (MessageItem), + * so this markup lives in one place. The detail is full-width (no hanging indent + * under the heading) so it wraps less and leaves no stranded icon / empty gap. + * The heading reuses Mantine's adaptive red "light" colour so it stays correct + * in dark mode. Layout-only props (mb/mt/...) are forwarded to the Alert root. + */ +interface ChatErrorAlertProps extends Omit { + title: string; + detail: string; +} + +export default function ChatErrorAlert({ + title, + detail, + ...alertProps +}: ChatErrorAlertProps) { + // Mantine's own "light" alert colour, adaptive across light/dark schemes. + const accent = "var(--mantine-color-red-light-color)"; + return ( + + + + + {title} + + + + {detail} + + + ); +} diff --git a/apps/client/src/features/ai-chat/components/chat-thread.tsx b/apps/client/src/features/ai-chat/components/chat-thread.tsx index 7243f2ad..f14cd732 100644 --- a/apps/client/src/features/ai-chat/components/chat-thread.tsx +++ b/apps/client/src/features/ai-chat/components/chat-thread.tsx @@ -1,13 +1,14 @@ import { useCallback, useMemo, useRef, useState } from "react"; import { generateId } from "ai"; -import { ActionIcon, Alert, Box, Group, Stack, Text } from "@mantine/core"; -import { IconAlertTriangle, IconClockHour4, IconX } from "@tabler/icons-react"; +import { ActionIcon, Box, Group, Stack, Text } from "@mantine/core"; +import { IconClockHour4, IconX } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; import { useChat, type UIMessage } from "@ai-sdk/react"; import { DefaultChatTransport } from "ai"; import MessageList from "@/features/ai-chat/components/message-list.tsx"; import ChatInput from "@/features/ai-chat/components/chat-input.tsx"; import RoleCards from "@/features/ai-chat/components/role-cards.tsx"; +import ChatErrorAlert from "@/features/ai-chat/components/chat-error-alert.tsx"; import { IAiChatMessageRow, IAiRole, @@ -277,15 +278,11 @@ export default function ChatThread({ /> {errorView && ( - } - mb="xs" + - {errorView.detail} - + detail={errorView.detail} + mb="xs" + /> )} diff --git a/apps/client/src/features/ai-chat/components/message-item.tsx b/apps/client/src/features/ai-chat/components/message-item.tsx index 67e69f3e..7071c2bb 100644 --- a/apps/client/src/features/ai-chat/components/message-item.tsx +++ b/apps/client/src/features/ai-chat/components/message-item.tsx @@ -1,8 +1,8 @@ -import { Alert, Box, Text } from "@mantine/core"; -import { IconAlertTriangle } from "@tabler/icons-react"; +import { Box, Text } from "@mantine/core"; import { useTranslation } from "react-i18next"; import type { UIMessage } from "@ai-sdk/react"; import ToolCallCard from "@/features/ai-chat/components/tool-call-card.tsx"; +import ChatErrorAlert from "@/features/ai-chat/components/chat-error-alert.tsx"; import { ToolUiPart, isToolPart } from "@/features/ai-chat/utils/tool-parts.tsx"; import { renderChatMarkdown } from "@/features/ai-chat/utils/markdown.ts"; import { resolveAssistantName } from "@/features/ai-chat/utils/assistant-name.ts"; @@ -118,15 +118,11 @@ export default function MessageItem({ // cause plus a one-line detail. const errorView = describeChatError(errorText, t); return ( - } - mt={4} + - {errorView.detail} - + detail={errorView.detail} + mt={4} + /> ); })()}