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]'
)}
/>
) : (