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