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
|
|
@ -0,0 +1,194 @@
|
|||
import { useState } from "react";
|
||||
import { cn } from "~/lib/utils";
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Brain,
|
||||
Search,
|
||||
FileText,
|
||||
Lightbulb,
|
||||
CheckCircle,
|
||||
Loader2
|
||||
} from "lucide-react";
|
||||
|
||||
export type ThinkingStepType = "thinking" | "searching" | "reading" | "analyzing" | "complete";
|
||||
|
||||
export interface ThinkingStep {
|
||||
/** Step ID */
|
||||
id: string;
|
||||
/** Step type for icon selection */
|
||||
type: ThinkingStepType;
|
||||
/** Step title/label */
|
||||
title: string;
|
||||
/** Step description or content */
|
||||
content?: string;
|
||||
/** Whether step is currently active */
|
||||
isActive?: boolean;
|
||||
/** Whether step is complete */
|
||||
isComplete?: boolean;
|
||||
/** Timestamp */
|
||||
timestamp?: Date;
|
||||
}
|
||||
|
||||
export interface ThinkingStepsDisplayProps {
|
||||
/** List of thinking steps */
|
||||
steps: ThinkingStep[];
|
||||
/** Whether AI is currently thinking */
|
||||
isThinking?: boolean;
|
||||
/** Whether to show expanded by default */
|
||||
defaultExpanded?: boolean;
|
||||
/** Additional class names */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const STEP_ICONS: Record<ThinkingStepType, typeof Brain> = {
|
||||
thinking: Brain,
|
||||
searching: Search,
|
||||
reading: FileText,
|
||||
analyzing: Lightbulb,
|
||||
complete: CheckCircle,
|
||||
};
|
||||
|
||||
const STEP_COLORS: Record<ThinkingStepType, string> = {
|
||||
thinking: "text-purple-500",
|
||||
searching: "text-blue-500",
|
||||
reading: "text-green-500",
|
||||
analyzing: "text-orange-500",
|
||||
complete: "text-green-600",
|
||||
};
|
||||
|
||||
/**
|
||||
* ThinkingStepsDisplay - Shows AI reasoning process
|
||||
*
|
||||
* Features:
|
||||
* - Collapsible thinking steps
|
||||
* - Step-specific icons and colors
|
||||
* - Active step animation
|
||||
* - Expandable step details
|
||||
*/
|
||||
export function ThinkingStepsDisplay({
|
||||
steps,
|
||||
isThinking = false,
|
||||
defaultExpanded = true,
|
||||
className,
|
||||
}: ThinkingStepsDisplayProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||
|
||||
if (steps.length === 0 && !isThinking) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const activeStep = steps.find(s => s.isActive);
|
||||
const completedSteps = steps.filter(s => s.isComplete).length;
|
||||
|
||||
return (
|
||||
<div className={cn("rounded-lg border bg-muted/30", className)}>
|
||||
{/* Header - clickable to expand/collapse */}
|
||||
<button
|
||||
className="w-full flex items-center gap-2 p-3 text-left hover:bg-muted/50 transition-colors"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
|
||||
<Brain className={cn(
|
||||
"h-4 w-4",
|
||||
isThinking ? "text-purple-500 animate-pulse" : "text-muted-foreground"
|
||||
)} />
|
||||
|
||||
<span className="flex-1 text-sm font-medium">
|
||||
{isThinking ? "Thinking..." : "Thought Process"}
|
||||
</span>
|
||||
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{completedSteps}/{steps.length} steps
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* Steps list */}
|
||||
{isExpanded && (
|
||||
<div className="px-3 pb-3 space-y-2">
|
||||
{steps.map((step, index) => (
|
||||
<StepItem key={step.id} step={step} index={index} />
|
||||
))}
|
||||
|
||||
{/* Active thinking indicator */}
|
||||
{isThinking && !activeStep && (
|
||||
<div className="flex items-center gap-2 p-2 rounded-md bg-purple-500/10">
|
||||
<Loader2 className="h-4 w-4 text-purple-500 animate-spin" />
|
||||
<span className="text-sm text-purple-600 dark:text-purple-400">
|
||||
Processing...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual step item
|
||||
*/
|
||||
function StepItem({ step, index }: { step: ThinkingStep; index: number }) {
|
||||
const [isDetailExpanded, setIsDetailExpanded] = useState(false);
|
||||
const Icon = STEP_ICONS[step.type];
|
||||
const colorClass = STEP_COLORS[step.type];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-md transition-colors",
|
||||
step.isActive && "bg-primary/5 ring-1 ring-primary/20",
|
||||
step.isComplete && "opacity-80"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="flex items-start gap-2 p-2 cursor-pointer"
|
||||
onClick={() => step.content && setIsDetailExpanded(!isDetailExpanded)}
|
||||
>
|
||||
{/* Step number or icon */}
|
||||
<div className={cn(
|
||||
"w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0",
|
||||
step.isActive ? "bg-primary/10" : "bg-muted"
|
||||
)}>
|
||||
{step.isActive ? (
|
||||
<Loader2 className={cn("h-3 w-3 animate-spin", colorClass)} />
|
||||
) : step.isComplete ? (
|
||||
<CheckCircle className="h-3 w-3 text-green-500" />
|
||||
) : (
|
||||
<Icon className={cn("h-3 w-3", colorClass)} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Step content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className={cn(
|
||||
"text-sm",
|
||||
step.isActive && "font-medium"
|
||||
)}>
|
||||
{step.title}
|
||||
</p>
|
||||
|
||||
{/* Expandable detail */}
|
||||
{step.content && isDetailExpanded && (
|
||||
<p className="text-xs text-muted-foreground mt-1 whitespace-pre-wrap">
|
||||
{step.content}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Expand indicator */}
|
||||
{step.content && (
|
||||
<ChevronRight className={cn(
|
||||
"h-4 w-4 text-muted-foreground transition-transform",
|
||||
isDetailExpanded && "rotate-90"
|
||||
)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue