Skip to content

Commit 286b381

Browse files
committed
Refine agent tool rendering
1 parent 8a875dd commit 286b381

File tree

9 files changed

+443
-330
lines changed

9 files changed

+443
-330
lines changed

cli/src/components/branch-item.tsx

Lines changed: 151 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { TextAttributes, type BorderCharacters } from '@opentui/core'
22
import React, { type ReactNode } from 'react'
33

4-
const borderCharsWithoutVertical: BorderCharacters = {
5-
topLeft: '',
6-
topRight: '',
7-
bottomLeft: '',
8-
bottomRight: '',
4+
const containerBorderChars: BorderCharacters = {
5+
topLeft: '',
6+
topRight: '',
7+
bottomLeft: '',
8+
bottomRight: '',
99
horizontal: '─',
10-
vertical: ' ',
11-
topT: ' ',
12-
bottomT: ' ',
13-
leftT: ' ',
14-
rightT: ' ',
15-
cross: ' ',
10+
vertical: '',
11+
topT: '',
12+
bottomT: '',
13+
leftT: '',
14+
rightT: '',
15+
cross: '',
1616
}
1717

1818
import type { ChatTheme } from '../utils/theme-system'
@@ -25,9 +25,12 @@ interface BranchItemProps {
2525
agentId?: string
2626
isCollapsed: boolean
2727
isStreaming: boolean
28-
branchChar: string
2928
streamingPreview: string
3029
finishedPreview: string
30+
availableWidth: number
31+
statusLabel?: string
32+
statusColor?: string
33+
statusIndicator?: string
3134
theme: ChatTheme
3235
onToggle: () => void
3336
}
@@ -39,13 +42,15 @@ export const BranchItem = ({
3942
agentId,
4043
isCollapsed,
4144
isStreaming,
42-
branchChar,
4345
streamingPreview,
4446
finishedPreview,
47+
availableWidth,
48+
statusLabel,
49+
statusColor,
50+
statusIndicator = '●',
4551
theme,
4652
onToggle,
4753
}: BranchItemProps) => {
48-
const cornerColor = theme.agentPrefix
4954
const isExpanded = !isCollapsed
5055
const toggleFrameColor = isExpanded
5156
? theme.agentToggleExpandedBg
@@ -57,6 +62,16 @@ export const BranchItem = ({
5762
const toggleLabel = `${isCollapsed ? '▸' : '▾'} `
5863
const collapseButtonFrame = theme.agentToggleExpandedBg
5964
const collapseButtonText = collapseButtonFrame
65+
const separatorColor = theme.agentResponseCount
66+
const innerContentWidth = Math.max(0, Math.floor(availableWidth) - 4)
67+
const horizontalLine =
68+
innerContentWidth > 0 ? '─'.repeat(innerContentWidth) : ''
69+
const statusText =
70+
statusLabel && statusLabel.length > 0
71+
? statusIndicator === '✓'
72+
? `${statusLabel} ${statusIndicator}`
73+
: `${statusIndicator} ${statusLabel}`
74+
: null
6075

6176
const isTextRenderable = (value: ReactNode): boolean => {
6277
if (value === null || value === undefined || typeof value === 'boolean') {
@@ -151,80 +166,142 @@ export const BranchItem = ({
151166
flexShrink: 0,
152167
marginTop: 1,
153168
marginBottom: 0,
169+
width: '100%',
154170
}}
155171
>
156-
<box style={{ flexDirection: 'column', gap: 0 }}>
157-
<RaisedPill
158-
segments={[
159-
{ text: toggleLabel, fg: toggleIconColor },
160-
{
161-
text: name,
162-
fg: toggleLabelColor,
163-
attr: isExpanded ? TextAttributes.BOLD : undefined,
164-
},
165-
]}
166-
frameColor={toggleFrameColor}
167-
textColor={toggleLabelColor}
168-
onPress={onToggle}
169-
style={{ alignSelf: 'flex-start' }}
170-
/>
171-
<box style={{ flexShrink: 1, marginBottom: 0 }}>
172-
{isStreaming && isCollapsed && streamingPreview && (
173-
<text
174-
key="streaming-preview"
175-
fg={theme.agentText}
176-
attributes={TextAttributes.ITALIC}
177-
>
178-
{streamingPreview}
172+
<box
173+
border
174+
borderStyle="single"
175+
borderColor={toggleFrameColor}
176+
customBorderChars={containerBorderChars}
177+
style={{
178+
flexDirection: 'column',
179+
gap: 0,
180+
paddingLeft: 0,
181+
paddingRight: 0,
182+
paddingTop: 0,
183+
paddingBottom: 0,
184+
width: '100%',
185+
}}
186+
>
187+
{prompt ? (
188+
<box
189+
style={{
190+
flexDirection: 'column',
191+
gap: 0,
192+
paddingLeft: 1,
193+
paddingRight: 1,
194+
paddingTop: 0,
195+
paddingBottom: 0,
196+
width: '100%',
197+
}}
198+
>
199+
<text fg={theme.agentToggleHeaderText}>Prompt</text>
200+
<text fg={theme.agentText} style={{ wrapMode: 'word' }}>
201+
{prompt}
179202
</text>
180-
)}
181-
{!isStreaming && isCollapsed && finishedPreview && (
182-
<text
183-
key="finished-preview"
184-
fg={theme.agentResponseCount}
185-
attributes={TextAttributes.ITALIC}
203+
</box>
204+
) : null}
205+
<box
206+
style={{
207+
flexDirection: 'row',
208+
alignItems: 'center',
209+
paddingLeft: 1,
210+
paddingRight: 1,
211+
paddingTop: 0,
212+
paddingBottom: 0,
213+
width: '100%',
214+
}}
215+
onMouseDown={onToggle}
216+
>
217+
<text style={{ wrapMode: 'none' }}>
218+
<span fg={toggleIconColor}>{toggleLabel}</span>
219+
<span
220+
fg={toggleLabelColor}
221+
attributes={isExpanded ? TextAttributes.BOLD : undefined}
186222
>
187-
{finishedPreview}
188-
</text>
189-
)}
190-
{!isCollapsed && (
191-
<box style={{ flexDirection: 'column', gap: 0 }}>
192-
{content && (
223+
{name}
224+
</span>
225+
{statusText ? (
226+
<span
227+
fg={statusColor ?? theme.agentResponseCount}
228+
attributes={TextAttributes.DIM}
229+
>
230+
{` ${statusText}`}
231+
</span>
232+
) : null}
233+
</text>
234+
</box>
235+
236+
{isCollapsed ? (
237+
(isStreaming && streamingPreview) || (!isStreaming && finishedPreview) ? (
238+
<box
239+
style={{
240+
paddingLeft: 1,
241+
paddingRight: 1,
242+
paddingTop: 0,
243+
paddingBottom: 1,
244+
}}
245+
>
246+
<text
247+
fg={isStreaming ? theme.agentText : theme.agentResponseCount}
248+
attributes={TextAttributes.ITALIC}
249+
>
250+
{isStreaming ? streamingPreview : finishedPreview}
251+
</text>
252+
</box>
253+
) : null
254+
) : (
255+
<>
256+
{horizontalLine && (
257+
<box style={{ paddingLeft: 1, paddingRight: 1 }}>
258+
<text style={{ wrapMode: 'none' }}>
259+
<span fg={separatorColor}>{horizontalLine}</span>
260+
</text>
261+
</box>
262+
)}
263+
<box
264+
style={{
265+
flexDirection: 'column',
266+
gap: 0,
267+
paddingLeft: 1,
268+
paddingRight: 1,
269+
paddingTop: 0,
270+
paddingBottom: 0,
271+
}}
272+
>
273+
{prompt && (
193274
<box
194-
border
195-
borderStyle="single"
196-
borderColor={cornerColor}
197-
customBorderChars={borderCharsWithoutVertical}
198275
style={{
199276
flexDirection: 'column',
200277
gap: 0,
201-
paddingLeft: 1,
202-
paddingRight: 1,
203-
paddingTop: 0,
204-
paddingBottom: 0,
278+
marginBottom: content ? 1 : 0,
205279
}}
206280
>
207-
{prompt && (
208-
<box style={{ flexDirection: 'column', gap: 0 }}>
209-
<text fg={theme.agentToggleHeaderText}>Prompt</text>
210-
<text fg={theme.agentText}>{prompt}</text>
211-
<text> </text>
212-
<text fg={theme.agentToggleHeaderText}>Response</text>
213-
</box>
281+
<text fg={theme.agentToggleHeaderText}>Prompt</text>
282+
<text fg={theme.agentText} style={{ wrapMode: 'word' }}>
283+
{prompt}
284+
</text>
285+
{content && (
286+
<text fg={theme.agentToggleHeaderText} style={{ marginTop: 1 }}>
287+
Response
288+
</text>
214289
)}
215-
{renderExpandedContent(content)}
216290
</box>
217291
)}
218-
<RaisedPill
219-
segments={[{ text: 'Collapse', fg: collapseButtonText }]}
220-
frameColor={collapseButtonFrame}
221-
textColor={collapseButtonText}
222-
onPress={onToggle}
223-
style={{ alignSelf: 'flex-end' }}
224-
/>
292+
{renderExpandedContent(content)}
293+
<box style={{ alignSelf: 'flex-end', marginTop: content ? 0 : 1 }}>
294+
<RaisedPill
295+
segments={[{ text: 'Collapse', fg: collapseButtonText }]}
296+
frameColor={collapseButtonFrame}
297+
textColor={collapseButtonText}
298+
padding={0}
299+
onPress={onToggle}
300+
/>
301+
</box>
225302
</box>
226-
)}
227-
</box>
303+
</>
304+
)}
228305
</box>
229306
</box>
230307
)

cli/src/components/login-modal-utils.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,6 @@
22
* Utility functions for the login screen component
33
*/
44

5-
/**
6-
* Calculates the relative luminance of a hex color to determine if it's light or dark mode
7-
*/
8-
export function isLightModeColor(hexColor: string): boolean {
9-
if (!hexColor) return false
10-
11-
const hex = hexColor.replace('#', '')
12-
const r = parseInt(hex.substring(0, 2), 16)
13-
const g = parseInt(hex.substring(2, 4), 16)
14-
const b = parseInt(hex.substring(4, 6), 16)
15-
16-
// Calculate relative luminance
17-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
18-
return luminance > 0.5
19-
}
20-
215
/**
226
* Formats a URL for display by wrapping it at logical breakpoints
237
*/

cli/src/components/login-modal.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
import {
2424
formatUrl,
2525
generateFingerprintId,
26-
isLightModeColor,
2726
parseLogoLines,
2827
calculateResponsiveLayout,
2928
calculateModalDimensions,
@@ -222,14 +221,7 @@ export const LoginModal = ({
222221
}
223222
}, [hasOpenedBrowser, loginUrl, copyToClipboard])
224223

225-
// Determine if we're in light mode by checking background color luminance
226-
const isLightMode = useMemo(
227-
() => isLightModeColor(theme.background),
228-
[theme.background],
229-
)
230-
231-
// Use pure black/white for logo
232-
const logoColor = isLightMode ? '#000000' : '#ffffff'
224+
const logoColor = theme.chromeText
233225

234226
// Use custom hook for sheen animation
235227
const { applySheenToChar } = useSheenAnimation({

0 commit comments

Comments
 (0)