/ src / components / permissions / FileWritePermissionRequest / FileWriteToolDiff.tsx
FileWriteToolDiff.tsx
 1  import * as React from 'react'
 2  import { existsSync, readFileSync } from 'fs'
 3  import { useMemo } from 'react'
 4  import { StructuredDiff } from '../../StructuredDiff.js'
 5  import { Box, Text } from 'ink'
 6  import { getTheme } from '../../../utils/theme.js'
 7  import { intersperse } from '../../../utils/array.js'
 8  import { getCwd } from '../../../utils/state.js'
 9  import { extname, relative } from 'path'
10  import { detectFileEncoding } from '../../../utils/file.js'
11  import { HighlightedCode } from '../../HighlightedCode.js'
12  import { getPatch } from '../../../utils/diff.js'
13  
14  type Props = {
15    file_path: string
16    content: string
17    verbose: boolean
18    width: number
19  }
20  
21  export function FileWriteToolDiff({
22    file_path,
23    content,
24    verbose,
25    width,
26  }: Props): React.ReactNode {
27    const fileExists = useMemo(() => existsSync(file_path), [file_path])
28    const oldContent = useMemo(() => {
29      if (!fileExists) {
30        return ''
31      }
32      const enc = detectFileEncoding(file_path)
33      return readFileSync(file_path, enc)
34    }, [file_path, fileExists])
35    const hunks = useMemo(() => {
36      if (!fileExists) {
37        return null
38      }
39      return getPatch({
40        filePath: file_path,
41        fileContents: oldContent,
42        oldStr: oldContent,
43        newStr: content,
44      })
45    }, [fileExists, file_path, oldContent, content])
46  
47    return (
48      <Box
49        borderColor={getTheme().secondaryBorder}
50        borderStyle="round"
51        flexDirection="column"
52        paddingX={1}
53      >
54        <Box paddingBottom={1}>
55          <Text bold>{verbose ? file_path : relative(getCwd(), file_path)}</Text>
56        </Box>
57        {hunks ? (
58          intersperse(
59            hunks.map(_ => (
60              <StructuredDiff
61                key={_.newStart}
62                patch={_}
63                dim={false}
64                width={width}
65              />
66            )),
67            i => (
68              <Text color={getTheme().secondaryText} key={`ellipsis-${i}`}>
69                ...
70              </Text>
71            ),
72          )
73        ) : (
74          <HighlightedCode
75            code={content || '(No content)'}
76            language={extname(file_path).slice(1)}
77          />
78        )}
79      </Box>
80    )
81  }