-
Notifications
You must be signed in to change notification settings - Fork 520
Expand file tree
/
Copy pathstr-replace.tsx
More file actions
113 lines (102 loc) · 3.12 KB
/
str-replace.tsx
File metadata and controls
113 lines (102 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { TextAttributes } from '@opentui/core'
import { DiffViewer } from './diff-viewer'
import { defineToolComponent } from './types'
import { useTheme } from '../../hooks/use-theme'
import type { ToolRenderConfig } from './types'
function extractValueForKey(output: string, key: string): string | null {
if (!output) return null
const lines = output.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const match = line.match(/^\s*([A-Za-z0-9_]+):\s*(.*)$/)
if (match && match[1] === key) {
const rest = match[2]
if (rest.trim().startsWith('|')) {
const baseIndent = lines[i + 1]?.match(/^\s*/)?.[0].length ?? 0
const acc: string[] = []
for (let j = i + 1; j < lines.length; j++) {
const l = lines[j]
const indent = l.match(/^\s*/)?.[0].length ?? 0
if (l.trim().length === 0) {
acc.push('')
continue
}
if (indent < baseIndent) break
acc.push(l.slice(baseIndent))
}
return acc.join('\n')
} else {
let val = rest.trim()
if (val.startsWith('"') && val.endsWith('"')) {
val = val.slice(1, -1)
}
return val
}
}
}
return null
}
interface EditHeaderProps {
name: string
filePath: string | null
}
const EditHeader = ({ name, filePath }: EditHeaderProps) => {
const theme = useTheme()
const bulletChar = '• '
return (
<box style={{ flexDirection: 'row', alignItems: 'center', width: '100%' }}>
<text style={{ wrapMode: 'word' }}>
<span fg={theme.foreground}>{bulletChar}</span>
<span fg={theme.foreground} attributes={TextAttributes.BOLD}>
{name}
</span>
{filePath ? <span fg={theme.foreground}>{` ${filePath}`}</span> : null}
</text>
</box>
)
}
interface EditBodyProps {
name: string
filePath: string | null
diffText: string
isCreate: boolean
}
const EditBody = ({ name, filePath, diffText, isCreate }: EditBodyProps) => {
return (
<box style={{ flexDirection: 'column', gap: 0, width: '100%' }}>
<EditHeader name={name} filePath={filePath} />
{!isCreate && (
<box style={{ paddingLeft: 2, width: '100%' }}>
<DiffViewer diffText={diffText} />
</box>
)}
</box>
)
}
export const StrReplaceComponent = defineToolComponent({
toolName: 'str_replace',
render(toolBlock): ToolRenderConfig {
const outputStr =
typeof toolBlock.output === 'string' ? toolBlock.output : ''
const diff =
extractValueForKey(outputStr, 'unifiedDiff') ||
extractValueForKey(outputStr, 'patch')
const filePath =
extractValueForKey(outputStr, 'file') ||
(typeof (toolBlock.input as any)?.path === 'string'
? (toolBlock.input as any).path
: null)
const message = extractValueForKey(outputStr, 'message')
const isCreate = message === 'Created new file'
return {
content: (
<EditBody
name={isCreate ? 'Create' : 'Edit'}
filePath={filePath}
diffText={diff ?? ''}
isCreate={isCreate}
/>
),
}
},
})