feat(ui): 将权限请求改为内联显示在聊天流中

- 添加 PermissionMessagePart 类型
- 创建 PermissionRequestInline 内联组件
- 修改 useChat 将权限请求作为 Part 处理
- 移除模态对话框,不再阻断 IDE 操作
This commit is contained in:
2025-12-17 22:03:00 +08:00
parent 2ff74e446c
commit 00680b8ed3
6 changed files with 533 additions and 30 deletions
+20 -2
View File
@@ -23,8 +23,9 @@ import { fadeInUp, smoothTransition } from '../utils/animations';
import { getAgentDisplayName } from '../utils/agent';
import { Markdown } from './Markdown';
import { FileMentionText } from './FileMentionTag';
import type { Message, ToolStatus, ToolMessagePart, QuestionMessagePart, FileDiffInfo } from '../api/types.js';
import type { Message, ToolStatus, ToolMessagePart, QuestionMessagePart, PermissionMessagePart, FileDiffInfo } from '../api/types.js';
import { AskUserQuestion } from './AskUserQuestion.js';
import { PermissionRequestInline } from './PermissionRequestInline.js';
interface ChatMessageProps {
message: Message;
@@ -34,10 +35,14 @@ interface ChatMessageProps {
onAnswerQuestion?: (questionPartId: string, answers: string[]) => void;
/** 查看文件 Diff 的回调 */
onViewDiff?: (diff: FileDiffInfo) => void;
/** 允许权限请求的回调 */
onAllowPermission?: (requestId: string, remember: boolean) => void;
/** 拒绝权限请求的回调 */
onDenyPermission?: (requestId: string, remember: boolean) => void;
}
export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
({ message, isStreaming = false, onAnswerQuestion, onViewDiff }, ref) => {
({ message, isStreaming = false, onAnswerQuestion, onViewDiff, onAllowPermission, onDenyPermission }, ref) => {
const isUser = message.role === 'user';
const [copied, setCopied] = useState(false);
@@ -109,6 +114,19 @@ export const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>(
{part.text}
</div>
);
case 'permission': {
// 权限请求组件:内联显示,不阻断用户操作
const permissionPart = part as PermissionMessagePart;
return (
<PermissionRequestInline
key={part.id}
part={permissionPart}
onAllow={onAllowPermission || (() => {})}
onDeny={onDenyPermission || (() => {})}
disabled={permissionPart.handled}
/>
);
}
default:
return null;
}