返回 VideoClaw
RewriteResultBadge.tsx
根目录 / video-claw / video-claw / frontend / components / stages / RewriteResultBadge.tsx
1 'use client';
2
3 import React, { useState } from 'react';
4 import { AlertTriangle, X } from 'lucide-react';
5
6 export interface RewriteResult {
7 stage?: string;
8 model?: string;
9 error?: string;
10 original_prompt?: string;
11 optimized_prompt?: string;
12 doctor_reason_type?: string;
13 doctor_reason?: string;
14 confidence?: number;
15 }
16
17 function hasRewriteResult(result?: RewriteResult | null): result is RewriteResult {
18 return Boolean(result && Object.keys(result).length > 0);
19 }
20
21 function PromptPanel({ title, text }: { title: string; text?: string }) {
22 return (
23 <div className="min-h-[260px] rounded-lg border border-gray-200 bg-gray-50 p-3">
24 <div className="mb-2 text-xs font-semibold text-gray-700">{title}</div>
25 <pre className="max-h-[360px] overflow-y-auto whitespace-pre-wrap break-words text-xs leading-relaxed text-gray-600 custom-scrollbar">
26 {text || '无'}
27 </pre>
28 </div>
29 );
30 }
31
32 export default function RewriteResultBadge({ rewriteResult }: { rewriteResult?: RewriteResult | null }) {
33 const [open, setOpen] = useState(false);
34 if (!hasRewriteResult(rewriteResult)) return null;
35
36 return (
37 <>
38 <button
39 type="button"
40 onClick={() => setOpen(true)}
41 className="inline-flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-amber-100 text-amber-700 hover:bg-amber-200"
42 title="查看提示词自动优化记录"
43 >
44 <AlertTriangle className="h-3.5 w-3.5" />
45 </button>
46 {open && (
47 <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/35 p-4">
48 <div className="w-full max-w-5xl rounded-xl bg-white shadow-2xl">
49 <div className="flex items-center justify-between border-b border-gray-100 px-5 py-4">
50 <div>
51 <div className="text-sm font-semibold text-gray-900">提示词自动优化记录</div>
52 <div className="mt-0.5 text-xs text-gray-500">
53 Doctor Agent 已判断该错误可通过重写提示词处理
54 </div>
55 </div>
56 <button
57 type="button"
58 onClick={() => setOpen(false)}
59 className="flex h-8 w-8 items-center justify-center rounded-lg text-gray-500 hover:bg-gray-100"
60 title="关闭"
61 >
62 <X className="h-4 w-4" />
63 </button>
64 </div>
65 <div className="space-y-4 p-5">
66 <div className="grid gap-4 md:grid-cols-2">
67 <PromptPanel title="原提示词" text={rewriteResult.original_prompt} />
68 <PromptPanel title="优化后提示词" text={rewriteResult.optimized_prompt} />
69 </div>
70 <div className="grid gap-3 rounded-lg border border-amber-100 bg-amber-50 p-3 text-xs text-gray-700 md:grid-cols-2">
71 <div>
72 <span className="font-semibold text-gray-900">doctor_reason_type:</span>
73 <span>{rewriteResult.doctor_reason_type || 'unknown'}</span>
74 </div>
75 <div>
76 <span className="font-semibold text-gray-900">model:</span>
77 <span>{rewriteResult.model || '无'}</span>
78 </div>
79 <div className="md:col-span-2">
80 <span className="font-semibold text-gray-900">doctor_reason:</span>
81 <span>{rewriteResult.doctor_reason || '无'}</span>
82 </div>
83 {rewriteResult.error && (
84 <div className="md:col-span-2">
85 <span className="font-semibold text-gray-900">error:</span>
86 <span className="break-words">{rewriteResult.error}</span>
87 </div>
88 )}
89 </div>
90 </div>
91 </div>
92 </div>
93 )}
94 </>
95 );
96 }
97
97 lines Plain Text