import { useState } from 'react' import { ThemeConfig, ThemeVariant } from '@/types/project' import { Card } from '@/components/ui/card' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Slider } from '@/components/ui/slider' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { PaintBrush, Sparkle, Plus, Trash, Moon, Sun, Palette } from '@phosphor-icons/react' import { AIService } from '@/lib/ai-service' import { toast } from 'sonner' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog' interface StyleDesignerProps { theme: ThemeConfig onThemeChange: (theme: ThemeConfig | ((current: ThemeConfig) => ThemeConfig)) => void } export function StyleDesigner({ theme, onThemeChange }: StyleDesignerProps) { const [newColorName, setNewColorName] = useState('') const [newColorValue, setNewColorValue] = useState('#000000') const [customColorDialogOpen, setCustomColorDialogOpen] = useState(false) const [newVariantDialogOpen, setNewVariantDialogOpen] = useState(false) const [newVariantName, setNewVariantName] = useState('') if (!theme.variants || theme.variants.length === 0) { return (

Theme configuration is invalid or missing

) } const activeVariant = theme.variants.find((v) => v.id === theme.activeVariantId) || theme.variants[0] const updateTheme = (updates: Partial) => { onThemeChange((current) => ({ ...current, ...updates })) } const updateActiveVariantColors = (colorUpdates: Partial) => { onThemeChange((current) => ({ ...current, variants: (current.variants || []).map((v) => v.id === current.activeVariantId ? { ...v, colors: { ...v.colors, ...colorUpdates } } : v ), })) } const addCustomColor = () => { if (!newColorName.trim()) { toast.error('Please enter a color name') return } updateActiveVariantColors({ customColors: { ...activeVariant.colors.customColors, [newColorName]: newColorValue, }, }) setNewColorName('') setNewColorValue('#000000') setCustomColorDialogOpen(false) toast.success(`Added custom color: ${newColorName}`) } const removeCustomColor = (colorName: string) => { const { [colorName]: _, ...remainingColors } = activeVariant.colors.customColors updateActiveVariantColors({ customColors: remainingColors, }) toast.success(`Removed custom color: ${colorName}`) } const addVariant = () => { if (!newVariantName.trim()) { toast.error('Please enter a variant name') return } const newVariant: ThemeVariant = { id: `variant-${Date.now()}`, name: newVariantName, colors: { ...activeVariant.colors, customColors: {} }, } onThemeChange((current) => ({ ...current, variants: [...(current.variants || []), newVariant], activeVariantId: newVariant.id, })) setNewVariantName('') setNewVariantDialogOpen(false) toast.success(`Added theme variant: ${newVariantName}`) } const deleteVariant = (variantId: string) => { if (!theme.variants || theme.variants.length <= 1) { toast.error('Cannot delete the last theme variant') return } onThemeChange((current) => { const remainingVariants = (current.variants || []).filter((v) => v.id !== variantId) return { ...current, variants: remainingVariants, activeVariantId: current.activeVariantId === variantId ? remainingVariants[0].id : current.activeVariantId, } }) toast.success('Theme variant deleted') } const duplicateVariant = (variantId: string) => { const variantToDuplicate = (theme.variants || []).find((v) => v.id === variantId) if (!variantToDuplicate) return const newVariant: ThemeVariant = { id: `variant-${Date.now()}`, name: `${variantToDuplicate.name} Copy`, colors: { ...variantToDuplicate.colors, customColors: { ...variantToDuplicate.colors.customColors } }, } onThemeChange((current) => ({ ...current, variants: [...(current.variants || []), newVariant], })) toast.success('Theme variant duplicated') } const generateThemeWithAI = async () => { const description = prompt('Describe the visual style you want (e.g., "modern and professional", "vibrant and playful"):') if (!description) return try { toast.info('Generating theme with AI...') const generatedTheme = await AIService.generateThemeFromDescription(description) if (generatedTheme) { onThemeChange((current) => ({ ...current, ...generatedTheme })) toast.success('Theme generated successfully!') } else { toast.error('AI generation failed. Please try again.') } } catch (error) { toast.error('Failed to generate theme') console.error(error) } } const renderColorInput = (label: string, colorKey: keyof typeof activeVariant.colors, excludeCustom = true) => { if (excludeCustom && colorKey === 'customColors') return null const value = activeVariant.colors[colorKey] as string return (
updateActiveVariantColors({ [colorKey]: e.target.value })} className="w-20 h-10 cursor-pointer" /> updateActiveVariantColors({ [colorKey]: e.target.value })} className="flex-1 font-mono" />
) } return (

Theme Designer

Create and customize multiple theme variants with custom colors

Theme Variants

{theme.variants.map((variant) => (
))}
Standard Colors Extended Colors Custom Colors ({Object.keys(activeVariant.colors.customColors).length})
{renderColorInput('Primary Color', 'primaryColor')} {renderColorInput('Secondary Color', 'secondaryColor')} {renderColorInput('Error Color', 'errorColor')} {renderColorInput('Warning Color', 'warningColor')} {renderColorInput('Success Color', 'successColor')}
{renderColorInput('Background', 'background')} {renderColorInput('Surface', 'surface')} {renderColorInput('Text', 'text')} {renderColorInput('Text Secondary', 'textSecondary')} {renderColorInput('Border', 'border')}

Add custom colors for your specific needs

{Object.keys(activeVariant.colors.customColors).length === 0 ? (

No custom colors yet

) : (
{Object.entries(activeVariant.colors.customColors).map(([name, value]) => (
updateActiveVariantColors({ customColors: { ...activeVariant.colors.customColors, [name]: e.target.value, }, }) } className="w-20 h-10 cursor-pointer" /> updateActiveVariantColors({ customColors: { ...activeVariant.colors.customColors, [name]: e.target.value, }, }) } className="flex-1 font-mono" />
))}
)}

Typography

updateTheme({ fontFamily: e.target.value })} placeholder="Roboto, Arial, sans-serif" />
{theme.fontSize.small}px
updateTheme({ fontSize: { ...theme.fontSize, small: value }, }) } min={10} max={20} step={1} />
{theme.fontSize.medium}px
updateTheme({ fontSize: { ...theme.fontSize, medium: value }, }) } min={12} max={24} step={1} />
{theme.fontSize.large}px
updateTheme({ fontSize: { ...theme.fontSize, large: value }, }) } min={16} max={48} step={1} />

Spacing & Shape

{theme.spacing}px
updateTheme({ spacing: value })} min={4} max={16} step={1} />

Material UI multiplies this value (e.g., spacing(2) = {theme.spacing * 2}px)

{theme.borderRadius}px
updateTheme({ borderRadius: value }) } min={0} max={24} step={1} />

Preview - {activeVariant.name} Mode

Primary
Secondary
Error
Warning
Success
{Object.entries(activeVariant.colors.customColors).map(([name, color]) => (
{name}
))}

Large Text Sample

Medium Text Sample

Small Text Sample (Secondary)

Add Custom Color Create a custom color for your theme variant
setNewColorName(e.target.value)} placeholder="e.g., accent, highlight, brand" />
setNewColorValue(e.target.value)} className="w-20 h-10 cursor-pointer" /> setNewColorValue(e.target.value)} className="flex-1 font-mono" />
Add Theme Variant Create a new theme variant based on the current one
setNewVariantName(e.target.value)} placeholder="e.g., High Contrast, Colorblind Friendly" />
) }