From d279adc6efd8c60c9ecaa4c33c4dc89bb93b2d6a Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Mon, 5 Oct 2026 01:53:53 -0400 Subject: [PATCH] fix(activity): keep code blocks in row previews from rendering as scroll boxes (#25410) The activity row previews the agent's last reply with the compact markdown renderer, which draws fenced code blocks and tables as their own scrollable boxes. Those boxes escaped the row's line clamp, so a reply with a code block put a tall grey box with scrollbars in the middle of the list. Row-scoped overrides now flatten code blocks and tables into plain wrapping text, so the existing clamp cuts them like any other line. Other markdown surfaces are unchanged. --- .../activity-thread-hover-card.test.tsx | 31 +++++++++++++++++++ .../activity/activity-thread-row.tsx | 5 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx b/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx index ef1f109d50a..d64f60c11a1 100644 --- a/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx +++ b/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx @@ -262,4 +262,35 @@ describe('ActivityThreadHoverCard and ActivityThreadRow', () => { expect(onJump).toHaveBeenCalledWith(thread) }) + + it('flattens code blocks and tables in the reply preview so the line clamp cuts them', async () => { + const thread = createTestThread({ + currentAgentState: null, + responsePreview: + 'KK1-START\n\n```\n| metric | base median |\n| --- | --- |\n| p2 | 12 |\n```\n\n| a | b |\n| - | - |\n| 1 | 2 |' + }) + + await act(async () => { + root.render() + }) + + const pre = container.querySelector('pre') + const table = container.querySelector('table') + expect(pre).not.toBeNull() + expect(table).not.toBeNull() + const preview = pre?.closest('.line-clamp-3') + expect(preview).not.toBeNull() + expect(preview?.contains(table ?? null)).toBe(true) + // Why: the compact renderers draw both as scroll boxes; the row must undo that. + const previewClasses = preview?.className.split(/\s+/) ?? [] + expect(previewClasses).toEqual( + expect.arrayContaining([ + '[&_pre]:!max-h-none', + '[&_pre]:!overflow-visible', + '[&_pre]:!whitespace-normal', + '[&_div]:!overflow-visible', + '[&_:is(table,thead,tbody,tr,th,td)]:!inline' + ]) + ) + }) }) diff --git a/src/renderer/src/components/activity/activity-thread-row.tsx b/src/renderer/src/components/activity/activity-thread-row.tsx index fef63bda46e..207ba0d6021 100644 --- a/src/renderer/src/components/activity/activity-thread-row.tsx +++ b/src/renderer/src/components/activity/activity-thread-row.tsx @@ -133,7 +133,10 @@ export const ActivityThreadRow = React.memo(function ActivityThreadRow({ className={cn( 'min-w-0 break-words text-[13px] leading-5 text-foreground/80', compactMode ? 'line-clamp-2' : 'line-clamp-3', - '[&_*]:!m-0 [&_*]:!p-0 [&_br]:hidden [&_ol]:list-none [&_ul]:list-none' + '[&_*]:!m-0 [&_*]:!p-0 [&_br]:hidden [&_ol]:list-none [&_ul]:list-none', + // Why: code blocks and tables otherwise render as scroll boxes the line clamp cannot cut. + '[&_pre]:!max-h-none [&_pre]:!overflow-visible [&_pre]:!whitespace-normal [&_pre]:!rounded-none [&_pre]:!bg-transparent [&_pre]:![font:inherit] [&_pre_code]:![font:inherit]', + '[&_div]:!overflow-visible [&_:is(table,thead,tbody,tr,th,td)]:!inline [&_:is(table,thead,tbody,tr,th,td)]:!align-baseline [&_:is(th,td)]:!border-0 [&_:is(th,td)]:!pr-2 [&_table]:![font:inherit]' )} /> ) : (