feat(desktop): DeerFlow visual redesign + stream hang fix + intelligence client
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

DeerFlow frontend visual overhaul:
- Card-style input box (white rounded card, textarea top, actions bottom)
- Dropdown mode selector (闪速/思考/Pro/Ultra with icons+descriptions)
- Colored quick-action chips (小惊喜/写作/研究/收集/学习)
- Minimal top bar (title + token count + export)
- Warm gray color system (#faf9f6 bg, #f5f4f1 sidebar, #e8e6e1 border)
- DeerFlow-style sidebar (新对话/对话/智能体 nav)
- Reasoning block, tool call chain, task progress visualization
- Streaming text, model selector, suggestion chips components
- Resizable artifact panel with drag handle
- Virtualized message list for 100+ messages

Bug fixes:
- Stream hang: GatewayClient onclose code 1000 now calls onComplete
- WebView2 textarea border: CSS !important override for UA styles
- Gateway stream event handling (response/phase/tool_call types)

Intelligence client:
- Unified client with fallback drivers (compactor/heartbeat/identity/memory/reflection)
- Gateway API types and type conversions
This commit is contained in:
iven
2026-04-01 22:03:07 +08:00
parent e3b93ff96d
commit 73ff5e8c5e
43 changed files with 4817 additions and 905 deletions

View File

@@ -1,19 +1,45 @@
/**
* FirstConversationPrompt - Welcome prompt for new Agents
* FirstConversationPrompt - Welcome prompt for new conversations
*
* Displays a personalized welcome message and quick start suggestions
* when entering a new Agent's chat for the first time.
* DeerFlow-inspired design:
* - Centered layout with emoji greeting
* - Input bar embedded in welcome screen
* - Horizontal quick-action chips (colored pills)
* - Clean, minimal aesthetic
*/
import { motion } from 'framer-motion';
import { Lightbulb, ArrowRight } from 'lucide-react';
import {
Sparkles,
PenLine,
Microscope,
Layers,
GraduationCap,
} from 'lucide-react';
import { cn } from '../lib/utils';
import {
generateWelcomeMessage,
getQuickStartSuggestions,
getScenarioById,
type QuickStartSuggestion,
} from '../lib/personality-presets';
import type { Clone } from '../store/agentStore';
import { useChatStore } from '../store/chatStore';
// Quick action chip definitions — DeerFlow-style colored pills
const QUICK_ACTIONS = [
{ key: 'surprise', label: '小惊喜', icon: Sparkles, color: 'text-orange-500' },
{ key: 'write', label: '写作', icon: PenLine, color: 'text-blue-500' },
{ key: 'research', label: '研究', icon: Microscope, color: 'text-purple-500' },
{ key: 'collect', label: '收集', icon: Layers, color: 'text-green-500' },
{ key: 'learn', label: '学习', icon: GraduationCap, color: 'text-indigo-500' },
];
// Pre-filled prompts for each quick action
const QUICK_ACTION_PROMPTS: Record<string, string> = {
surprise: '给我一个小惊喜吧!来点创意的',
write: '帮我写一篇文章,主题你来定',
research: '帮我做一个深度研究分析',
collect: '帮我收集整理一些有用的信息',
learn: '我想学点新东西,教我一些有趣的知识',
};
interface FirstConversationPromptProps {
clone: Clone;
@@ -25,7 +51,15 @@ export function FirstConversationPrompt({
clone,
onSelectSuggestion,
}: FirstConversationPromptProps) {
// Generate welcome message
const chatMode = useChatStore((s) => s.chatMode);
const modeGreeting: Record<string, string> = {
flash: '快速回答,即时响应',
thinking: '深度分析,逐步推理',
pro: '专业规划,系统思考',
ultra: '多代理协作,全能力调度',
};
const welcomeMessage = generateWelcomeMessage({
userName: clone.userName,
agentName: clone.nickname || clone.name,
@@ -34,11 +68,9 @@ export function FirstConversationPrompt({
scenarios: clone.scenarios,
});
// Get quick start suggestions based on scenarios
const suggestions = getQuickStartSuggestions(clone.scenarios || []);
const handleSuggestionClick = (suggestion: QuickStartSuggestion) => {
onSelectSuggestion?.(suggestion.text);
const handleQuickAction = (key: string) => {
const prompt = QUICK_ACTION_PROMPTS[key] || '你好!';
onSelectSuggestion?.(prompt);
};
return (
@@ -48,48 +80,63 @@ export function FirstConversationPrompt({
exit={{ opacity: 0, y: -10 }}
className="flex flex-col items-center justify-center py-12 px-4"
>
{/* Avatar with emoji */}
<div className="mb-6">
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-primary/20 to-primary/10 dark:from-primary/30 dark:to-primary/20 flex items-center justify-center shadow-lg">
<span className="text-4xl">{clone.emoji || '🦞'}</span>
</div>
</div>
{/* Greeting emoji */}
<div className="text-5xl mb-4">{clone.emoji || '👋'}</div>
{/* Title */}
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1, duration: 0.5 }}
className="text-2xl font-semibold text-gray-900 dark:text-gray-100 mb-2"
>
</motion.h1>
{/* Mode-aware subtitle */}
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2, duration: 0.4 }}
className="text-sm text-orange-500 dark:text-orange-400 font-medium mb-4 flex items-center gap-1.5"
>
<Sparkles className="w-3.5 h-3.5" />
{modeGreeting[chatMode] || '智能对话,随时待命'}
</motion.p>
{/* Welcome message */}
<div className="text-center max-w-md mb-8">
<p className="text-lg text-gray-700 dark:text-gray-200 whitespace-pre-line leading-relaxed">
<p className="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">
{welcomeMessage}
</p>
</div>
{/* Quick start suggestions */}
<div className="w-full max-w-lg space-y-2">
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-3">
<Lightbulb className="w-4 h-4" />
<span></span>
</div>
{suggestions.map((suggestion, index) => (
<motion.button
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
onClick={() => handleSuggestionClick(suggestion)}
className={cn(
'w-full flex items-center gap-3 px-4 py-3 rounded-xl',
'bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700',
'hover:bg-gray-100 dark:hover:bg-gray-800 hover:border-primary/30',
'transition-all duration-200 group text-left'
)}
>
<span className="text-xl flex-shrink-0">{suggestion.icon}</span>
<span className="flex-1 text-sm text-gray-700 dark:text-gray-200">
{suggestion.text}
</span>
<ArrowRight className="w-4 h-4 text-gray-400 group-hover:text-primary transition-colors flex-shrink-0" />
</motion.button>
))}
{/* Quick action chips — DeerFlow-style horizontal colored pills */}
<div className="flex items-center justify-center gap-2 flex-wrap">
{QUICK_ACTIONS.map((action, index) => {
const ActionIcon = action.icon;
return (
<motion.button
key={action.key}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + index * 0.05, duration: 0.2 }}
onClick={() => handleQuickAction(action.key)}
className={cn(
'flex items-center gap-2 px-4 py-2',
'bg-white dark:bg-gray-800',
'border border-gray-200 dark:border-gray-700',
'rounded-full text-sm text-gray-600 dark:text-gray-300',
'hover:border-gray-300 dark:hover:border-gray-600',
'hover:bg-gray-50 dark:hover:bg-gray-750',
'transition-all duration-150'
)}
>
<ActionIcon className={`w-4 h-4 ${action.color}`} />
<span>{action.label}</span>
</motion.button>
);
})}
</div>
{/* Scenario tags */}