inital working version

This commit is contained in:
Stefan Hardegger
2025-07-22 21:49:40 +02:00
parent bebb799784
commit 59d29dceaf
98 changed files with 8027 additions and 856 deletions

View File

@@ -0,0 +1,276 @@
'use client';
import { useState, useEffect } from 'react';
import AppLayout from '../../components/layout/AppLayout';
import { useTheme } from '../../lib/theme';
import Button from '../../components/ui/Button';
type FontFamily = 'serif' | 'sans' | 'mono';
type FontSize = 'small' | 'medium' | 'large' | 'extra-large';
type ReadingWidth = 'narrow' | 'medium' | 'wide';
interface Settings {
theme: 'light' | 'dark';
fontFamily: FontFamily;
fontSize: FontSize;
readingWidth: ReadingWidth;
}
const defaultSettings: Settings = {
theme: 'light',
fontFamily: 'serif',
fontSize: 'medium',
readingWidth: 'medium',
};
export default function SettingsPage() {
const { theme, setTheme } = useTheme();
const [settings, setSettings] = useState<Settings>(defaultSettings);
const [saved, setSaved] = useState(false);
// Load settings from localStorage on mount
useEffect(() => {
const savedSettings = localStorage.getItem('storycove-settings');
if (savedSettings) {
try {
const parsed = JSON.parse(savedSettings);
setSettings({ ...defaultSettings, ...parsed, theme });
} catch (error) {
console.error('Failed to parse saved settings:', error);
setSettings({ ...defaultSettings, theme });
}
} else {
setSettings({ ...defaultSettings, theme });
}
}, [theme]);
// Save settings to localStorage
const saveSettings = () => {
localStorage.setItem('storycove-settings', JSON.stringify(settings));
// Apply theme change
setTheme(settings.theme);
// Apply font settings to CSS custom properties
const root = document.documentElement;
const fontFamilyMap = {
serif: 'Georgia, Times, serif',
sans: 'Inter, system-ui, sans-serif',
mono: 'Monaco, Consolas, monospace',
};
const fontSizeMap = {
small: '14px',
medium: '16px',
large: '18px',
'extra-large': '20px',
};
const readingWidthMap = {
narrow: '600px',
medium: '800px',
wide: '1000px',
};
root.style.setProperty('--reading-font-family', fontFamilyMap[settings.fontFamily]);
root.style.setProperty('--reading-font-size', fontSizeMap[settings.fontSize]);
root.style.setProperty('--reading-max-width', readingWidthMap[settings.readingWidth]);
setSaved(true);
setTimeout(() => setSaved(false), 2000);
};
const updateSetting = <K extends keyof Settings>(key: K, value: Settings[K]) => {
setSettings(prev => ({ ...prev, [key]: value }));
};
return (
<AppLayout>
<div className="max-w-2xl mx-auto space-y-8">
<div>
<h1 className="text-3xl font-bold theme-header">Settings</h1>
<p className="theme-text mt-2">
Customize your StoryCove reading experience
</p>
</div>
<div className="space-y-6">
{/* Theme Settings */}
<div className="theme-card theme-shadow rounded-lg p-6">
<h2 className="text-xl font-semibold theme-header mb-4">Appearance</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium theme-header mb-2">
Theme
</label>
<div className="flex gap-4">
<button
onClick={() => updateSetting('theme', 'light')}
className={`px-4 py-2 rounded-lg border transition-colors ${
settings.theme === 'light'
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
Light
</button>
<button
onClick={() => updateSetting('theme', 'dark')}
className={`px-4 py-2 rounded-lg border transition-colors ${
settings.theme === 'dark'
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
🌙 Dark
</button>
</div>
</div>
</div>
</div>
{/* Reading Settings */}
<div className="theme-card theme-shadow rounded-lg p-6">
<h2 className="text-xl font-semibold theme-header mb-4">Reading Experience</h2>
<div className="space-y-6">
{/* Font Family */}
<div>
<label className="block text-sm font-medium theme-header mb-2">
Font Family
</label>
<div className="flex gap-4 flex-wrap">
<button
onClick={() => updateSetting('fontFamily', 'serif')}
className={`px-4 py-2 rounded-lg border transition-colors font-serif ${
settings.fontFamily === 'serif'
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
Serif
</button>
<button
onClick={() => updateSetting('fontFamily', 'sans')}
className={`px-4 py-2 rounded-lg border transition-colors font-sans ${
settings.fontFamily === 'sans'
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
Sans Serif
</button>
<button
onClick={() => updateSetting('fontFamily', 'mono')}
className={`px-4 py-2 rounded-lg border transition-colors font-mono ${
settings.fontFamily === 'mono'
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
Monospace
</button>
</div>
</div>
{/* Font Size */}
<div>
<label className="block text-sm font-medium theme-header mb-2">
Font Size
</label>
<div className="flex gap-4 flex-wrap">
{(['small', 'medium', 'large', 'extra-large'] as FontSize[]).map((size) => (
<button
key={size}
onClick={() => updateSetting('fontSize', size)}
className={`px-4 py-2 rounded-lg border transition-colors capitalize ${
settings.fontSize === size
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
{size.replace('-', ' ')}
</button>
))}
</div>
</div>
{/* Reading Width */}
<div>
<label className="block text-sm font-medium theme-header mb-2">
Reading Width
</label>
<div className="flex gap-4">
{(['narrow', 'medium', 'wide'] as ReadingWidth[]).map((width) => (
<button
key={width}
onClick={() => updateSetting('readingWidth', width)}
className={`px-4 py-2 rounded-lg border transition-colors capitalize ${
settings.readingWidth === width
? 'theme-accent-bg text-white border-transparent'
: 'theme-card theme-text theme-border hover:border-gray-400'
}`}
>
{width}
</button>
))}
</div>
</div>
</div>
</div>
{/* Preview */}
<div className="theme-card theme-shadow rounded-lg p-6">
<h2 className="text-xl font-semibold theme-header mb-4">Preview</h2>
<div
className="p-4 theme-card border theme-border rounded-lg"
style={{
fontFamily: settings.fontFamily === 'serif' ? 'Georgia, Times, serif'
: settings.fontFamily === 'sans' ? 'Inter, system-ui, sans-serif'
: 'Monaco, Consolas, monospace',
fontSize: settings.fontSize === 'small' ? '14px'
: settings.fontSize === 'medium' ? '16px'
: settings.fontSize === 'large' ? '18px'
: '20px',
maxWidth: settings.readingWidth === 'narrow' ? '600px'
: settings.readingWidth === 'medium' ? '800px'
: '1000px',
}}
>
<h3 className="text-xl font-bold theme-header mb-2">Sample Story Title</h3>
<p className="theme-text mb-4">by Sample Author</p>
<p className="theme-text leading-relaxed">
This is how your story text will look with the current settings.
The quick brown fox jumps over the lazy dog. Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua.
</p>
</div>
</div>
{/* Actions */}
<div className="flex justify-end gap-4">
<Button
variant="ghost"
onClick={() => {
setSettings({ ...defaultSettings, theme });
}}
>
Reset to Defaults
</Button>
<Button
onClick={saveSettings}
className={saved ? 'bg-green-600 hover:bg-green-700' : ''}
>
{saved ? '✓ Saved!' : 'Save Settings'}
</Button>
</div>
</div>
</div>
</AppLayout>
);
}