From 8609ab1adbbe1fbc5fae93b61a611297a3642b27 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 15 May 2026 21:44:23 +0200 Subject: [PATCH] feat(frontend): add fade+pill truncation for long chat messages Long chat messages in BeautifiedJsonView are now clamped to 160px with a bottom fade gradient and a centered "Show more" pill. Clicking the pill expands to full height and shows a "Show less" pill at the bottom. Short messages render unchanged. - Measurement ref is separate from the clamped container so ResizeObserver reads unconstrained scrollHeight - Explicit maxHeight sentinel when expanded so the CSS transition animates in both directions - aria-expanded on toggle buttons, aria-hidden on fade overlay - Expanded state resets when text content changes --- .../DrillInView/BeautifiedJsonView.tsx | 128 ++++++++++++++---- 1 file changed, 103 insertions(+), 25 deletions(-) diff --git a/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx b/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx index a4b11ed5f0..dc8dcb9281 100644 --- a/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx +++ b/web/oss/src/components/DrillInView/BeautifiedJsonView.tsx @@ -467,6 +467,108 @@ const NodeRow = memo(function NodeRow({ ) }) +// ── TruncatedMessageBody ─────────────────────────────────────────────── +// +// Wraps the editor for a chat message. When the content exceeds +// TRUNCATE_HEIGHT_PX the body is clamped with a bottom fade overlay and +// a centered "Show more" pill. Clicking the pill expands to full height +// and shows a "Show less" pill at the bottom. + +const TRUNCATE_HEIGHT_PX = 160 +const EXPAND_SENTINEL_PX = 9999 + +const PILL_BUTTON_CLASSES = + "text-[11px] font-medium text-[var(--ant-color-text-secondary)] bg-[var(--ant-color-fill-quaternary)] hover:bg-[var(--ant-color-fill-tertiary)] border border-solid border-[var(--ant-color-border-secondary)] rounded-full px-3 py-0.5 cursor-pointer focus-visible:ring-1 focus-visible:ring-[var(--ant-color-primary)] focus-visible:outline-none motion-safe:transition-colors" + +const TruncatedMessageBody = memo(function TruncatedMessageBody({ + editorId, + text, +}: { + editorId: string + text: string +}) { + const measureRef = useRef(null) + const [needsTruncation, setNeedsTruncation] = useState(false) + const [expanded, setExpanded] = useState(false) + + useEffect(() => { + setExpanded(false) + const el = measureRef.current + if (!el) return + const check = () => setNeedsTruncation(el.scrollHeight > TRUNCATE_HEIGHT_PX + 8) + check() + const observer = new ResizeObserver(check) + observer.observe(el) + return () => observer.disconnect() + }, [text]) + + const toggle = useCallback(() => setExpanded((v) => !v), []) + + const isTruncated = needsTruncation && !expanded + + return ( +
+
+
+ + + + +
+
+ + {isTruncated ? ( +
+ + ) : null} + + {needsTruncation && expanded ? ( +
+ +
+ ) : null} +
+ ) +}) + // ── MessageNodeRow ────────────────────────────────────────────────────── const MessageNodeRow = memo(function MessageNodeRow({ @@ -488,30 +590,6 @@ const MessageNodeRow = memo(function MessageNodeRow({ [msg.role, roleColor], ) - const body = useMemo( - () => ( - - - - - ), - [editorId, text], - ) - return ( } /> ) })