feat: add collapsible user message setting

This commit is contained in:
Bohdan Triapitsyn
2026-06-11 01:26:50 +03:00
parent a6571aa8b7
commit e36085d898
16 changed files with 100 additions and 11 deletions
@@ -8,6 +8,7 @@ import { useSkillsStore } from '@/stores/useSkillsStore';
import { Icon } from "@/components/icon/Icon";
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
import { getDirectoryForFilePath } from '@/lib/path-utils';
import { useI18n } from '@/lib/i18n';
import {
buildAgentHref,
buildAgentMentionUrl,
@@ -57,9 +58,11 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
const [isExpanded, setIsExpanded] = React.useState(false);
const [isTruncated, setIsTruncated] = React.useState(false);
const userMessageRenderingMode = useUIStore((state) => state.userMessageRenderingMode);
const collapsibleUserMessages = useUIStore((state) => state.collapsibleUserMessages);
const skills = useSkillsStore((state) => state.skills);
const openContextFile = useUIStore((state) => state.openContextFile);
const effectiveDirectory = useEffectiveDirectory();
const { t } = useI18n();
const normalizedRenderingMode = normalizeUserMessageRenderingMode(userMessageRenderingMode);
const textRef = React.useRef<HTMLDivElement>(null);
const skillByName = React.useMemo(() => new Map(skills.map((skill) => [skill.name, skill])), [skills]);
@@ -89,7 +92,7 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
if (!el) return;
const checkTruncation = () => {
if (!isExpanded) {
if (collapsibleUserMessages && !isExpanded) {
setIsTruncated(el.scrollHeight > el.clientHeight);
}
};
@@ -100,7 +103,14 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
resizeObserver.observe(el);
return () => resizeObserver.disconnect();
}, [textContent, isExpanded]);
}, [collapsibleUserMessages, textContent, isExpanded]);
React.useEffect(() => {
if (!collapsibleUserMessages) {
setIsExpanded(false);
setIsTruncated(false);
}
}, [collapsibleUserMessages]);
const handleClick = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const target = event.target as HTMLElement | null;
@@ -123,10 +133,10 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
return;
}
if (!isExpanded && isTruncated) {
if (collapsibleUserMessages && !isExpanded && isTruncated) {
setIsExpanded(true);
}
}, [hasActiveSelectionInElement, isExpanded, isTruncated, openSkill]);
}, [collapsibleUserMessages, hasActiveSelectionInElement, isExpanded, isTruncated, openSkill]);
const handleCollapse = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
@@ -222,12 +232,12 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
return (
<div className="relative" key={part.id || `${messageId}-user-text`}>
{isExpanded && (
{collapsibleUserMessages && isExpanded && (
<button
type="button"
onClick={handleCollapse}
className="absolute top-0 right-0 z-10 flex items-center justify-center rounded-sm bg-[var(--surface-elevated)] p-0.5 text-[var(--surface-mutedForeground)] hover:text-[var(--surface-foreground)] hover:bg-[var(--interactive-hover)] transition-colors"
aria-label="Collapse"
aria-label={t('chat.message.userText.collapseAria')}
>
<Icon name="arrow-up-s" className="h-3.5 w-3.5" />
</button>
@@ -237,8 +247,8 @@ const UserTextPart: React.FC<UserTextPartProps> = ({ part, messageId, agentMenti
"break-words font-sans typography-markdown-body",
isExpanded && "pb-3",
normalizedRenderingMode === 'plain' && 'whitespace-pre-wrap',
!isExpanded && "line-clamp-2",
isTruncated && !isExpanded && "cursor-pointer"
collapsibleUserMessages && !isExpanded && "line-clamp-2",
collapsibleUserMessages && isTruncated && !isExpanded && "cursor-pointer"
)}
ref={textRef}
onClick={handleClick}