Skip to content

Commit b651b46

Browse files
committed
refactor(cli): extract message-block internals and wire up parallel agent display
1 parent 4a3692b commit b651b46

File tree

5 files changed

+241
-1154
lines changed

5 files changed

+241
-1154
lines changed

cli/src/components/blocks/implementor-row.tsx

Lines changed: 34 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import {
1212
type FileStats,
1313
} from '../../utils/implementor-helpers'
1414
import { useTheme } from '../../hooks/use-theme'
15-
import { useTerminalLayout } from '../../hooks/use-terminal-layout'
16-
import { computeSmartColumns } from '../../utils/layout-helpers'
15+
import { useGridLayout } from '../../hooks/use-grid-layout'
1716
import { getRelativePath } from '../../utils/path-helpers'
1817
import { PROPOSAL_BORDER_CHARS } from '../../utils/ui-constants'
1918
import { Button } from '../button'
@@ -24,50 +23,17 @@ import type { AgentContentBlock, ContentBlock } from '../../types/chat'
2423
interface ImplementorGroupProps {
2524
implementors: AgentContentBlock[]
2625
siblingBlocks: ContentBlock[]
27-
onToggleCollapsed: (id: string) => void
2826
availableWidth: number
2927
}
3028

31-
/**
32-
* Responsive card grid for comparing implementor proposals
33-
*/
3429
export const ImplementorGroup = memo(
3530
({
3631
implementors,
3732
siblingBlocks,
3833
availableWidth,
3934
}: ImplementorGroupProps) => {
4035
const theme = useTheme()
41-
const { width } = useTerminalLayout()
42-
43-
// Determine max columns based on terminal width
44-
const maxColumns = useMemo(() => {
45-
if (width.is('xs')) return 1
46-
if (width.is('sm')) return 1
47-
if (width.is('md')) return 2
48-
return 3 // lg
49-
}, [width])
50-
51-
// Smart column selection based on item count
52-
const columns = useMemo(() =>
53-
computeSmartColumns(implementors.length, maxColumns),
54-
[implementors.length, maxColumns])
55-
56-
// Calculate card width based on columns and available space
57-
const cardWidth = useMemo(() => {
58-
// No gap between columns - cards are flush
59-
return Math.floor(availableWidth / columns)
60-
}, [availableWidth, columns])
61-
62-
// Masonry layout: distribute items to columns round-robin style
63-
// (simpler than height-based, but still gives masonry effect)
64-
const columnGroups = useMemo(() => {
65-
const result: AgentContentBlock[][] = Array.from({ length: columns }, () => [])
66-
implementors.forEach((impl, idx) => {
67-
result[idx % columns].push(impl)
68-
})
69-
return result
70-
}, [implementors, columns])
36+
const { columns, columnWidth: cardWidth, columnGroups } = useGridLayout(implementors, availableWidth)
7137

7238
// Check if any implementors are still running
7339
const anyRunning = implementors.some(impl => impl.status === 'running')
@@ -84,52 +50,55 @@ export const ImplementorGroup = memo(
8450
marginTop: 1,
8551
}}
8652
>
87-
<text
88-
fg={theme.muted}
89-
attributes={TextAttributes.DIM}
90-
>
91-
{headerText}
92-
</text>
93-
9453
{/* Masonry layout: columns side by side, cards stack vertically in each */}
9554
<box
9655
style={{
9756
flexDirection: 'row',
98-
gap: 1, // Small horizontal gap to balance visual weight with vertical double-borders
57+
gap: 1,
9958
width: '100%',
10059
alignItems: 'flex-start',
10160
}}
10261
>
103-
{columnGroups.map((columnItems, colIdx) => (
104-
<box
105-
key={`col-${colIdx}`}
62+
{columnGroups.map((columnItems, colIdx) => {
63+
// Use first agent's ID as stable column key
64+
const columnKey = columnItems[0]?.agentId ?? `col-${colIdx}`
65+
return (
66+
<box
67+
key={columnKey}
10668
style={{
10769
flexDirection: 'column',
10870
gap: 0,
10971
flexGrow: 1,
11072
flexShrink: 1,
11173
flexBasis: 0,
112-
minWidth: 0, // Allow shrinking below content size
74+
minWidth: 0,
11375
}}
11476
>
115-
{columnItems.map((agentBlock) => {
116-
const implementorIndex = getImplementorIndex(
117-
agentBlock,
118-
siblingBlocks,
119-
)
120-
121-
return (
122-
<ImplementorCard
123-
key={agentBlock.agentId}
124-
agentBlock={agentBlock}
125-
implementorIndex={implementorIndex}
126-
cardWidth={cardWidth}
127-
/>
128-
)
129-
})}
130-
</box>
131-
))}
77+
{columnItems.map((agentBlock) => {
78+
const implementorIndex = getImplementorIndex(
79+
agentBlock,
80+
siblingBlocks,
81+
)
82+
83+
return (
84+
<ImplementorCard
85+
key={agentBlock.agentId}
86+
agentBlock={agentBlock}
87+
implementorIndex={implementorIndex}
88+
cardWidth={cardWidth}
89+
/>
90+
)
91+
})}
92+
</box>
93+
)
94+
})}
13295
</box>
96+
<text
97+
fg={theme.muted}
98+
attributes={TextAttributes.DIM}
99+
>
100+
{headerText}
101+
</text>
133102
</box>
134103
)
135104
},
@@ -141,10 +110,6 @@ interface ImplementorCardProps {
141110
cardWidth: number
142111
}
143112

144-
/**
145-
* Individual proposal card with dashed border
146-
* Click file rows to view their diffs
147-
*/
148113
const ImplementorCard = memo(
149114
({
150115
agentBlock,
@@ -274,10 +239,6 @@ const ImplementorCard = memo(
274239
},
275240
)
276241

277-
// ============================================================================
278-
// COMPACT FILE STATS VIEW
279-
// ============================================================================
280-
281242
interface CompactFileStatsProps {
282243
fileStats: FileStats[]
283244
availableWidth: number
@@ -287,12 +248,6 @@ interface CompactFileStatsProps {
287248
fileDiffs: Map<string, string>
288249
}
289250

290-
/**
291-
* Compact view showing file changes with full-width, center-aligned addition/deletion bars.
292-
* The left side is a green bar (additions) and the right side is a red bar (deletions),
293-
* both extending to the center with their +N / -N counts rendered in white inside the bars.
294-
* Click a file name to view its diff inline below that row.
295-
*/
296251
const CompactFileStats = memo(({
297252
fileStats,
298253
availableWidth,
@@ -354,10 +309,6 @@ interface CompactFileRowProps {
354309
diff?: string
355310
}
356311

357-
/**
358-
* Single file row with full-width colored bars meeting at center.
359-
* File name is underlined on hover, clickable to show diff inline below.
360-
*/
361312
const CompactFileRow = memo(({
362313
file,
363314
availableWidth,

0 commit comments

Comments
 (0)