mirror of
https://github.com/handsomezhuzhu/AeroStart.git
synced 2026-02-20 20:10:15 +00:00
✨ feat: initialize AeroStart browser start page project
Implement a modern, customizable browser start page with comprehensive features: - Multi-theme support with 8 preset color schemes - Custom wallpaper system supporting images and videos with multiple fit modes - Integrated search functionality with 5 major search engines (Google, Baidu, Bing, DuckDuckGo, Bilibili) - Real-time clock component with 12/24 hour format options - Dynamic background blur effect during search for enhanced focus - Complete i18n system with English and Chinese language support - Responsive design with smooth animations and transitions - Local storage integration for persistent user preferences - Context menu system for quick settings access - Toast notification system for user feedback - Error boundary for robust error handling Tech Stack: - React 19 with TypeScript - Vite 6 for build tooling - Tailwind CSS for styling - Local storage for data persistence Project Structure: - Core components: Clock, SearchBox, SettingsModal, ThemeSettings, WallpaperManager - Utility modules: storage management, search suggestions - Context providers: Toast notifications, i18n - Type definitions and constants configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
186
components/ThemeSettings.tsx
Normal file
186
components/ThemeSettings.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
|
||||
import React from 'react';
|
||||
import { CheckIcon } from './Icons';
|
||||
import { UserSettings, Language } from '../types';
|
||||
import { THEMES } from '../constants';
|
||||
import { useTranslation } from '../i18n';
|
||||
|
||||
interface ThemeSettingsProps {
|
||||
settings: UserSettings;
|
||||
onUpdateSettings: (newSettings: UserSettings) => void;
|
||||
}
|
||||
|
||||
const ThemeSettings: React.FC<ThemeSettingsProps> = ({ settings, onUpdateSettings }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const toggleSeconds = () => {
|
||||
onUpdateSettings({ ...settings, showSeconds: !settings.showSeconds });
|
||||
};
|
||||
|
||||
const toggleMaskBlur = () => {
|
||||
onUpdateSettings({ ...settings, enableMaskBlur: !settings.enableMaskBlur });
|
||||
};
|
||||
|
||||
const toggle24Hour = () => {
|
||||
onUpdateSettings({ ...settings, use24HourFormat: !settings.use24HourFormat });
|
||||
};
|
||||
|
||||
const toggleSearchHistory = () => {
|
||||
onUpdateSettings({ ...settings, enableSearchHistory: !settings.enableSearchHistory });
|
||||
};
|
||||
|
||||
const handleBlurChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onUpdateSettings({ ...settings, backgroundBlur: parseInt(e.target.value) });
|
||||
};
|
||||
|
||||
const handleOpacityChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onUpdateSettings({ ...settings, searchOpacity: parseFloat(e.target.value) });
|
||||
};
|
||||
|
||||
const handleThemeChange = (colorHex: string) => {
|
||||
onUpdateSettings({ ...settings, themeColor: colorHex });
|
||||
};
|
||||
|
||||
const handleLanguageChange = (lang: Language) => {
|
||||
onUpdateSettings({ ...settings, language: lang });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Language Selection */}
|
||||
<div className="space-y-3">
|
||||
<span className="text-white/80 font-light block">{t.language}</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => handleLanguageChange('en')}
|
||||
className={`
|
||||
flex-1 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200
|
||||
${settings.language === 'en'
|
||||
? 'bg-white text-black'
|
||||
: 'bg-white/5 text-white/60 hover:bg-white/10 hover:text-white'}
|
||||
`}
|
||||
>
|
||||
{t.english}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleLanguageChange('zh')}
|
||||
className={`
|
||||
flex-1 px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200
|
||||
${settings.language === 'zh'
|
||||
? 'bg-white text-black'
|
||||
: 'bg-white/5 text-white/60 hover:bg-white/10 hover:text-white'}
|
||||
`}
|
||||
>
|
||||
{t.chinese}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Theme color */}
|
||||
<div className="space-y-3">
|
||||
<span className="text-white/80 font-light block">{t.themeColor}</span>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{THEMES.map((theme) => (
|
||||
<button
|
||||
key={theme.hex}
|
||||
onClick={() => handleThemeChange(theme.hex)}
|
||||
className={`
|
||||
w-8 h-8 rounded-full flex items-center justify-center transition-all duration-300
|
||||
${settings.themeColor === theme.hex ? 'ring-2 ring-white scale-110' : 'hover:scale-110 opacity-80 hover:opacity-100'}
|
||||
`}
|
||||
style={{ backgroundColor: theme.hex }}
|
||||
title={theme.name}
|
||||
>
|
||||
{settings.themeColor === theme.hex && (
|
||||
<CheckIcon className="w-4 h-4 text-white drop-shadow-md" />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Toggle settings */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-white/80 font-light">{t.showSeconds}</span>
|
||||
<button
|
||||
onClick={toggleSeconds}
|
||||
className="w-12 h-6 rounded-full transition-colors duration-300 relative bg-white/10"
|
||||
style={{ backgroundColor: settings.showSeconds ? settings.themeColor : undefined }}
|
||||
>
|
||||
<div className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform duration-300 shadow-md ${settings.showSeconds ? 'left-7' : 'left-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-white/80 font-light">{t.use24HourFormat}</span>
|
||||
<button
|
||||
onClick={toggle24Hour}
|
||||
className="w-12 h-6 rounded-full transition-colors duration-300 relative bg-white/10"
|
||||
style={{ backgroundColor: settings.use24HourFormat ? settings.themeColor : undefined }}
|
||||
>
|
||||
<div className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform duration-300 shadow-md ${settings.use24HourFormat ? 'left-7' : 'left-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-white/80 font-light">{t.maskBlurEffect}</span>
|
||||
<button
|
||||
onClick={toggleMaskBlur}
|
||||
className="w-12 h-6 rounded-full transition-colors duration-300 relative bg-white/10"
|
||||
style={{ backgroundColor: settings.enableMaskBlur ? settings.themeColor : undefined }}
|
||||
>
|
||||
<div className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform duration-300 shadow-md ${settings.enableMaskBlur ? 'left-7' : 'left-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-white/80 font-light">{t.searchHistory}</span>
|
||||
<button
|
||||
onClick={toggleSearchHistory}
|
||||
className="w-12 h-6 rounded-full transition-colors duration-300 relative bg-white/10"
|
||||
style={{ backgroundColor: settings.enableSearchHistory ? settings.themeColor : undefined }}
|
||||
>
|
||||
<div className={`absolute top-1 w-4 h-4 rounded-full bg-white transition-transform duration-300 shadow-md ${settings.enableSearchHistory ? 'left-7' : 'left-1'}`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Background blur slider */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between text-sm text-white/60 font-light">
|
||||
<span>{t.backgroundBlur}</span>
|
||||
<span>{settings.backgroundBlur}px</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="20"
|
||||
step="1"
|
||||
value={settings.backgroundBlur}
|
||||
onChange={handleBlurChange}
|
||||
className="w-full h-1 bg-white/20 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Search box opacity slider */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between text-sm text-white/60 font-light">
|
||||
<span>{t.searchBoxOpacity}</span>
|
||||
<span>{Math.round(settings.searchOpacity * 100)}%</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0.1"
|
||||
max="1"
|
||||
step="0.05"
|
||||
value={settings.searchOpacity}
|
||||
onChange={handleOpacityChange}
|
||||
className="w-full h-1 bg-white/20 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeSettings;
|
||||
Reference in New Issue
Block a user