Skip to content

Commit 8dcfcb1

Browse files
authored
fix(code): collapse long queued messages instead of filling the viewport (#3223)
1 parent 7ece0f0 commit 8dcfcb1

3 files changed

Lines changed: 93 additions & 53 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { CaretDown, CaretUp } from "@phosphor-icons/react";
2+
import { Box } from "@radix-ui/themes";
3+
import {
4+
type CSSProperties,
5+
type ReactNode,
6+
useEffect,
7+
useRef,
8+
useState,
9+
} from "react";
10+
11+
const COLLAPSED_MAX_HEIGHT = 160;
12+
13+
interface CollapsibleMessageContentProps {
14+
children: ReactNode;
15+
className?: string;
16+
/** Extra classes for the inner content box (e.g. per-caller typography). */
17+
contentClassName?: string;
18+
/** Color the bottom fade blends into — match the caller's background. */
19+
fadeColor?: string;
20+
style?: CSSProperties;
21+
}
22+
23+
export function CollapsibleMessageContent({
24+
children,
25+
className,
26+
contentClassName,
27+
fadeColor = "var(--gray-2)",
28+
style,
29+
}: CollapsibleMessageContentProps) {
30+
const [isExpanded, setIsExpanded] = useState(false);
31+
const [isOverflowing, setIsOverflowing] = useState(false);
32+
const contentRef = useRef<HTMLDivElement>(null);
33+
34+
useEffect(() => {
35+
const el = contentRef.current;
36+
if (el) {
37+
setIsOverflowing(el.scrollHeight > COLLAPSED_MAX_HEIGHT);
38+
}
39+
}, []);
40+
41+
return (
42+
<Box className={className} style={style}>
43+
<Box
44+
ref={contentRef}
45+
className={`relative overflow-hidden font-medium text-[13px] [&>*:last-child]:mb-0 ${contentClassName ?? ""}`}
46+
style={
47+
!isExpanded && isOverflowing
48+
? { maxHeight: COLLAPSED_MAX_HEIGHT }
49+
: undefined
50+
}
51+
>
52+
{children}
53+
{!isExpanded && isOverflowing && (
54+
<Box
55+
className="pointer-events-none absolute inset-x-0 bottom-0 h-12"
56+
style={{
57+
background: `linear-gradient(transparent, ${fadeColor})`,
58+
}}
59+
/>
60+
)}
61+
</Box>
62+
{isOverflowing && (
63+
<button
64+
type="button"
65+
onClick={() => setIsExpanded((prev) => !prev)}
66+
className="mt-1 inline-flex items-center gap-1 text-[12px] text-accent-11 hover:text-accent-12"
67+
>
68+
{isExpanded ? (
69+
<>
70+
<CaretUp size={12} />
71+
Show less
72+
</>
73+
) : (
74+
<>
75+
<CaretDown size={12} />
76+
Show more
77+
</>
78+
)}
79+
</button>
80+
)}
81+
</Box>
82+
);
83+
}

packages/ui/src/features/sessions/components/session-update/QueuedMessageView.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Button } from "@posthog/quill";
88
import { Box, Flex, IconButton, Tooltip } from "@radix-ui/themes";
99
import { MarkdownRenderer } from "../../../editor/components/MarkdownRenderer";
1010
import type { QueuedMessage } from "../../sessionStore";
11+
import { CollapsibleMessageContent } from "./CollapsibleMessageContent";
1112
import { hasFileMentions, parseFileMentions } from "./parseFileMentions";
1213

