Skip to content

Commit 7b65f1b

Browse files
committed
refactor(cli): refactor blocks-renderer and agent-branch-wrapper
Refactor block rendering components to use zustand store for context. Add use-grid-layout hook and update implementor-helpers utilities.
1 parent 413ff1f commit 7b65f1b

File tree

7 files changed

+363
-193
lines changed

7 files changed

+363
-193
lines changed

cli/src/components/blocks/agent-branch-wrapper.tsx

Lines changed: 112 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { TextAttributes } from '@opentui/core'
2-
import React, { memo, useCallback, useMemo, type ReactNode } from 'react'
2+
import React, { memo, useCallback, useMemo, useRef, type ReactNode } from 'react'
33

44
import { AgentBlockGrid } from './agent-block-grid'
55
import { AgentBranchItem } from './agent-branch-item'
@@ -14,6 +14,7 @@ import { shouldRenderAsSimpleText } from '../../utils/constants'
1414
import { isImplementorAgent, getImplementorIndex } from '../../utils/implementor-helpers'
1515
import { processBlocks, type BlockProcessorHandlers } from '../../utils/block-processor'
1616
import { getAgentStatusInfo } from '../../utils/agent-helpers'
17+
import { extractHtmlBlockMargins } from '../../utils/block-margins'
1718
import { isTextBlock } from '../../types/chat'
1819
import type {
1920
AgentContentBlock,
@@ -36,6 +37,22 @@ interface AgentBodyProps {
3637
isLastMessage?: boolean
3738
}
3839

40+
/** Props stored in ref for stable handler access in AgentBody */
41+
interface AgentBodyPropsRef {
42+
keyPrefix: string
43+
nestedBlocks: ContentBlock[]
44+
parentIsStreaming: boolean
45+
availableWidth: number
46+
markdownPalette: MarkdownPalette
47+
streamingAgents: Set<string>
48+
onToggleCollapsed: (id: string) => void
49+
onBuildFast: () => void
50+
onBuildMax: () => void
51+
isLastMessage?: boolean
52+
theme: ReturnType<typeof useTheme>
53+
getAgentMarkdownOptions: (indent: number) => { codeBlockWidth: number; palette: MarkdownPalette }
54+
}
55+
3956
const AgentBody = memo(
4057
({
4158
agentBlock,
@@ -69,83 +86,114 @@ const AgentBody = memo(
6986
[availableWidth, markdownPalette, theme.foreground],
7087
)
7188

89+
// Store props in ref for stable handler access (avoids 12+ useMemo dependencies)
90+
const propsRef = useRef<AgentBodyPropsRef>(null!)
91+
propsRef.current = {
92+
keyPrefix,
93+
nestedBlocks,
94+
parentIsStreaming,
95+
availableWidth,
96+
markdownPalette,
97+
streamingAgents,
98+
onToggleCollapsed,
99+
onBuildFast,
100+
onBuildMax,
101+
isLastMessage,
102+
theme,
103+
getAgentMarkdownOptions,
104+
}
105+
106+
// Handlers are stable (empty deps) and read latest props from ref
72107
const handlers: BlockProcessorHandlers = useMemo(
73108
() => ({
74-
onReasoningGroup: (reasoningBlocks, startIndex) => (
75-
<ThinkingBlock
76-
key={reasoningBlocks[0]?.thinkingId ?? `${keyPrefix}-thinking-${startIndex}`}
77-
blocks={reasoningBlocks}
78-
onToggleCollapsed={onToggleCollapsed}
79-
availableWidth={availableWidth}
80-
isNested={true}
81-
/>
82-
),
109+
onReasoningGroup: (reasoningBlocks, startIndex) => {
110+
const p = propsRef.current
111+
return (
112+
<ThinkingBlock
113+
key={reasoningBlocks[0]?.thinkingId ?? `${p.keyPrefix}-thinking-${startIndex}`}
114+
blocks={reasoningBlocks}
115+
onToggleCollapsed={p.onToggleCollapsed}
116+
availableWidth={p.availableWidth}
117+
isNested={true}
118+
/>
119+
)
120+
},
83121

84-
onToolGroup: (toolBlocks, startIndex, nextIndex) => (
85-
<ToolBlockGroup
86-
key={`${keyPrefix}-tool-group-${startIndex}`}
87-
toolBlocks={toolBlocks}
88-
keyPrefix={keyPrefix}
89-
startIndex={startIndex}
90-
nextIndex={nextIndex}
91-
siblingBlocks={nestedBlocks}
92-
availableWidth={availableWidth}
93-
streamingAgents={streamingAgents}
94-
onToggleCollapsed={onToggleCollapsed}
95-
markdownPalette={markdownPalette}
96-
/>
97-
),
122+
onToolGroup: (toolBlocks, startIndex, nextIndex) => {
123+
const p = propsRef.current
124+
return (
125+
<ToolBlockGroup
126+
key={`${p.keyPrefix}-tool-group-${startIndex}`}
127+
toolBlocks={toolBlocks}
128+
keyPrefix={p.keyPrefix}
129+
startIndex={startIndex}
130+
nextIndex={nextIndex}
131+
siblingBlocks={p.nestedBlocks}
132+
availableWidth={p.availableWidth}
133+
streamingAgents={p.streamingAgents}
134+
onToggleCollapsed={p.onToggleCollapsed}
135+
markdownPalette={p.markdownPalette}
136+
/>
137+
)
138+
},
98139

99-
onImplementorGroup: (implementors, startIndex) => (
100-
<ImplementorGroup
101-
key={`${keyPrefix}-implementor-group-${startIndex}`}
102-
implementors={implementors}
103-
siblingBlocks={nestedBlocks}
104-
availableWidth={availableWidth}
105-
/>
106-
),
140+
onImplementorGroup: (implementors, startIndex) => {
141+
const p = propsRef.current
142+
return (
143+
<ImplementorGroup
144+
key={`${p.keyPrefix}-implementor-group-${startIndex}`}
145+
implementors={implementors}
146+
siblingBlocks={p.nestedBlocks}
147+
availableWidth={p.availableWidth}
148+
/>
149+
)
150+
},
107151

108-
onAgentGroup: (agentBlocks, startIndex) => (
109-
<AgentBlockGrid
110-
key={`${keyPrefix}-agent-grid-${startIndex}`}
111-
agentBlocks={agentBlocks}
112-
keyPrefix={`${keyPrefix}-agent-grid-${startIndex}`}
113-
availableWidth={availableWidth}
114-
streamingAgents={streamingAgents}
115-
renderAgentBranch={(innerAgentBlock, prefix, width) => (
116-
<AgentBranchWrapper
117-
agentBlock={innerAgentBlock}
118-
keyPrefix={prefix}
119-
availableWidth={width}
120-
markdownPalette={markdownPalette}
121-
streamingAgents={streamingAgents}
122-
onToggleCollapsed={onToggleCollapsed}
123-
onBuildFast={onBuildFast}
124-
onBuildMax={onBuildMax}
125-
siblingBlocks={nestedBlocks}
126-
isLastMessage={isLastMessage}
127-
/>
128-
)}
129-
/>
130-
),
152+
onAgentGroup: (agentBlocks, startIndex) => {
153+
const p = propsRef.current
154+
return (
155+
<AgentBlockGrid
156+
key={`${p.keyPrefix}-agent-grid-${startIndex}`}
157+
agentBlocks={agentBlocks}
158+
keyPrefix={`${p.keyPrefix}-agent-grid-${startIndex}`}
159+
availableWidth={p.availableWidth}
160+
streamingAgents={p.streamingAgents}
161+
renderAgentBranch={(innerAgentBlock, prefix, width) => (
162+
<AgentBranchWrapper
163+
agentBlock={innerAgentBlock}
164+
keyPrefix={prefix}
165+
availableWidth={width}
166+
markdownPalette={p.markdownPalette}
167+
streamingAgents={p.streamingAgents}
168+
onToggleCollapsed={p.onToggleCollapsed}
169+
onBuildFast={p.onBuildFast}
170+
onBuildMax={p.onBuildMax}
171+
siblingBlocks={p.nestedBlocks}
172+
isLastMessage={p.isLastMessage}
173+
/>
174+
)}
175+
/>
176+
)
177+
},
131178

132179
onSingleBlock: (block, index) => {
180+
const p = propsRef.current
133181
if (block.type === 'text') {
134182
const textBlock = block as TextContentBlock
135183
const nestedStatus = textBlock.status
136-
const isNestedStreamingText = parentIsStreaming || nestedStatus === 'running'
184+
const isNestedStreamingText = p.parentIsStreaming || nestedStatus === 'running'
137185
const filteredNestedContent = isNestedStreamingText
138186
? trimTrailingNewlines(textBlock.content)
139187
: textBlock.content.trim()
140-
const markdownOptionsForLevel = getAgentMarkdownOptions(0)
188+
const markdownOptionsForLevel = p.getAgentMarkdownOptions(0)
141189
const marginTop = textBlock.marginTop ?? 0
142190
const marginBottom = textBlock.marginBottom ?? 0
143191
const explicitColor = textBlock.color
144-
const nestedTextColor = explicitColor ?? theme.foreground
192+
const nestedTextColor = explicitColor ?? p.theme.foreground
145193

146194
return (
147195
<text
148-
key={`${keyPrefix}-text-${index}`}
196+
key={`${p.keyPrefix}-text-${index}`}
149197
style={{
150198
wrapMode: 'word',
151199
fg: nestedTextColor,
@@ -165,12 +213,11 @@ const AgentBody = memo(
165213

166214
if (block.type === 'html') {
167215
const htmlBlock = block as HtmlContentBlock
168-
const marginTop = htmlBlock.marginTop ?? 0
169-
const marginBottom = htmlBlock.marginBottom ?? 0
216+
const { marginTop, marginBottom } = extractHtmlBlockMargins(htmlBlock)
170217

171218
return (
172219
<box
173-
key={`${keyPrefix}-html-${index}`}
220+
key={`${p.keyPrefix}-html-${index}`}
174221
style={{
175222
flexDirection: 'column',
176223
gap: 0,
@@ -179,8 +226,8 @@ const AgentBody = memo(
179226
}}
180227
>
181228
{htmlBlock.render({
182-
textColor: theme.foreground,
183-
theme,
229+
textColor: p.theme.foreground,
230+
theme: p.theme,
184231
})}
185232
</box>
186233
)
@@ -190,20 +237,7 @@ const AgentBody = memo(
190237
return null
191238
},
192239
}),
193-
[
194-
keyPrefix,
195-
nestedBlocks,
196-
parentIsStreaming,
197-
availableWidth,
198-
markdownPalette,
199-
streamingAgents,
200-
onToggleCollapsed,
201-
onBuildFast,
202-
onBuildMax,
203-
isLastMessage,
204-
theme,
205-
getAgentMarkdownOptions,
206-
],
240+
[], // Empty deps - handlers read from propsRef.current
207241
)
208242

209243
return processBlocks(nestedBlocks, handlers) as ReactNode[]

0 commit comments

Comments
 (0)