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:
@@ -24,6 +24,7 @@ import {
|
||||
createSession,
|
||||
type Session,
|
||||
type ActiveFileInfo,
|
||||
type FileDiffInfo,
|
||||
} from '@ai-assistant/ui';
|
||||
import { ChatPage } from './pages/Chat';
|
||||
|
||||
@@ -53,6 +54,9 @@ export function App() {
|
||||
return saved !== 'false'; // 默认开启
|
||||
});
|
||||
|
||||
// Diff 显示状态(当 AI 编辑/写入文件时触发)
|
||||
const [pendingDiff, setPendingDiff] = useState<FileDiffInfo | null>(null);
|
||||
|
||||
// 持久化自动附加开关状态
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ai-assistant-auto-attach-file', String(autoAttachActiveFile));
|
||||
@@ -121,6 +125,16 @@ export function App() {
|
||||
setCurrentSessionId(newSessionId);
|
||||
}, []);
|
||||
|
||||
// 文件 diff 回调(当 AI 编辑/写入文件时触发)
|
||||
const handleFileDiff = useCallback((diff: FileDiffInfo) => {
|
||||
setPendingDiff(diff);
|
||||
}, []);
|
||||
|
||||
// Diff 关闭回调
|
||||
const handleDiffClose = useCallback(() => {
|
||||
setPendingDiff(null);
|
||||
}, []);
|
||||
|
||||
// 处理面板宽度调整
|
||||
const handleResize = useCallback((delta: number) => {
|
||||
setIdePanelWidth((prev) => {
|
||||
@@ -159,7 +173,11 @@ export function App() {
|
||||
className="hidden md:flex flex-col"
|
||||
style={{ width: `${idePanelWidth}%` }}
|
||||
>
|
||||
<IDE onActiveFileChange={setActiveFile} />
|
||||
<IDE
|
||||
onActiveFileChange={setActiveFile}
|
||||
pendingDiff={pendingDiff}
|
||||
onDiffClose={handleDiffClose}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 可拖拽分割线 */}
|
||||
@@ -191,6 +209,8 @@ export function App() {
|
||||
activeFile={activeFile}
|
||||
autoAttachActiveFile={autoAttachActiveFile}
|
||||
onAutoAttachActiveFileToggle={setAutoAttachActiveFile}
|
||||
onFileDiff={handleFileDiff}
|
||||
onViewDiff={handleFileDiff}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center h-full">
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
DiagnosticsIndicator,
|
||||
ToolbarOverflowMenu,
|
||||
type ActiveFileInfo,
|
||||
type FileDiffInfo,
|
||||
} from '@ai-assistant/ui';
|
||||
|
||||
interface ChatPageProps {
|
||||
@@ -43,6 +44,10 @@ interface ChatPageProps {
|
||||
autoAttachActiveFile?: boolean;
|
||||
/** 自动附加开关变更回调 */
|
||||
onAutoAttachActiveFileToggle?: (enabled: boolean) => void;
|
||||
/** 文件 diff 回调(当 AI 写入/编辑文件时触发) */
|
||||
onFileDiff?: (diff: FileDiffInfo) => void;
|
||||
/** 查看文件 diff 回调(点击 View Diff 按钮) */
|
||||
onViewDiff?: (diff: FileDiffInfo) => void;
|
||||
}
|
||||
|
||||
export function ChatPage({
|
||||
@@ -63,6 +68,8 @@ export function ChatPage({
|
||||
activeFile,
|
||||
autoAttachActiveFile,
|
||||
onAutoAttachActiveFileToggle,
|
||||
onFileDiff,
|
||||
onViewDiff,
|
||||
}: ChatPageProps) {
|
||||
const {
|
||||
messages,
|
||||
@@ -100,6 +107,7 @@ export function ChatPage({
|
||||
: undefined,
|
||||
});
|
||||
},
|
||||
onFileDiff,
|
||||
});
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
@@ -208,13 +216,13 @@ export function ChatPage({
|
||||
|
||||
<AnimatePresence mode="popLayout">
|
||||
{messages.map((message) => (
|
||||
<ChatMessage key={message.id} message={message} onAnswerQuestion={answerQuestion} />
|
||||
<ChatMessage key={message.id} message={message} onAnswerQuestion={answerQuestion} onViewDiff={onViewDiff ?? onFileDiff} />
|
||||
))}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* 流式消息 - 复用 ChatMessage 组件 */}
|
||||
{streamingMessage && (
|
||||
<ChatMessage message={streamingMessage} isStreaming onAnswerQuestion={answerQuestion} />
|
||||
<ChatMessage message={streamingMessage} isStreaming onAnswerQuestion={answerQuestion} onViewDiff={onViewDiff ?? onFileDiff} />
|
||||
)}
|
||||
|
||||
{/* 子 Agent 进度显示 */}
|
||||
|
||||
Reference in New Issue
Block a user