From ee65c62d621d8c3ca6d71567ca9754595d6124d8 Mon Sep 17 00:00:00 2001 From: 7418 <7418@openclaw.ai> Date: Mon, 9 Feb 2026 17:33:49 +0800 Subject: [PATCH] feat: collapse long user messages with expand/collapse toggle (#37) User messages exceeding 300px are now collapsed by default with a gradient fade overlay and expand/collapse button. Only applies to user messages; assistant messages remain fully visible. Co-Authored-By: Claude Opus 4.6 --- src/components/chat/MessageItem.tsx | 53 +++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/components/chat/MessageItem.tsx b/src/components/chat/MessageItem.tsx index 1e4de98..08de3ea 100644 --- a/src/components/chat/MessageItem.tsx +++ b/src/components/chat/MessageItem.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useCallback } from 'react'; +import { useState, useCallback, useRef, useEffect } from 'react'; import type { Message, TokenUsage, FileAttachment } from '@/types'; import { Message as AIMessage, @@ -14,7 +14,7 @@ import { ToolInput, ToolOutput, } from '@/components/ai-elements/tool'; -import { CopyIcon, CheckIcon } from 'lucide-react'; +import { CopyIcon, CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react'; import type { ToolUIPart } from 'ai'; import { FileAttachmentDisplay } from './FileAttachmentDisplay'; @@ -214,6 +214,8 @@ function TokenUsageDisplay({ usage }: { usage: TokenUsage }) { ); } +const COLLAPSE_HEIGHT = 300; + export function MessageItem({ message }: MessageItemProps) { const isUser = message.role === 'user'; const { text, tools } = parseToolBlocks(message.content); @@ -226,6 +228,17 @@ export function MessageItem({ message }: MessageItemProps) { const displayText = isUser ? textWithoutFiles : text; + // Collapse/expand state for long user messages + const [isExpanded, setIsExpanded] = useState(false); + const [isOverflowing, setIsOverflowing] = useState(false); + const contentRef = useRef(null); + + useEffect(() => { + if (isUser && contentRef.current) { + setIsOverflowing(contentRef.current.scrollHeight > COLLAPSE_HEIGHT); + } + }, [isUser, displayText]); + let tokenUsage: TokenUsage | null = null; if (message.token_usage) { try { @@ -273,7 +286,41 @@ export function MessageItem({ message }: MessageItemProps) { {/* Text content */} {displayText && ( isUser ? ( -
{displayText}
+
+
+ {displayText} +
+ {isOverflowing && !isExpanded && ( +
+ )} + {isOverflowing && ( + + )} +
) : ( {displayText} )