代码修改
This commit is contained in:
1 parent
8f5af46ec2
commit
8f75e12871
168 files changed
+76575
No files matched your search
@@ -0,0 +1,246 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Bot, CheckCircle2, ChevronDown, Info, Sparkles, UserRound, Wrench } from 'lucide-react';
|
||||
import MarkdownRenderer from '../MarkdownRenderer';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
|
||||
interface ConversationStep {
|
||||
text: string;
|
||||
subText?: string;
|
||||
status: 'done' | 'processing';
|
||||
}
|
||||
|
||||
interface ToolCall {
|
||||
id: string;
|
||||
name: string;
|
||||
input?: Record<string, unknown>;
|
||||
output?: string;
|
||||
}
|
||||
|
||||
interface ConversationMessage {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
steps?: ConversationStep[];
|
||||
thinkings?: Array<{ duration: number; content: string }>;
|
||||
toolCalls?: ToolCall[];
|
||||
}
|
||||
|
||||
interface SkillCreatorConversationItemProps {
|
||||
message: ConversationMessage;
|
||||
isConnecting?: boolean;
|
||||
}
|
||||
|
||||
function MessageAvatar({ role, active = false }: { role: 'user' | 'assistant'; active?: boolean }) {
|
||||
const { theme } = useTheme();
|
||||
const isDarkMode = theme === 'dark';
|
||||
const Icon = role === 'assistant' ? Bot : UserRound;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative w-9 h-9 rounded-2xl flex items-center justify-center flex-shrink-0 shadow-sm ${
|
||||
role === 'assistant'
|
||||
? isDarkMode
|
||||
? 'bg-gradient-to-br from-slate-700 to-slate-900 text-sky-200 ring-1 ring-slate-600'
|
||||
: 'bg-gradient-to-br from-red-50 to-rose-100 text-red-500 ring-1 ring-red-100'
|
||||
: isDarkMode
|
||||
? 'bg-gradient-to-br from-blue-500 to-indigo-600 text-white'
|
||||
: 'bg-gradient-to-br from-red-500 to-rose-500 text-white'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{role === 'assistant' && active && (
|
||||
<Sparkles className={`absolute -right-0.5 -top-0.5 w-3.5 h-3.5 ${isDarkMode ? 'text-sky-200' : 'text-amber-400'}`} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StepsSummary({ steps }: { steps: ConversationStep[] }) {
|
||||
const { theme } = useTheme();
|
||||
const isDarkMode = theme === 'dark';
|
||||
const [open, setOpen] = useState(true);
|
||||
const doneCount = steps.filter((step) => step.status === 'done').length;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`mb-3 overflow-hidden rounded-2xl border shadow-sm ${
|
||||
isDarkMode ? 'border-slate-700 bg-slate-900/60 text-slate-300' : 'border-red-100 bg-white text-gray-600'
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
className={`w-full h-10 px-3.5 flex items-center gap-2 text-xs transition ${
|
||||
isDarkMode ? 'bg-slate-800/70 hover:bg-slate-800' : 'bg-red-50/60 hover:bg-red-50'
|
||||
}`}
|
||||
>
|
||||
<CheckCircle2 className={`w-3.5 h-3.5 ${isDarkMode ? 'text-emerald-400' : 'text-red-500'}`} />
|
||||
<span className="font-medium">执行步骤</span>
|
||||
<span className={isDarkMode ? 'text-slate-500' : 'text-gray-400'}>已完成 {doneCount} / {steps.length}</span>
|
||||
<ChevronDown className={`w-3.5 h-3.5 ml-auto transition-transform ${open ? '' : '-rotate-90'}`} />
|
||||
</button>
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${open ? 'max-h-[640px] opacity-100' : 'max-h-0 opacity-0'}`}>
|
||||
<div className={`border-t px-4 py-3 space-y-3 ${isDarkMode ? 'border-slate-700' : 'border-red-50'}`}>
|
||||
{steps.map((step, index) => {
|
||||
const done = step.status === 'done';
|
||||
|
||||
return (
|
||||
<div key={`${step.text}-${index}`} className="flex gap-3">
|
||||
<div className="pt-0.5">
|
||||
{done ? (
|
||||
<CheckCircle2 className={`w-4 h-4 ${isDarkMode ? 'text-emerald-400' : 'text-red-500'}`} />
|
||||
) : (
|
||||
<span className={`block w-4 h-4 rounded-full border-2 ${isDarkMode ? 'border-sky-300 border-t-transparent' : 'border-red-400 border-t-transparent'} animate-spin`} />
|
||||
)}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className={`text-xs font-medium ${isDarkMode ? 'text-slate-200' : 'text-gray-800'}`}>
|
||||
第 {index + 1} 步:{step.text}
|
||||
</div>
|
||||
{step.subText && (
|
||||
<div className={`mt-1 rounded-xl px-3 py-2 text-xs leading-5 ${isDarkMode ? 'bg-slate-950/50 text-slate-400' : 'bg-gray-50 text-gray-600'}`}>
|
||||
{step.subText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ThinkingPanel({ duration, content }: { duration: number; content: string }) {
|
||||
const { theme } = useTheme();
|
||||
const isDarkMode = theme === 'dark';
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (duration > 0) setOpen(false);
|
||||
}, [duration]);
|
||||
|
||||
return (
|
||||
<div className={`mb-3 rounded-2xl border overflow-hidden shadow-sm ${isDarkMode ? 'border-slate-700 bg-slate-900/60' : 'border-red-100 bg-white'}`}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
className={`w-full h-10 px-3.5 flex items-center gap-2 text-xs transition ${
|
||||
isDarkMode ? 'text-slate-300 bg-slate-800/70 hover:bg-slate-800' : 'text-gray-600 bg-red-50/60 hover:bg-red-50'
|
||||
}`}
|
||||
>
|
||||
<Info className={`w-3.5 h-3.5 ${isDarkMode ? 'text-sky-300' : 'text-red-500'}`} />
|
||||
<span className="font-medium">思考过程</span>
|
||||
<ChevronDown className={`w-3.5 h-3.5 ml-auto transition-transform ${open ? '' : '-rotate-90'}`} />
|
||||
</button>
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${open ? 'opacity-100' : 'max-h-0 opacity-0'}`}>
|
||||
<div className={`px-4 py-3 text-xs leading-6 whitespace-pre-wrap border-t ${isDarkMode ? 'border-slate-700 text-slate-300' : 'border-red-50 text-gray-600'}`}>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ToolCallPanel({ toolCall }: { toolCall: ToolCall }) {
|
||||
const { theme } = useTheme();
|
||||
const isDarkMode = theme === 'dark';
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const details = {
|
||||
input: toolCall.input && Object.keys(toolCall.input).length > 0 ? JSON.stringify(toolCall.input, null, 2) : '',
|
||||
output: toolCall.output ?? '',
|
||||
};
|
||||
const hasDetails = Boolean(details.input || details.output);
|
||||
|
||||
return (
|
||||
<div className={`mb-3 rounded-2xl border overflow-hidden shadow-sm ${isDarkMode ? 'border-slate-700 bg-slate-900/60' : 'border-red-100 bg-white'}`}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
className={`w-full h-10 px-3.5 flex items-center gap-2 text-xs transition ${
|
||||
isDarkMode ? 'text-slate-300 bg-slate-800/70 hover:bg-slate-800' : 'text-gray-600 bg-red-50/60 hover:bg-red-50'
|
||||
}`}
|
||||
>
|
||||
<Wrench className={`w-3.5 h-3.5 ${isDarkMode ? 'text-amber-300' : 'text-red-500'}`} />
|
||||
<span className="font-medium">{toolCall.name}</span>
|
||||
{hasDetails && <ChevronDown className={`w-3.5 h-3.5 ml-auto transition-transform ${open ? '' : '-rotate-90'}`} />}
|
||||
</button>
|
||||
{hasDetails && (
|
||||
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${open ? 'max-h-[520px] opacity-100' : 'max-h-0 opacity-0'}`}>
|
||||
<div className={`border-t px-4 py-3 space-y-3 ${isDarkMode ? 'border-slate-700' : 'border-red-50'}`}>
|
||||
{details.input && (
|
||||
<div>
|
||||
<p className={`text-[11px] mb-1 ${isDarkMode ? 'text-slate-400' : 'text-gray-500'}`}>请求参数</p>
|
||||
<pre className={`rounded-xl px-3 py-2 text-[11px] leading-5 overflow-x-auto ${isDarkMode ? 'bg-slate-950/60 text-slate-300' : 'bg-gray-50 text-gray-700'}`}>{details.input}</pre>
|
||||
</div>
|
||||
)}
|
||||
{details.output && (
|
||||
<div>
|
||||
<p className={`text-[11px] mb-1 ${isDarkMode ? 'text-slate-400' : 'text-gray-500'}`}>返回结果</p>
|
||||
<pre className={`rounded-xl px-3 py-2 text-[11px] leading-5 overflow-x-auto ${isDarkMode ? 'bg-slate-950/60 text-slate-300' : 'bg-gray-50 text-gray-700'}`}>{details.output}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SkillCreatorConversationItem({ message, isConnecting = false }: SkillCreatorConversationItemProps) {
|
||||
const { theme } = useTheme();
|
||||
const isDarkMode = theme === 'dark';
|
||||
|
||||
if (message.role === 'user') {
|
||||
return (
|
||||
<div className="flex justify-end gap-3">
|
||||
<div className={`max-w-[72%] rounded-2xl rounded-tr-md px-4 py-3 text-sm leading-relaxed shadow-sm ${
|
||||
isDarkMode ? 'bg-blue-600/80 text-white shadow-blue-950/20' : 'bg-gradient-to-r from-red-500 to-rose-500 text-white shadow-red-200/70'
|
||||
}`}>
|
||||
{message.content}
|
||||
</div>
|
||||
<MessageAvatar role="user" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<MessageAvatar role="assistant" active={!message.content} />
|
||||
<div
|
||||
className={`max-w-[86%] flex-1 rounded-3xl border px-4 py-3.5 text-sm shadow-sm ${
|
||||
isDarkMode ? 'border-slate-700 bg-slate-800/45 text-slate-200 shadow-black/10' : 'border-red-100 bg-white text-[#2a2f3f] shadow-red-100/60'
|
||||
}`}
|
||||
>
|
||||
{message.steps && message.steps.length > 0 && <StepsSummary steps={message.steps} />}
|
||||
{message.thinkings?.map((thinking, index) => (
|
||||
<ThinkingPanel key={`${message.id}-thinking-${index}`} duration={thinking.duration} content={thinking.content} />
|
||||
))}
|
||||
{message.toolCalls?.map((toolCall) => (
|
||||
<ToolCallPanel key={toolCall.id} toolCall={toolCall} />
|
||||
))}
|
||||
{isConnecting && (
|
||||
<div className={`flex items-center gap-2 text-sm ${isDarkMode ? 'text-slate-400' : 'text-gray-500'}`}>
|
||||
<span className="skill-creator-dot" />
|
||||
<span className="skill-creator-dot" />
|
||||
<span className="skill-creator-dot" />
|
||||
<span
|
||||
data-text="正在连接技能服务..."
|
||||
className={`ml-1 skill-creator-shimmer-text ${isDarkMode ? 'skill-creator-shimmer-dark' : 'skill-creator-shimmer-light'}`}
|
||||
>
|
||||
正在连接技能服务...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{message.content && (
|
||||
<MarkdownRenderer
|
||||
content={message.content}
|
||||
className={isDarkMode ? '[&_p]:text-slate-200 [&_li]:text-slate-200' : '[&_p]:text-[#2a2f3f] [&_li]:text-[#2a2f3f]'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user