diff --git a/src/components/EnhancedChatInterface.tsx b/src/components/EnhancedChatInterface.tsx index 82eaab6e..ea3110b3 100644 --- a/src/components/EnhancedChatInterface.tsx +++ b/src/components/EnhancedChatInterface.tsx @@ -17,7 +17,7 @@ import { toast } from 'sonner'; // Import extracted components import { ChatSidebar } from './chat/ChatSidebar'; -import { ChatMessage } from './chat/ChatMessage'; +import { EnhancedChatMessage } from './chat/EnhancedChatMessage'; import { ChatInput } from './chat/ChatInput'; import { WelcomeScreen } from './chat/WelcomeScreen'; import { ErrorBoundary } from './ErrorBoundary'; @@ -398,19 +398,30 @@ const EnhancedChatInterface: React.FC = () => { {/* Messages area */}
- {memoizedMessages.map((message, index) => ( - - ))} + {memoizedMessages.map((message, index) => { + const isUser = message.role === 'user'; + const isFirstInGroup = index === 0 || memoizedMessages[index - 1].role !== message.role; + + return ( + { + await navigator.clipboard.writeText(text); + setCopiedMessage(messageId); + setTimeout(() => setCopiedMessage(null), 2000); + toast.success('Message copied to clipboard!'); + }} + copiedMessage={copiedMessage} + onApproveDiagram={handleApproveDiagram} + onRequestDiagramChanges={handleRequestDiagramChanges} + isSubmittingDiagram={isSubmittingDiagram} + /> + ); + })} {typingIndicator} diff --git a/src/components/chat/EnhancedChatMessage.tsx b/src/components/chat/EnhancedChatMessage.tsx new file mode 100644 index 00000000..4ed97505 --- /dev/null +++ b/src/components/chat/EnhancedChatMessage.tsx @@ -0,0 +1,274 @@ +import { memo, useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Card, CardContent } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { CodeArtifact } from '@/components/ui/code-artifact'; +import { executeCode } from '@/lib/sandbox'; + +import { + Copy, + Check, + Play, + Code2, + Sparkles, + FileText, + Image, + Terminal, + ChevronDown, + ChevronUp +} from 'lucide-react'; +import { toast } from 'sonner'; +import DiagramMessageComponent from '../DiagramMessageComponent'; + +// Types for the enhanced message component +interface ConvexMessage { + _id: string; + content: string | unknown; + role: 'user' | 'assistant'; + createdAt: number; + metadata?: { + model?: string; + tokens?: number; + cost?: number; + diagramData?: unknown; + }; +} + +interface EnhancedChatMessageProps { + message: ConvexMessage; + isUser: boolean; + isFirstInGroup: boolean; + formatTimestamp: (timestamp: number) => string; + copyToClipboard: (text: string, messageId: string) => Promise; + copiedMessage: string | null; + onApproveDiagram?: (messageId: string) => Promise; + onRequestDiagramChanges?: (messageId: string, feedback: string) => Promise; + isSubmittingDiagram?: boolean; +} + +// Enhanced message component with modern AI tool styling +export const EnhancedChatMessage = memo(({ + message, + isUser, + isFirstInGroup, + formatTimestamp, + copyToClipboard, + copiedMessage, + onApproveDiagram, + onRequestDiagramChanges, + isSubmittingDiagram +}) => { + const [showCodeArtifacts, setShowCodeArtifacts] = useState(true); + const [isExpanded, setIsExpanded] = useState(false); + + // Extract code blocks from message content + const extractCodeBlocks = (content: string) => { + const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g; + const codeBlocks: Array<{ language: string; code: string; id: string }> = []; + let match; + + while ((match = codeBlockRegex.exec(content)) !== null) { + const language = match[1] || 'plaintext'; + const code = match[2]; + codeBlocks.push({ + language, + code: code.trim(), + id: `code-${Math.random().toString(36).substr(2, 9)}` + }); + } + + return codeBlocks; + }; + + // Remove code blocks from text content for display + const cleanContent = (content: string) => { + return content.replace(/```(\w+)?\n[\s\S]*?```/g, '').trim(); + }; + + const codeBlocks = extractCodeBlocks(typeof message.content === 'string' ? message.content : ''); + const textContent = cleanContent(typeof message.content === 'string' ? message.content : ''); + + return ( +
+ + + +
+ {/* Message Header */} +
+
+ {isUser ? ( +
+ U +
+ ) : ( +
+ +
+ )} + + {isUser ? 'You' : 'ZapDev AI'} + +
+ + {/* Action Buttons */} +
+ {codeBlocks.length > 0 && ( + + )} + + + + +
+
+ + {/* Message Content */} + {textContent && ( +
+ {textContent} +
+ )} + + {/* Code Artifacts */} + + {showCodeArtifacts && codeBlocks.length > 0 && ( + + {codeBlocks.map((block, index) => ( + + ))} + + )} + + + {/* Metadata */} + + {isExpanded && ( + +
+ {message.metadata?.model && ( + + {String(message.metadata.model)} + + )} + + {message.metadata?.tokens && ( + + {String(message.metadata.tokens)} tokens + + )} + + {message.metadata?.cost && ( + + ${String(message.metadata.cost.toFixed(4))} + + )} + + {message.metadata?.diagramData && ( + + Contains Diagram + + )} + + {codeBlocks.length > 0 && ( + + {codeBlocks.length} Code Block{codeBlocks.length > 1 ? 's' : ''} + + )} +
+
+ )} +
+
+ + {/* Timestamp */} +
+ {String(formatTimestamp(message.createdAt))} +
+
+
+
+ + {/* Diagram Component */} + {onApproveDiagram && onRequestDiagramChanges && message.metadata?.diagramData && ( + + )} +
+ ); +}); + +EnhancedChatMessage.displayName = 'EnhancedChatMessage'; diff --git a/src/components/ui/code-artifact.tsx b/src/components/ui/code-artifact.tsx new file mode 100644 index 00000000..0032f270 --- /dev/null +++ b/src/components/ui/code-artifact.tsx @@ -0,0 +1,427 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { + Play, + Loader2, + CheckCircle, + XCircle, + Terminal, + FileText, + Image, + BarChart, + Sparkles, + Code2, + Zap, + Download, + Copy, + Check, + Maximize2, + Minimize2, + RotateCcw, + Settings, + Eye, + EyeOff, + ChevronDown, + ChevronUp, + ExternalLink +} from 'lucide-react'; +import { toast } from 'sonner'; + +interface ExecutionResult { + success: boolean; + output?: string; + error?: string; + logs: string[]; + artifacts?: Array<{ + name: string; + url: string; + type: 'image' | 'file' | 'chart'; + }>; + executionTime?: number; + memoryUsage?: number; +} + +interface CodeArtifactProps { + code: string; + language: string; + title?: string; + description?: string; + onExecute: (code: string, language: string) => Promise; + autoRun?: boolean; + showLineNumbers?: boolean; + className?: string; +} + +export function CodeArtifact({ + code, + language, + title = "Code Preview", + description, + onExecute, + autoRun = false, + showLineNumbers = true, + className = "" +}: CodeArtifactProps) { + const [isExecuting, setIsExecuting] = useState(false); + const [result, setResult] = useState(null); + const [copied, setCopied] = useState(false); + const [activeTab, setActiveTab] = useState('preview'); + const [isExpanded, setIsExpanded] = useState(false); + const [showPreview, setShowPreview] = useState(true); + const codeRef = useRef(null); + + useEffect(() => { + if (autoRun) { + const t = setTimeout(() => { + handleExecute(); + }, 500); + return () => clearTimeout(t); + } + }, [autoRun]); + + const handleExecute = async () => { + setIsExecuting(true); + setResult(null); + + try { + const executionResult = await onExecute(code, language); + setResult(executionResult); + + if (executionResult.success) { + toast.success('Code executed successfully!'); + setActiveTab('output'); + } else { + toast.error('Execution failed'); + setActiveTab('error'); + } + } catch (error) { + console.error('Execution error:', error); + const errorMessage = String(error); + toast.error('Code execution failed'); + + setResult({ + success: false, + error: errorMessage, + logs: [] + }); + setActiveTab('error'); + } finally { + setIsExecuting(false); + } + }; + + const handleCopy = () => { + navigator.clipboard.writeText(code); + setCopied(true); + toast.success('Code copied to clipboard!'); + setTimeout(() => setCopied(false), 2000); + }; + + const getLanguageIcon = () => { + switch (language.toLowerCase()) { + case 'javascript': + case 'typescript': + return
; + case 'python': + return
; + case 'html': + return
; + case 'css': + return
; + case 'json': + return
; + default: + return ; + } + }; + + const getSyntaxHighlightClass = () => { + switch (language.toLowerCase()) { + case 'javascript': + case 'typescript': + return 'language-javascript'; + case 'python': + return 'language-python'; + case 'html': + return 'language-html'; + case 'css': + return 'language-css'; + case 'json': + return 'language-json'; + default: + return 'language-plaintext'; + } + }; + + return ( + + + {/* Header */} + +
+
+ +
+ +
+
+
+ + {title} + + {description && ( +

{description}

+ )} +
+
+ +
+ + {getLanguageIcon()} + {language} + + +
+ + + + + +
+ + +
+
+
+ + + +
+ + + + Preview + + + + Output + + + + Logs + + + + Error + + +
+ + {/* Code Preview Tab */} + +
+ +
+
+                      {showLineNumbers && (
+                        
+                          {code.split('\n').map((line, index) => (
+                            
+ + {index + 1} + + {line} +
+ ))} +
+ )} + {!showLineNumbers && ( + {code} + )} +
+
+
+ + {/* Code overlay gradient */} +
+
+ + + {/* Output Tab */} + + {result?.output ? ( + +
+
+                      {result.output}
+                    
+
+
+ ) : ( +
+ +

No output available

+
+ )} +
+ + {/* Logs Tab */} + + {result?.logs && result.logs.length > 0 ? ( + +
+ {result.logs.map((log, index) => ( +
+ {log} +
+ ))} +
+
+ ) : ( +
+ +

No logs available

+
+ )} +
+ + {/* Error Tab */} + + {result?.error ? ( +
+
+ + Execution Error +
+ +
+                      {result.error}
+                    
+
+
+ ) : ( +
+ +

No errors occurred

+
+ )} +
+ + + {/* Status Bar */} + {result && ( +
+
+
+
+ {result.success ? ( + + ) : ( + + )} + {result.success ? 'Success' : 'Failed'} +
+ {result.executionTime && ( + {result.executionTime}ms + )} + {result.memoryUsage && ( + {result.memoryUsage}MB + )} +
+ + {result.artifacts && result.artifacts.length > 0 && ( +
+ {result.artifacts.length} artifact(s) + +
+ )} +
+
+ )} + + + + ); +} diff --git a/src/index.css b/src/index.css index 39a40fbd..06c675ef 100644 --- a/src/index.css +++ b/src/index.css @@ -4,64 +4,66 @@ @import "tailwindcss"; @theme { - --color-border: hsl(0 0% 12%); - --color-input: hsl(0 0% 12%); + /* Modern AI Tool Color Scheme */ + --color-border: hsl(0 0% 15%); + --color-input: hsl(0 0% 8%); --color-ring: hsl(224 82% 60%); - --color-background: hsl(0 0% 4%); - --color-foreground: hsl(0 0% 100%); + --color-background: hsl(0 0% 2%); + --color-foreground: hsl(0 0% 98%); --color-primary: #3E6FF3; --color-primary-foreground: hsl(0 0% 100%); - --color-secondary: hsl(0 0% 9%); - --color-secondary-foreground: hsl(0 0% 100%); + --color-secondary: hsl(0 0% 6%); + --color-secondary-foreground: hsl(0 0% 95%); --color-destructive: hsl(0 84% 60%); --color-destructive-foreground: hsl(0 0% 100%); - --color-muted: hsl(0 0% 9%); - --color-muted-foreground: hsl(0 0% 64%); - --color-accent: hsl(0 0% 9%); - --color-accent-foreground: hsl(0 0% 100%); - --color-popover: hsl(0 0% 4%); - --color-popover-foreground: hsl(0 0% 100%); - --color-card: hsl(0 0% 4%); - --color-card-foreground: hsl(0 0% 100%); - --color-sidebar: hsl(0 0% 98%); - --color-sidebar-foreground: hsl(240 5.3% 26.1%); - --color-sidebar-primary: hsl(240 5.9% 10%); - --color-sidebar-primary-foreground: hsl(0 0% 98%); - --color-sidebar-accent: hsl(240 4.8% 95.9%); - --color-sidebar-accent-foreground: hsl(240 5.9% 10%); - --color-sidebar-border: hsl(220 13% 91%); - --color-sidebar-ring: hsl(217.2 91.2% 59.8%); - + --color-muted: hsl(0 0% 8%); + --color-muted-foreground: hsl(0 0% 65%); + --color-accent: hsl(0 0% 10%); + --color-accent-foreground: hsl(0 0% 95%); + --color-popover: hsl(0 0% 3%); + --color-popover-foreground: hsl(0 0% 98%); + --color-card: hsl(0 0% 3%); + --color-card-foreground: hsl(0 0% 98%); + --color-sidebar: hsl(0 0% 4%); + --color-sidebar-foreground: hsl(0 0% 85%); + --color-sidebar-primary: hsl(0 0% 8%); + --color-sidebar-primary-foreground: hsl(0 0% 95%); + --color-sidebar-accent: hsl(0 0% 6%); + --color-sidebar-accent-foreground: hsl(0 0% 90%); + --color-sidebar-border: hsl(0 0% 12%); + --color-sidebar-ring: hsl(224 82% 60%); + --font-family-sans: "Inter", sans-serif; --font-family-mono: "JetBrains Mono", monospace; - - --radius: 0.75rem; - - /* Enhanced design tokens for ZapDev */ - --color-chat-bg: hsl(0 0% 2.5%); - --color-chat-surface: hsl(0 0% 6%); - --color-chat-surface-elevated: hsl(0 0% 8%); - --color-chat-border: hsl(0 0% 15%); - --color-chat-user-bg: hsl(214 100% 50% / 0.1); - --color-chat-user-border: hsl(214 100% 50% / 0.2); - --color-chat-assistant-bg: hsl(0 0% 8% / 0.8); - --color-chat-assistant-border: hsl(0 0% 20% / 0.5); - - /* Gradient tokens */ + + --radius: 0.5rem; + + /* Enhanced design tokens for modern AI tools */ + --color-chat-bg: hsl(0 0% 2%); + --color-chat-surface: hsl(0 0% 4%); + --color-chat-surface-elevated: hsl(0 0% 6%); + --color-chat-border: hsl(0 0% 12%); + --color-chat-user-bg: hsl(214 100% 50% / 0.08); + --color-chat-user-border: hsl(214 100% 50% / 0.15); + --color-chat-assistant-bg: hsl(0 0% 5% / 0.9); + --color-chat-assistant-border: hsl(0 0% 15% / 0.6); + + /* Modern gradient tokens */ --gradient-primary: linear-gradient(135deg, #3E6FF3 0%, #5577FF 50%, #1E40AF 100%); - --gradient-surface: linear-gradient(135deg, hsl(0 0% 8% / 0.9) 0%, hsl(0 0% 6% / 0.9) 100%); - --gradient-glow: radial-gradient(circle at center, hsl(214 100% 50% / 0.15) 0%, transparent 70%); - + --gradient-surface: linear-gradient(135deg, hsl(0 0% 4% / 0.95) 0%, hsl(0 0% 2% / 0.95) 100%); + --gradient-glow: radial-gradient(circle at center, hsl(214 100% 50% / 0.08) 0%, transparent 70%); + --gradient-sidebar: linear-gradient(180deg, hsl(0 0% 4%) 0%, hsl(0 0% 2%) 100%); + /* Animation tokens */ --duration-fast: 150ms; --duration-normal: 300ms; --duration-slow: 500ms; - - /* Shadow tokens */ - --shadow-soft: 0 2px 8px -2px hsl(0 0% 0% / 0.1); - --shadow-medium: 0 4px 16px -4px hsl(0 0% 0% / 0.15); - --shadow-strong: 0 8px 32px -8px hsl(0 0% 0% / 0.2); - --shadow-glow: 0 0 32px hsl(214 100% 50% / 0.3); + + /* Modern shadow tokens */ + --shadow-soft: 0 1px 3px hsl(0 0% 0% / 0.12); + --shadow-medium: 0 4px 12px hsl(0 0% 0% / 0.15); + --shadow-strong: 0 8px 24px hsl(0 0% 0% / 0.2); + --shadow-glow: 0 0 24px hsl(214 100% 50% / 0.2); } @@ -86,12 +88,12 @@ } } -/* Premium glass morphism components */ +/* Modern glass morphism components - inspired by AI tools */ .glass { background: var(--gradient-surface); - backdrop-filter: blur(32px); - -webkit-backdrop-filter: blur(32px); - border: 1px solid hsl(0 0% 100% / 0.12); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid hsl(0 0% 100% / 0.08); position: relative; overflow: hidden; box-shadow: var(--shadow-soft); @@ -103,10 +105,10 @@ inset: 0; border-radius: inherit; padding: 1px; - background: linear-gradient(135deg, - hsl(0 0% 100% / 0.2) 0%, - hsl(0 0% 100% / 0.05) 50%, - hsl(0 0% 100% / 0.1) 100%); + background: linear-gradient(135deg, + hsl(0 0% 100% / 0.15) 0%, + hsl(0 0% 100% / 0.03) 50%, + hsl(0 0% 100% / 0.08) 100%); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; @@ -115,14 +117,13 @@ } .glass-elevated { - background: linear-gradient(135deg, hsl(0 0% 8% / 0.95) 0%, hsl(0 0% 6% / 0.95) 100%); - backdrop-filter: blur(40px); - -webkit-backdrop-filter: blur(40px); - border: 1px solid hsl(0 0% 100% / 0.18); - box-shadow: - 0 8px 32px -8px hsl(0 0% 0% / 0.3), - 0 0 0 1px hsl(0 0% 100% / 0.05), - inset 0 1px 0 hsl(0 0% 100% / 0.1); + background: var(--gradient-surface); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid hsl(0 0% 100% / 0.1); + box-shadow: + 0 4px 16px -4px hsl(0 0% 0% / 0.2), + 0 0 0 1px hsl(0 0% 100% / 0.03); } .glass-hover { @@ -130,13 +131,12 @@ } .glass-hover:hover { - background: linear-gradient(135deg, hsl(0 0% 100% / 0.12) 0%, hsl(0 0% 100% / 0.06) 100%); - border-color: hsl(0 0% 100% / 0.25); - transform: translateY(-2px) scale(1.02); - box-shadow: - 0 12px 40px -8px hsl(0 0% 0% / 0.4), - 0 0 0 1px hsl(0 0% 100% / 0.08), - inset 0 1px 0 hsl(0 0% 100% / 0.15); + background: linear-gradient(135deg, hsl(0 0% 100% / 0.08) 0%, hsl(0 0% 100% / 0.04) 100%); + border-color: hsl(0 0% 100% / 0.15); + transform: translateY(-1px); + box-shadow: + 0 8px 24px -4px hsl(0 0% 0% / 0.25), + 0 0 0 1px hsl(0 0% 100% / 0.05); } /* Enhanced text gradients */ @@ -194,14 +194,14 @@ transform: translateY(0); } -/* Premium chat-specific styling */ +/* Modern chat bubble styling - inspired by AI tools */ .chat-bubble-user { - background: linear-gradient(135deg, hsl(214 100% 50% / 0.15) 0%, hsl(214 100% 50% / 0.08) 100%); - border: 1px solid hsl(214 100% 50% / 0.25); + background: linear-gradient(135deg, hsl(214 100% 50% / 0.08) 0%, hsl(214 100% 50% / 0.04) 100%); + border: 1px solid hsl(214 100% 50% / 0.15); position: relative; - box-shadow: - 0 4px 16px -4px hsl(214 100% 50% / 0.2), - inset 0 1px 0 hsl(0 0% 100% / 0.1); + box-shadow: + 0 2px 8px -2px hsl(214 100% 50% / 0.15); + border-radius: 1rem; } .chat-bubble-user::before { @@ -216,17 +216,31 @@ } .chat-bubble-user:hover::before { - opacity: 1; + opacity: 0.5; } .chat-bubble-assistant { - background: linear-gradient(135deg, hsl(0 0% 8% / 0.9) 0%, hsl(0 0% 6% / 0.8) 100%); - border: 1px solid hsl(0 0% 20% / 0.6); - backdrop-filter: blur(24px); - -webkit-backdrop-filter: blur(24px); - box-shadow: - 0 4px 16px -4px hsl(0 0% 0% / 0.2), - inset 0 1px 0 hsl(0 0% 100% / 0.05); + background: var(--gradient-surface); + border: 1px solid hsl(0 0% 100% / 0.06); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + box-shadow: + 0 2px 8px -2px hsl(0 0% 0% / 0.15); + border-radius: 1rem; +} + +/* Modern input styling */ +.chat-input { + background: var(--gradient-surface); + border: 1px solid hsl(0 0% 100% / 0.08); + border-radius: 0.75rem; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); +} + +.chat-input:focus { + border-color: hsl(224 82% 60% / 0.5); + box-shadow: 0 0 0 1px hsl(224 82% 60% / 0.2); } /* Enhanced animations */ @@ -301,26 +315,44 @@ transition: all var(--duration-slow) ease-out; } -/* Premium scrollbar styling */ +/* Modern scrollbar styling */ .custom-scrollbar::-webkit-scrollbar { - width: 8px; + width: 6px; } .custom-scrollbar::-webkit-scrollbar-track { - background: hsl(0 0% 5%); - border-radius: 4px; + background: transparent; } .custom-scrollbar::-webkit-scrollbar-thumb { - background: linear-gradient(45deg, hsl(0 0% 25%) 0%, hsl(0 0% 20%) 100%); - border-radius: 4px; - border: 1px solid hsl(0 0% 15%); + background: hsl(0 0% 20%); + border-radius: 3px; transition: all var(--duration-fast); } .custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: linear-gradient(45deg, hsl(214 100% 50% / 0.6) 0%, hsl(214 100% 45% / 0.4) 100%); - border-color: hsl(214 100% 50% / 0.3); + background: hsl(0 0% 30%); +} + +/* Modern sidebar styling */ +.sidebar { + background: var(--gradient-sidebar); + border-right: 1px solid hsl(0 0% 100% / 0.06); +} + +.sidebar-item { + border-radius: 0.5rem; + transition: all var(--duration-fast); + margin: 0.125rem 0.5rem; +} + +.sidebar-item:hover { + background: hsl(0 0% 100% / 0.05); +} + +.sidebar-item.active { + background: hsl(214 100% 50% / 0.1); + border: 1px solid hsl(214 100% 50% / 0.2); } /* Enhanced focus styles */ @@ -330,6 +362,140 @@ border-radius: var(--radius); } +/* Modern code artifact styles */ +.code-artifact-container { + background: linear-gradient(135deg, hsl(0 0% 4% / 0.95) 0%, hsl(0 0% 2% / 0.95) 100%); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border: 1px solid hsl(0 0% 100% / 0.08); + border-radius: 1rem; + box-shadow: + 0 8px 32px -4px hsl(0 0% 0% / 0.3), + 0 0 0 1px hsl(0 0% 100% / 0.03); + position: relative; + overflow: hidden; +} + +.code-artifact-container::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 1px; + background: linear-gradient(135deg, + hsl(214 100% 50% / 0.15) 0%, + hsl(0 0% 100% / 0.03) 50%, + hsl(264 100% 50% / 0.15) 100%); + -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.code-artifact-header { + background: linear-gradient(135deg, + hsl(214 100% 50% / 0.05) 0%, + hsl(264 100% 50% / 0.05) 50%, + hsl(214 100% 50% / 0.05) 100%); + border-bottom: 1px solid hsl(0 0% 100% / 0.08); + position: relative; + overflow: hidden; +} + +.code-artifact-header::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(90deg, + transparent 0%, + hsl(214 100% 50% / 0.03) 50%, + transparent 100%); + animation: shimmer 2s ease-in-out infinite; + pointer-events: none; +} + +@keyframes shimmer { + 0%, 100% { transform: translateX(-100%); } + 50% { transform: translateX(100%); } +} + +/* Enhanced chat bubble gradients */ +.chat-bubble-user { + background: linear-gradient(135deg, + hsl(214 100% 50% / 0.12) 0%, + hsl(214 100% 50% / 0.06) 50%, + hsl(214 100% 50% / 0.03) 100%); + border: 1px solid hsl(214 100% 50% / 0.2); + position: relative; + box-shadow: + 0 4px 16px -2px hsl(214 100% 50% / 0.2), + 0 0 0 1px hsl(214 100% 50% / 0.05); + border-radius: 1.25rem; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +.chat-bubble-assistant { + background: linear-gradient(135deg, + hsl(0 0% 5% / 0.95) 0%, + hsl(0 0% 3% / 0.95) 50%, + hsl(0 0% 5% / 0.95) 100%); + border: 1px solid hsl(0 0% 100% / 0.08); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + box-shadow: + 0 4px 16px -2px hsl(0 0% 0% / 0.2), + 0 0 0 1px hsl(0 0% 100% / 0.03); + border-radius: 1.25rem; +} + +/* Modern prose styling */ +.prose { + color: hsl(0 0% 95%); +} + +.prose-invert { + color: hsl(0 0% 95%); +} + +.prose code { + background: hsl(0 0% 10%); + border: 1px solid hsl(0 0% 20%); + border-radius: 0.375rem; + padding: 0.125rem 0.25rem; + font-size: 0.875em; + color: hsl(0 0% 90%); +} + +.prose pre { + background: hsl(0 0% 5%); + border: 1px solid hsl(0 0% 15%); + border-radius: 0.5rem; + padding: 1rem; + overflow-x: auto; + color: hsl(0 0% 90%); +} + +.prose pre code { + background: transparent; + border: none; + padding: 0; + color: inherit; +} + +/* Enhanced button hover effects */ +.glass-hover:hover { + background: linear-gradient(135deg, + hsl(0 0% 100% / 0.1) 0%, + hsl(0 0% 100% / 0.05) 100%); + border-color: hsl(0 0% 100% / 0.2); + transform: translateY(-1px); + box-shadow: + 0 8px 24px -4px hsl(0 0% 0% / 0.3), + 0 0 0 1px hsl(0 0% 100% / 0.08); +} + /* Typography enhancements */ .font-code { font-family: var(--font-family-mono);