feat: implement comprehensive theming system
- Add ThemeContext with support for dark, gray, light, and custom themes - Create theme switching UI in Settings with theme selector - Add custom color editor for custom theme mode - Update styles.css with theme-specific CSS variables - Add theme storage API methods for persistence - Update syntax highlighting to match selected theme - Wrap App with ThemeProvider for global theme access The theming system allows users to switch between predefined themes or create their own custom theme with live color editing.
This commit is contained in:
@@ -14,6 +14,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import {
|
||||
api,
|
||||
type ClaudeSettings,
|
||||
@@ -25,6 +26,7 @@ import { ClaudeVersionSelector } from "./ClaudeVersionSelector";
|
||||
import { StorageTab } from "./StorageTab";
|
||||
import { HooksEditor } from "./HooksEditor";
|
||||
import { SlashCommandsManager } from "./SlashCommandsManager";
|
||||
import { useTheme } from "@/hooks";
|
||||
|
||||
interface SettingsProps {
|
||||
/**
|
||||
@@ -77,6 +79,9 @@ export const Settings: React.FC<SettingsProps> = ({
|
||||
const [userHooksChanged, setUserHooksChanged] = useState(false);
|
||||
const getUserHooks = React.useRef<(() => any) | null>(null);
|
||||
|
||||
// Theme hook
|
||||
const { theme, setTheme, customColors, setCustomColors } = useTheme();
|
||||
|
||||
// Load settings on mount
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
@@ -375,6 +380,155 @@ export const Settings: React.FC<SettingsProps> = ({
|
||||
<h3 className="text-base font-semibold mb-4">General Settings</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Theme Selector */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="theme">Theme</Label>
|
||||
<Select
|
||||
value={theme}
|
||||
onValueChange={(value) => setTheme(value as any)}
|
||||
>
|
||||
<SelectTrigger id="theme" className="w-full">
|
||||
<SelectValue placeholder="Select a theme" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="gray">Gray</SelectItem>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="custom">Custom</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Choose your preferred color theme for the interface
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Custom Color Editor */}
|
||||
{theme === 'custom' && (
|
||||
<div className="space-y-4 p-4 border rounded-lg bg-muted/20">
|
||||
<h4 className="text-sm font-medium">Custom Theme Colors</h4>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{/* Background Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-background" className="text-xs">Background</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-background"
|
||||
type="text"
|
||||
value={customColors.background}
|
||||
onChange={(e) => setCustomColors({ background: e.target.value })}
|
||||
placeholder="oklch(0.12 0.01 240)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.background }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Foreground Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-foreground" className="text-xs">Foreground</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-foreground"
|
||||
type="text"
|
||||
value={customColors.foreground}
|
||||
onChange={(e) => setCustomColors({ foreground: e.target.value })}
|
||||
placeholder="oklch(0.98 0.01 240)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.foreground }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Primary Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-primary" className="text-xs">Primary</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-primary"
|
||||
type="text"
|
||||
value={customColors.primary}
|
||||
onChange={(e) => setCustomColors({ primary: e.target.value })}
|
||||
placeholder="oklch(0.98 0.01 240)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.primary }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-card" className="text-xs">Card</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-card"
|
||||
type="text"
|
||||
value={customColors.card}
|
||||
onChange={(e) => setCustomColors({ card: e.target.value })}
|
||||
placeholder="oklch(0.14 0.01 240)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.card }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Accent Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-accent" className="text-xs">Accent</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-accent"
|
||||
type="text"
|
||||
value={customColors.accent}
|
||||
onChange={(e) => setCustomColors({ accent: e.target.value })}
|
||||
placeholder="oklch(0.16 0.01 240)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.accent }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Destructive Color */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="color-destructive" className="text-xs">Destructive</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="color-destructive"
|
||||
type="text"
|
||||
value={customColors.destructive}
|
||||
onChange={(e) => setCustomColors({ destructive: e.target.value })}
|
||||
placeholder="oklch(0.6 0.2 25)"
|
||||
className="font-mono text-xs"
|
||||
/>
|
||||
<div
|
||||
className="w-10 h-10 rounded border"
|
||||
style={{ backgroundColor: customColors.destructive }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Use CSS color values (hex, rgb, oklch, etc.). Changes apply immediately.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Include Co-authored By */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5 flex-1">
|
||||
|
Reference in New Issue
Block a user