mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-28 10:26:33 +02:00
feat(crypto): add SurfSense 2.0 Crypto Co-Pilot UI components
Frontend - Web Dashboard: - Add crypto dashboard page with Watchlist, Alerts, Market, Profile tabs - Add 11 tool-ui components for inline chat display - Add crypto components (ChainIcon, SafetyBadge, PriceDisplay, etc.) - Add modals (AddTokenModal, CreateAlertModal) - Add mock data for development Frontend - Browser Extension: - Add shared components (ChainIcon, RiskBadge, PriceDisplay, SuggestionCard) - Add crypto components (SafetyScoreDisplay, WatchlistPanel, AlertConfigModal) - Add chat enhancements (WelcomeScreen, ThinkingStepsDisplay) - Add widget components for inline display - Enhance TokenInfoCard, ChatHeader, ChatInput, ChatInterface Documentation: - Add conversational UX specification - Add UX analysis report - Update extension UX design This implements the Conversational UX paradigm where crypto features are AI-callable tools that render inline in the chat interface.
This commit is contained in:
parent
ad795eb830
commit
e4d020799b
58 changed files with 11315 additions and 661 deletions
113
surfsense_browser_extension/sidepanel/chat/WelcomeScreen.tsx
Normal file
113
surfsense_browser_extension/sidepanel/chat/WelcomeScreen.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import { useMemo } from "react";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { SuggestionCard, DEFAULT_CRYPTO_SUGGESTIONS } from "../components/shared";
|
||||
|
||||
export interface WelcomeScreenProps {
|
||||
/** User's display name for personalized greeting */
|
||||
userName?: string;
|
||||
/** Callback when a suggestion is clicked */
|
||||
onSuggestionClick?: (text: string) => void;
|
||||
/** Custom suggestions (overrides defaults) */
|
||||
suggestions?: Array<{ text: string; type: "general" | "safety" | "trending" | "wallet" | "custom" }>;
|
||||
/** Additional class names */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time-based greeting message
|
||||
*/
|
||||
function getTimeBasedGreeting(userName?: string): string {
|
||||
const hour = new Date().getHours();
|
||||
|
||||
// Greeting variations for each time period
|
||||
const morningGreetings = ["Good morning", "Fresh start today", "Morning"];
|
||||
const afternoonGreetings = ["Good afternoon", "Afternoon", "Hey there"];
|
||||
const eveningGreetings = ["Good evening", "Evening", "Hey there"];
|
||||
const nightGreetings = ["Good night", "Evening", "Winding down"];
|
||||
const lateNightGreetings = ["Still up?", "Night owl mode", "Burning the midnight oil"];
|
||||
|
||||
let greeting: string;
|
||||
if (hour < 5) {
|
||||
greeting = lateNightGreetings[Math.floor(Math.random() * lateNightGreetings.length)];
|
||||
} else if (hour < 12) {
|
||||
greeting = morningGreetings[Math.floor(Math.random() * morningGreetings.length)];
|
||||
} else if (hour < 18) {
|
||||
greeting = afternoonGreetings[Math.floor(Math.random() * afternoonGreetings.length)];
|
||||
} else if (hour < 22) {
|
||||
greeting = eveningGreetings[Math.floor(Math.random() * eveningGreetings.length)];
|
||||
} else {
|
||||
greeting = nightGreetings[Math.floor(Math.random() * nightGreetings.length)];
|
||||
}
|
||||
|
||||
// Add personalization with name if available
|
||||
if (userName) {
|
||||
const firstName = userName.split(/\s+/)[0];
|
||||
return `${greeting}, ${firstName}!`;
|
||||
}
|
||||
|
||||
return `${greeting}!`;
|
||||
}
|
||||
|
||||
/**
|
||||
* WelcomeScreen - Displays greeting and suggestion cards for new conversations
|
||||
*
|
||||
* Features:
|
||||
* - Time-based personalized greeting
|
||||
* - Crypto-specific suggestion cards
|
||||
* - Animated entrance
|
||||
* - Accessible keyboard navigation
|
||||
*/
|
||||
export function WelcomeScreen({
|
||||
userName,
|
||||
onSuggestionClick,
|
||||
suggestions = DEFAULT_CRYPTO_SUGGESTIONS,
|
||||
className,
|
||||
}: WelcomeScreenProps) {
|
||||
// Memoize greeting so it doesn't change on re-renders
|
||||
const greeting = useMemo(() => getTimeBasedGreeting(userName), [userName]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col items-center justify-center h-full p-4",
|
||||
"animate-in fade-in slide-in-from-bottom-4 duration-500",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Logo and Greeting */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="text-5xl mb-4">🌊</div>
|
||||
<h1 className="text-2xl font-semibold mb-2 animate-in fade-in slide-in-from-bottom-2 duration-500 delay-100">
|
||||
{greeting}
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-sm animate-in fade-in slide-in-from-bottom-2 duration-500 delay-200">
|
||||
Your AI co-pilot for crypto research and analysis
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Suggestion Cards */}
|
||||
<div className="w-full max-w-sm space-y-2 animate-in fade-in slide-in-from-bottom-3 duration-500 delay-300">
|
||||
<p className="text-xs text-muted-foreground mb-3 flex items-center gap-1">
|
||||
<span>💡</span>
|
||||
<span>Try asking:</span>
|
||||
</p>
|
||||
{suggestions.slice(0, 4).map((suggestion, index) => (
|
||||
<SuggestionCard
|
||||
key={index}
|
||||
text={suggestion.text}
|
||||
type={suggestion.type}
|
||||
onClick={onSuggestionClick}
|
||||
className="animate-in fade-in slide-in-from-bottom-2 duration-300"
|
||||
style={{ animationDelay: `${400 + index * 100}ms` } as React.CSSProperties}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Footer hint */}
|
||||
<p className="text-xs text-muted-foreground mt-6 animate-in fade-in duration-500 delay-700">
|
||||
Press <kbd className="px-1.5 py-0.5 rounded bg-muted text-xs">⌘K</kbd> for quick actions
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue