mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-21 12:53:09 +00:00
feat(ui): add theme switching system with Twitter, Claude, and Neo Brutalism themes
Add a comprehensive theme system allowing users to switch between three distinct visual themes, each supporting both light and dark modes: - Twitter (default): Clean blue design with soft shadows - Claude: Warm beige/cream tones with orange primary accents - Neo Brutalism: Bold colors, hard shadows, 0px border radius New files: - ui/src/hooks/useTheme.ts: Theme state management hook with localStorage persistence for both theme selection and dark mode preference - ui/src/components/ThemeSelector.tsx: Header dropdown with hover preview and color swatches for quick theme switching Modified files: - ui/src/styles/globals.css: Added CSS custom properties for Claude and Neo Brutalism themes with light/dark variants, shadow variables integrated into @theme inline block - ui/src/App.tsx: Integrated useTheme hook and ThemeSelector component - ui/src/components/SettingsModal.tsx: Added theme selection UI with preview swatches and dark mode toggle - ui/index.html: Added DM Sans and Space Mono fonts for Neo Brutalism Features: - Independent theme and dark mode controls - Smooth CSS transitions when switching themes - Theme-specific shadow styles (soft vs hard) - Theme-specific fonts and border radius - Persisted preferences in localStorage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Loader2, AlertCircle } from 'lucide-react'
|
||||
import { Loader2, AlertCircle, Check, Moon, Sun } from 'lucide-react'
|
||||
import { useSettings, useUpdateSettings, useAvailableModels } from '../hooks/useProjects'
|
||||
import { useTheme, THEMES } from '../hooks/useTheme'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -20,6 +21,7 @@ export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
|
||||
const { data: settings, isLoading, isError, refetch } = useSettings()
|
||||
const { data: modelsData } = useAvailableModels()
|
||||
const updateSettings = useUpdateSettings()
|
||||
const { theme, setTheme, darkMode, toggleDarkMode } = useTheme()
|
||||
|
||||
const handleYoloToggle = () => {
|
||||
if (settings && !updateSettings.isPending) {
|
||||
@@ -80,6 +82,77 @@ export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
|
||||
{/* Settings Content */}
|
||||
{settings && !isLoading && (
|
||||
<div className="space-y-6">
|
||||
{/* Theme Selection */}
|
||||
<div className="space-y-3">
|
||||
<Label className="font-medium">Theme</Label>
|
||||
<div className="grid gap-2">
|
||||
{THEMES.map((themeOption) => (
|
||||
<button
|
||||
key={themeOption.id}
|
||||
onClick={() => setTheme(themeOption.id)}
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border-2 transition-colors text-left ${
|
||||
theme === themeOption.id
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:border-primary/50 hover:bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
{/* Color swatches */}
|
||||
<div className="flex gap-0.5 shrink-0">
|
||||
<div
|
||||
className="w-5 h-5 rounded-sm border border-border/50"
|
||||
style={{ backgroundColor: themeOption.previewColors.background }}
|
||||
/>
|
||||
<div
|
||||
className="w-5 h-5 rounded-sm border border-border/50"
|
||||
style={{ backgroundColor: themeOption.previewColors.primary }}
|
||||
/>
|
||||
<div
|
||||
className="w-5 h-5 rounded-sm border border-border/50"
|
||||
style={{ backgroundColor: themeOption.previewColors.accent }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Theme info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="font-medium text-sm">{themeOption.name}</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{themeOption.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Checkmark */}
|
||||
{theme === themeOption.id && (
|
||||
<Check size={18} className="text-primary shrink-0" />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dark Mode Toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="dark-mode" className="font-medium">
|
||||
Dark Mode
|
||||
</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Switch between light and dark appearance
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
id="dark-mode"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={toggleDarkMode}
|
||||
className="gap-2"
|
||||
>
|
||||
{darkMode ? <Sun size={16} /> : <Moon size={16} />}
|
||||
{darkMode ? 'Light' : 'Dark'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<hr className="border-border" />
|
||||
|
||||
{/* YOLO Mode Toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
|
||||
Reference in New Issue
Block a user