mirror of
https://github.com/0xdesign/design-plugin.git
synced 2026-09-19 07:32:24 +08:00
b957873a9b
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>
94 lines
2.7 KiB
TypeScript
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>
|
|
);
|
|
}
|