"use client" import type React from "react" import { useState, useRef, useEffect } from "react" import { useColorPalette } from "@/contexts/color-context" import { useMaterial } from "@/contexts/material-context" import { useDesign, availableFonts } from "@/contexts/design-context" import { useTheme } from "next-themes" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" import { CheckIcon } from "@heroicons/react/24/outline" import { ExportButtons } from "./export-buttons" import { XMarkIcon } from "@heroicons/react/24/outline" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" // Preset colors for the color picker const presetColors = [ { name: "Slack Blue", value: "#1D9BD1" }, { name: "Discord Blurple", value: "#5865F2" }, { name: "Spotify Green", value: "#1DB954" }, { name: "Notion Red", value: "#E03E3E" }, { name: "Figma Purple", value: "#A259FF" }, { name: "Linear Pink", value: "#F687B3" }, { name: "GitHub Green", value: "#2DA44E" }, { name: "Vercel Cyan", value: "#00B8D9" }, { name: "Stripe Purple", value: "#635BFF" }, { name: "Raycast Orange", value: "#FF6363" }, { name: "Twitter Blue", value: "#1DA1F2" }, { name: "Tailwind Teal", value: "#0EA5E9" }, ] // Fixed border radius for UI components const UI_BORDER_RADIUS = "6px" // Default UI font that won't change with font selection const UI_FONT = "Inter, sans-serif" // Common fonts to prioritize const COMMON_FONTS = [ { name: "Inter", value: "Inter, sans-serif" }, { name: "Arial", value: "Arial, sans-serif" }, { name: "Helvetica", value: "Helvetica, sans-serif" }, { name: "Roboto", value: "Roboto, sans-serif" }, { name: "Open Sans", value: "Open Sans, sans-serif" }, { name: "Poppins", value: "Poppins, sans-serif" }, ] export function RightSidebar() { const { primaryColor, setPrimaryColor } = useColorPalette() const { materials, selectedMaterial, setSelectedMaterial } = useMaterial() const { borderRadius, setBorderRadius, font, setFont } = useDesign() const [isColorPickerOpen, setIsColorPickerOpen] = useState(false) const [inputColor, setInputColor] = useState(primaryColor) const colorInputRef = useRef(null) const { setTheme, theme } = useTheme() const [mounted, setMounted] = useState(false) const [renderedFonts, setRenderedFonts] = useState>(new Set()) const [selectedFontName, setSelectedFontName] = useState(() => { // Find the font name based on the current font value const fontObj = availableFonts.find((f) => typeof f === "object" && f.value === font) return fontObj ? fontObj.name : "Inter" }) // Handle mounting for theme useEffect(() => { setMounted(true) }, []) // Update input color when primary color changes useEffect(() => { setInputColor(primaryColor) }, [primaryColor]) // Initialize with common fonts const initializedRef = useRef(false) // Initialize with common fonts - using a ref to ensure it only runs once const hasInitializedRef = useRef(false) useEffect(() => { if (mounted && !hasInitializedRef.current) { hasInitializedRef.current = true // Set default border radius only once setBorderRadius(6) // Initialize fonts const initialFonts = new Set() COMMON_FONTS.forEach((font) => { initialFonts.add(typeof font === "object" ? font.value : font) }) // Add the currently selected font initialFonts.add(font) setRenderedFonts(initialFonts) } }, [mounted]) // Only depend on mounted state // Function to handle color change from the color picker const handleColorPickerChange = (e: React.ChangeEvent) => { const newColor = e.target.value setInputColor(newColor) setPrimaryColor(newColor) } // Function to handle color input change const handleColorInputChange = (e: React.ChangeEvent) => { const newColor = e.target.value setInputColor(newColor) // Validate if it's a valid hex color if (/^#([0-9A-F]{3}){1,2}$/i.test(newColor)) { setPrimaryColor(newColor) } } // Function to handle color input blur const handleColorInputBlur = () => { // Ensure the color is valid, if not revert to the current primary color if (!/^#([0-9A-F]{3}){1,2}$/i.test(inputColor)) { setInputColor(primaryColor) } else { setPrimaryColor(inputColor) } } // Function to handle preset color selection const handlePresetColorSelect = (color: string) => { setInputColor(color) setPrimaryColor(color) } // Function to handle font selection const handleFontSelect = (value: string) => { // Find the font object by value const fontObj = availableFonts.find((f) => typeof f === "object" && f.value === value) if (fontObj && typeof fontObj === "object") { setFont(fontObj.value) setSelectedFontName(fontObj.name) } else { setFont(value) setSelectedFontName(value) } } // Get theme-aware selection border color const getSelectionBorderColor = (opacity = 0.9) => { return theme === "dark" ? `rgba(255, 255, 255, ${opacity})` : `rgba(0, 0, 0, ${opacity})` } // Get theme-aware hover border color with 36% opacity const getHoverBorderColor = () => { return theme === "dark" ? "rgba(255, 255, 255, 0.36)" : "rgba(0, 0, 0, 0.36)" } // Get theme-aware selection background color const getSelectionBackgroundColor = () => { return theme === "dark" ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)" } // Material tile border radius - consistent value for all material tiles const MATERIAL_TILE_RADIUS = "8px" if (!mounted) { return null } // Function to handle color picker button click const handleColorPickerClick = () => { setIsColorPickerOpen(!isColorPickerOpen) } const Separator = () =>
return (
{/* Close button for mobile */} {/* Rest of the sidebar content */}
{/* Properties Card */} {/* Color Section */}

Color

Preset Colors
{presetColors.map((color) => ( ))}
{/* Separator */} {/* Material Section */}

Material

{materials.map((material) => { // Determine background and styling based on material type and current theme const isGlass = material.name === "Glass" const isSelected = selectedMaterial?.name === material.name // Get theme-aware styling const bgColor = theme === "dark" ? isGlass ? "rgba(17, 17, 17, 0.7)" : "#111111" : isGlass ? "rgba(255, 255, 255, 0.7)" : "#ffffff" return ( ) })}
{/* Separator */} {/* Roundedness Section */}

Roundedness

{[0, 6, 12, 20].map((value) => ( setBorderRadius(value)} className={cn( "h-8 w-10 cursor-pointer transition-all relative group", borderRadius === value ? "shadow-md" : "border border-border", )} style={{ borderRadius: `${value}px`, border: borderRadius === value ? `2px solid ${getSelectionBorderColor()}` : undefined, boxShadow: borderRadius === value ? `0 0 0 1px ${getSelectionBackgroundColor()}` : undefined, transition: "all 0.2s ease-in-out", }} > {/* Number value label */}
{value === 0 ? "0" : (value / 16).toFixed(1)}
{/* Hover effect - using group hover instead of CSS hover */} {borderRadius !== value && (
)} ))}
{/* Separator */} {/* Font Section */}

Font

{/* Export Buttons at the bottom */}
) } function cn(...classes: (string | boolean | undefined)[]) { return classes.filter(Boolean).join(" ") }