1314
interface QueuedMessageViewProps {
@@ -33,13 +34,17 @@ export function QueuedMessageView({
3334
<Box className="rounded-lg border border-gray-5 bg-card px-3 py-2">
3435
<Flex align="center" gap="2">
3536
<Stack size={14} className="shrink-0 text-gray-9" />
36-
<Box className="min-w-0 flex-1 font-medium text-[13px] text-gray-12 [&>*:last-child]:mb-0">
37+
<CollapsibleMessageContent
38+
className="min-w-0 flex-1"
39+
contentClassName="text-gray-12"
40+
fadeColor="var(--card)"
41+
>
3742
{hasFileMentions(message.content) ? (
3843
parseFileMentions(message.content)
3944
) : (
4045
<MarkdownRenderer content={message.content} />
4146
)}
42-
</Box>
47+
</CollapsibleMessageContent>
4348
<Flex align="center" gap="1" className="shrink-0">
4449
{onSteer && (
4550
<Tooltip content={steerTooltip}>

packages/ui/src/features/sessions/components/session-update/UserMessage.tsx

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import {
2-
CaretDown,
3-
CaretUp,
42
Check,
53
Copy,
64
File,
@@ -17,6 +15,7 @@ import { MarkdownRenderer } from "../../../editor/components/MarkdownRenderer";
1715
import { useFeatureFlag } from "../../../feature-flags/useFeatureFlag";
1816
import { usePanelLayoutStore } from "../../../panels/panelLayoutStore";
1917
import type { UserMessageAttachment } from "../../userMessageTypes";
18+
import { CollapsibleMessageContent } from "./CollapsibleMessageContent";
2019
import { extractCanvasInstructions } from "./canvasInstructions";
2120
import { extractChannelContext } from "./channelContext";
2221
import { extractCustomInstructions } from "./customInstructions";
@@ -26,8 +25,6 @@ import {
2625
parseFileMentions,
2726
} from "./parseFileMentions";
2827

29-
const COLLAPSED_MAX_HEIGHT = 160;
30-
3128
interface UserMessageProps {
3229
content: string;
3330
timestamp?: number;
@@ -108,16 +105,6 @@ export const UserMessage = memo(function UserMessage({
108105
const containsFileMentions = hasFileMentions(displayContent);
109106
const showAttachmentChips = attachments.length > 0 && !containsFileMentions;
110107
const [copied, setCopied] = useState(false);
111-
const [isExpanded, setIsExpanded] = useState(false);
112-
const [isOverflowing, setIsOverflowing] = useState(false);
113-
const contentRef = useRef<HTMLDivElement>(null);
114-
115-
useEffect(() => {
116-
const el = contentRef.current;
117-
if (el) {
118-
setIsOverflowing(el.scrollHeight > COLLAPSED_MAX_HEIGHT);
119-
}
120-
}, []);
121108

122109
const copiedTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
123110

@@ -142,15 +129,7 @@ export const UserMessage = memo(function UserMessage({
142129
className="group/msg relative border-l-2 bg-gray-2 py-2 pl-3"
143130
style={{ borderColor: "var(--accent-9)" }}
144131
>
145-
<Box
146-
ref={contentRef}
147-
className="relative overflow-hidden font-medium text-[13px] [&>*:last-child]:mb-0 [&_p]:leading-[1.9]"
148-
style={
149-
!isExpanded && isOverflowing
150-
? { maxHeight: COLLAPSED_MAX_HEIGHT }
151-
: undefined
152-
}
153-
>
132+
<CollapsibleMessageContent contentClassName="[&_p]:leading-[1.9]">
154133
{containsFileMentions ? (
155134
parseFileMentions(displayContent)
156135
) : (
@@ -212,34 +191,7 @@ export const UserMessage = memo(function UserMessage({
212191
))}
213192
</Flex>
214193
)}
215-
{!isExpanded && isOverflowing && (
216-
<Box
217-
className="pointer-events-none absolute inset-x-0 bottom-0 h-12"
218-
style={{
219-
background: "linear-gradient(transparent, var(--gray-2))",
220-
}}
221-
/>
222-
)}
223-
</Box>
224-
{isOverflowing && (
225-
<button
226-
type="button"
227-
onClick={() => setIsExpanded((prev) => !prev)}
228-
className="mt-1 inline-flex items-center gap-1 text-[12px] text-accent-11 hover:text-accent-12"
229-
>
230-
{isExpanded ? (
231-
<>
232-
<CaretUp size={12} />
233-
Show less
234-
</>
235-
) : (
236-
<>
237-
<CaretDown size={12} />
238-
Show more
239-
</>
240-
)}
241-
</button>
242-
)}
194+
</CollapsibleMessageContent>
243195
{sourceUrl && (
244196
<a
245197
href={sourceUrl}

0 commit comments

Comments
 (0)