feat(ui): 添加文件 Diff 查看功能
当 AI 执行 write_file 或 edit_file 工具时,在工具结果中显示 View Diff 按钮, 点击后在 IDE 面板中显示文件修改前后的对比视图。 主要改动: - core: edit_file/write_file 工具返回 fileDiff 元数据 - ui: 新增 DiffEditor 组件用于显示文件差异 - ui: ChatMessage 添加 View Diff 按钮 - ui: IDE 组件支持 Diff 视图切换 - ui: useChat hook 处理 fileDiff 回调
This commit is contained in:
@@ -4,13 +4,14 @@
|
||||
* 整合文件浏览器和代码编辑器的 IDE 组件
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useState, useCallback, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '../utils/cn.js';
|
||||
import { readFile, getWorkingDirectory } from '../api/client.js';
|
||||
import { FileExplorer } from './FileExplorer.js';
|
||||
import { CodeEditor, getLanguageFromFilename, type EditorTab } from './CodeEditor.js';
|
||||
import type { ActiveFileInfo } from '../api/types.js';
|
||||
import { DiffEditor } from './DiffEditor.js';
|
||||
import type { ActiveFileInfo, FileDiffInfo } from '../api/types.js';
|
||||
|
||||
// localStorage 键名
|
||||
const STORAGE_KEY_TABS = 'ai-assistant-editor-tabs';
|
||||
@@ -28,12 +29,28 @@ interface IDEProps {
|
||||
sidebarWidth?: number;
|
||||
/** 当前活动文件变化回调 */
|
||||
onActiveFileChange?: (file: ActiveFileInfo | null) => void;
|
||||
/** 要显示的 Diff 信息(外部传入) */
|
||||
pendingDiff?: FileDiffInfo | null;
|
||||
/** Diff 关闭回调 */
|
||||
onDiffClose?: () => void;
|
||||
}
|
||||
|
||||
export function IDE({ className, sidebarWidth = 256, onActiveFileChange }: IDEProps) {
|
||||
/** IDE 组件暴露的方法 */
|
||||
export interface IDEHandle {
|
||||
/** 显示文件 diff */
|
||||
showDiff: (diff: FileDiffInfo) => void;
|
||||
/** 关闭 diff 视图 */
|
||||
closeDiff: () => void;
|
||||
}
|
||||
|
||||
export const IDE = forwardRef<IDEHandle, IDEProps>(function IDE(
|
||||
{ className, sidebarWidth = 256, onActiveFileChange, pendingDiff, onDiffClose },
|
||||
ref
|
||||
) {
|
||||
const [tabs, setTabs] = useState<EditorTab[]>([]);
|
||||
const [activeTabId, setActiveTabId] = useState<string | null>(null);
|
||||
const [workingDirectory, setWorkingDirectory] = useState<string>('');
|
||||
const [currentDiff, setCurrentDiff] = useState<FileDiffInfo | null>(null);
|
||||
const isRestoringTabs = useRef(false);
|
||||
const hasRestoredTabs = useRef(false);
|
||||
|
||||
@@ -235,6 +252,47 @@ export function IDE({ className, sidebarWidth = 256, onActiveFileChange }: IDEPr
|
||||
);
|
||||
}, []);
|
||||
|
||||
// 显示 diff 视图
|
||||
const showDiff = useCallback((diff: FileDiffInfo) => {
|
||||
setCurrentDiff(diff);
|
||||
|
||||
// 如果文件已在编辑器中打开,更新其内容
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) => {
|
||||
if (tab.path === diff.path) {
|
||||
return {
|
||||
...tab,
|
||||
content: diff.newContent,
|
||||
originalContent: diff.newContent,
|
||||
};
|
||||
}
|
||||
return tab;
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
// 关闭 diff 视图
|
||||
const closeDiff = useCallback(() => {
|
||||
setCurrentDiff(null);
|
||||
onDiffClose?.();
|
||||
}, [onDiffClose]);
|
||||
|
||||
// 暴露方法给父组件
|
||||
useImperativeHandle(ref, () => ({
|
||||
showDiff,
|
||||
closeDiff,
|
||||
}), [showDiff, closeDiff]);
|
||||
|
||||
// 处理外部传入的 pendingDiff
|
||||
useEffect(() => {
|
||||
if (pendingDiff) {
|
||||
showDiff(pendingDiff);
|
||||
}
|
||||
}, [pendingDiff, showDiff]);
|
||||
|
||||
// 判断是否显示 diff 视图
|
||||
const showDiffView = currentDiff !== null;
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full', className)}>
|
||||
{/* 文件浏览器 */}
|
||||
@@ -245,17 +303,24 @@ export function IDE({ className, sidebarWidth = 256, onActiveFileChange }: IDEPr
|
||||
<FileExplorer onFileSelect={handleFileSelect} workingDirectory={workingDirectory} />
|
||||
</div>
|
||||
|
||||
{/* 代码编辑器 */}
|
||||
{/* 代码编辑器 / Diff 视图 */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<CodeEditor
|
||||
tabs={tabs}
|
||||
activeTabId={activeTabId}
|
||||
onTabChange={handleTabChange}
|
||||
onTabClose={handleTabClose}
|
||||
onContentChange={handleContentChange}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
{showDiffView ? (
|
||||
<DiffEditor
|
||||
diff={currentDiff}
|
||||
onClose={closeDiff}
|
||||
/>
|
||||
) : (
|
||||
<CodeEditor
|
||||
tabs={tabs}
|
||||
activeTabId={activeTabId}
|
||||
onTabChange={handleTabChange}
|
||||
onTabClose={handleTabClose}
|
||||
onContentChange={handleContentChange}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user