mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
refactor: replace react-diff-view with @lobehub/ui CodeDiff (#12077)
* refactor: replace react-diff-view with @lobehub/ui CodeDiff
- Replace react-diff-view with @lobehub/ui's CodeDiff and PatchDiff components
- Use CodeDiff in Intervention for old/new content comparison
- Use PatchDiff in Render for unified diff patch display
- Remove react-diff-view dark theme CSS file
- Remove diff generation complexity, rely on built-in component styling
- Reduce code by ~93 lines
* 🔧 chore: add font-family style to previewText
This commit is contained in:
@@ -322,7 +322,6 @@
|
||||
"rc-util": "^5.44.4",
|
||||
"react": "^19.2.3",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-diff-view": "^3.3.2",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"react-hotkeys-hook": "^5.2.3",
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import { type EditLocalFileParams } from '@lobechat/electron-client-ipc';
|
||||
import { type BuiltinInterventionProps } from '@lobechat/types';
|
||||
import { Flexbox, Icon, Skeleton, Text } from '@lobehub/ui';
|
||||
import { createPatch } from 'diff';
|
||||
import { CodeDiff, Flexbox, Icon, Skeleton, Text } from '@lobehub/ui';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import path from 'path-browserify-esm';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { Diff, Hunk, parseDiff } from 'react-diff-view';
|
||||
import 'react-diff-view/style/index.css';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { LocalFile, LocalFolder } from '@/features/LocalFile';
|
||||
import { localFileService } from '@/services/electron/localFileService';
|
||||
import '@/styles/react-diff-view.dark.css';
|
||||
|
||||
const EditLocalFile = memo<BuiltinInterventionProps<EditLocalFileParams>>(({ args }) => {
|
||||
const { t } = useTranslation('tool');
|
||||
@@ -29,32 +24,17 @@ const EditLocalFile = memo<BuiltinInterventionProps<EditLocalFileParams>>(({ arg
|
||||
},
|
||||
);
|
||||
|
||||
const { resolvedTheme } = useTheme();
|
||||
const isDarkMode = resolvedTheme === 'dark';
|
||||
// Generate diff from full file content
|
||||
const files = useMemo(() => {
|
||||
if (!fileData?.content) return [];
|
||||
// Generate new content by applying the replacement
|
||||
const { oldContent, newContent } = useMemo(() => {
|
||||
if (!fileData?.content) return { newContent: '', oldContent: '' };
|
||||
|
||||
try {
|
||||
const oldContent = fileData.content;
|
||||
const oldContent = fileData.content;
|
||||
const newContent = args.replace_all
|
||||
? oldContent.replaceAll(args.old_string, args.new_string)
|
||||
: oldContent.replace(args.old_string, args.new_string);
|
||||
|
||||
// Generate new content by applying the replacement
|
||||
const newContent = args.replace_all
|
||||
? oldContent.replaceAll(args.old_string, args.new_string)
|
||||
: oldContent.replace(args.old_string, args.new_string);
|
||||
|
||||
// Use createPatch to generate unified diff with full file content
|
||||
const patch = createPatch(args.file_path, oldContent, newContent, '', '');
|
||||
|
||||
// Add git diff header for parseDiff compatibility
|
||||
const diffText = `diff --git a${args.file_path} b${args.file_path}\n${patch}`;
|
||||
|
||||
return parseDiff(diffText);
|
||||
} catch (error) {
|
||||
console.error('Failed to generate diff:', error);
|
||||
return [];
|
||||
}
|
||||
}, [fileData?.content, args.file_path, args.old_string, args.new_string, args.replace_all]);
|
||||
return { newContent, oldContent };
|
||||
}, [fileData?.content, args.old_string, args.new_string, args.replace_all]);
|
||||
|
||||
return (
|
||||
<Flexbox gap={12}>
|
||||
@@ -73,18 +53,16 @@ const EditLocalFile = memo<BuiltinInterventionProps<EditLocalFileParams>>(({ arg
|
||||
? t('localFiles.editFile.replaceAll')
|
||||
: t('localFiles.editFile.replaceFirst')}
|
||||
</Text>
|
||||
{files.map((file, index) => (
|
||||
<div key={`${file.oldPath}-${index}`} style={{ fontSize: '12px' }}>
|
||||
<Diff
|
||||
data-theme={isDarkMode ? 'dark' : 'light'}
|
||||
diffType={file.type}
|
||||
hunks={file.hunks}
|
||||
viewType="split"
|
||||
>
|
||||
{(hunks) => hunks.map((hunk) => <Hunk hunk={hunk} key={hunk.content} />)}
|
||||
</Diff>
|
||||
</div>
|
||||
))}
|
||||
{oldContent && (
|
||||
<CodeDiff
|
||||
fileName={args.file_path}
|
||||
newContent={newContent}
|
||||
oldContent={oldContent}
|
||||
showHeader={false}
|
||||
variant="borderless"
|
||||
viewMode="split"
|
||||
/>
|
||||
)}
|
||||
</Flexbox>
|
||||
)}
|
||||
</Flexbox>
|
||||
|
||||
@@ -1,31 +1,11 @@
|
||||
import { type EditLocalFileState } from '@lobechat/builtin-tool-local-system';
|
||||
import { type EditLocalFileParams } from '@lobechat/electron-client-ipc';
|
||||
import { type BuiltinRenderProps } from '@lobechat/types';
|
||||
import { Alert, Flexbox, Skeleton } from '@lobehub/ui';
|
||||
import { useTheme } from 'next-themes';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { Diff, Hunk, parseDiff } from 'react-diff-view';
|
||||
import 'react-diff-view/style/index.css';
|
||||
|
||||
import '@/styles/react-diff-view.dark.css';
|
||||
import { Alert, Flexbox, PatchDiff, Skeleton } from '@lobehub/ui';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
const EditLocalFile = memo<BuiltinRenderProps<EditLocalFileParams, EditLocalFileState>>(
|
||||
({ args, pluginState, pluginError }) => {
|
||||
// Parse diff for react-diff-view
|
||||
const files = useMemo(() => {
|
||||
const diffText = pluginState?.diffText;
|
||||
if (!diffText) return [];
|
||||
|
||||
try {
|
||||
return parseDiff(diffText);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse diff:', error);
|
||||
return [];
|
||||
}
|
||||
}, [pluginState?.diffText]);
|
||||
const { resolvedTheme } = useTheme();
|
||||
const isDarkMode = resolvedTheme === 'dark';
|
||||
|
||||
if (!args) return <Skeleton active />;
|
||||
|
||||
return (
|
||||
@@ -37,22 +17,15 @@ const EditLocalFile = memo<BuiltinRenderProps<EditLocalFileParams, EditLocalFile
|
||||
title="Edit Failed"
|
||||
type="error"
|
||||
/>
|
||||
) : (
|
||||
<Flexbox data-theme={isDarkMode ? 'dark' : 'light'} gap={12}>
|
||||
{files.map((file, index) => (
|
||||
<div key={`${file.oldPath}-${index}`} style={{ fontSize: '12px' }}>
|
||||
<Diff
|
||||
diffType={file.type}
|
||||
gutterType="default"
|
||||
hunks={file.hunks}
|
||||
viewType="unified"
|
||||
>
|
||||
{(hunks) => hunks.map((hunk) => <Hunk hunk={hunk} key={hunk.content} />)}
|
||||
</Diff>
|
||||
</div>
|
||||
))}
|
||||
</Flexbox>
|
||||
)}
|
||||
) : pluginState?.diffText ? (
|
||||
<PatchDiff
|
||||
fileName={args.file_path}
|
||||
patch={pluginState.diffText}
|
||||
showHeader={false}
|
||||
variant="borderless"
|
||||
viewMode="unified"
|
||||
/>
|
||||
) : null}
|
||||
</Flexbox>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -80,6 +80,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
previewText: css`
|
||||
overflow: auto;
|
||||
|
||||
font-family: ${cssVar.fontFamilyCode};
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/* react-diff-view doesn't ship a dark theme.
|
||||
* We override its CSS variables when the app is in dark mode.
|
||||
*
|
||||
* NOTE:
|
||||
* - ThemeProvider (antd-style / @lobehub/ui) sets a `data-theme="dark"` attribute at runtime.
|
||||
* - Keep this file purely as variable overrides to minimize drift with upstream styles.
|
||||
*/
|
||||
|
||||
[data-theme='dark'] .diff {
|
||||
/* base */
|
||||
--diff-background-color: transparent;
|
||||
--diff-text-color: #c9d1d9;
|
||||
--diff-font-family:
|
||||
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
|
||||
/* selection */
|
||||
--diff-selection-background-color: rgba(56, 139, 253, 0.35);
|
||||
--diff-selection-text-color: var(--diff-text-color);
|
||||
|
||||
/* gutter */
|
||||
--diff-gutter-insert-background-color: rgba(46, 160, 67, 0.22);
|
||||
--diff-gutter-insert-text-color: var(--diff-text-color);
|
||||
--diff-gutter-delete-background-color: rgba(248, 81, 73, 0.22);
|
||||
--diff-gutter-delete-text-color: var(--diff-text-color);
|
||||
--diff-gutter-selected-background-color: rgba(187, 128, 9, 0.28);
|
||||
--diff-gutter-selected-text-color: var(--diff-text-color);
|
||||
|
||||
/* code */
|
||||
--diff-code-insert-background-color: rgba(46, 160, 67, 0.14);
|
||||
--diff-code-insert-text-color: var(--diff-text-color);
|
||||
--diff-code-delete-background-color: rgba(248, 81, 73, 0.14);
|
||||
--diff-code-delete-text-color: var(--diff-text-color);
|
||||
--diff-code-insert-edit-background-color: rgba(46, 160, 67, 0.28);
|
||||
--diff-code-insert-edit-text-color: var(--diff-text-color);
|
||||
--diff-code-delete-edit-background-color: rgba(248, 81, 73, 0.28);
|
||||
--diff-code-delete-edit-text-color: var(--diff-text-color);
|
||||
--diff-code-selected-background-color: rgba(187, 128, 9, 0.28);
|
||||
--diff-code-selected-text-color: var(--diff-text-color);
|
||||
|
||||
/* omit marker */
|
||||
--diff-omit-gutter-line-color: rgba(248, 81, 73, 0.7);
|
||||
}
|
||||
Reference in New Issue
Block a user