Files
0xdesign__design-plugin/design-ide/components/chat/Message.tsx
0xdesign b957873a9b feat: Initial Design IDE project structure
Implements prototype-first development tool with:
- Next.js 15 App Router with TypeScript
- Tailwind CSS + shadcn/ui component library
- AI SDK integration with Claude for variant generation
- Sandpack for live code preview
- Core tools: askQuestion, generateVariant, processFeedback, buildFullStack
- Variant grid with 5-variant display (A-E focus areas)
- Feedback panel for Figma-style commenting
- Project management pages (list, IDE view)
- Supabase client setup for auth and storage

Key differentiator: Generate 5 visual prototypes exploring different
design directions before building, rather than immediate code generation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 18:17:33 -05:00

94 lines
2.7 KiB
TypeScript

'use client';
import { cn } from '@/lib/utils';
import { Bot, User } from 'lucide-react';
import ReactMarkdown from 'react-markdown';
interface MessageProps {
role: 'user' | 'assistant';
content: string;
isLoading?: boolean;
}
export function Message({ role, content, isLoading }: MessageProps) {
const isUser = role === 'user';
return (
<div
className={cn(
'flex gap-3 message-enter',
isUser ? 'flex-row-reverse' : 'flex-row'
)}
>
{/* Avatar */}
<div
className={cn(
'flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center',
isUser ? 'bg-primary text-primary-foreground' : 'bg-muted'
)}
>
{isUser ? (
<User className="h-4 w-4" />
) : (
<Bot className="h-4 w-4" />
)}
</div>
{/* Message content */}
<div
className={cn(
'flex-1 max-w-[80%] rounded-lg px-4 py-2',
isUser
? 'bg-primary text-primary-foreground'
: 'bg-muted'
)}
>
{isLoading ? (
<div className="flex items-center gap-1">
<div className="w-2 h-2 bg-current rounded-full animate-bounce" />
<div
className="w-2 h-2 bg-current rounded-full animate-bounce"
style={{ animationDelay: '0.1s' }}
/>
<div
className="w-2 h-2 bg-current rounded-full animate-bounce"
style={{ animationDelay: '0.2s' }}
/>
</div>
) : (
<div className="prose prose-sm dark:prose-invert max-w-none">
<ReactMarkdown
components={{
p: ({ children }) => (
<p className="mb-2 last:mb-0">{children}</p>
),
ul: ({ children }) => (
<ul className="list-disc pl-4 mb-2">{children}</ul>
),
ol: ({ children }) => (
<ol className="list-decimal pl-4 mb-2">{children}</ol>
),
li: ({ children }) => <li className="mb-1">{children}</li>,
code: ({ children, className }) => {
const isInline = !className;
return isInline ? (
<code className="bg-muted-foreground/20 px-1 py-0.5 rounded text-xs">
{children}
</code>
) : (
<code className="block bg-muted-foreground/20 p-2 rounded text-xs overflow-x-auto">
{children}
</code>
);
},
}}
>
{content}
</ReactMarkdown>
</div>
)}
</div>
</div>
);
}