feat: add web frontend directory structure
- Create web/src/ for TypeScript source files - Create web/static/ for compiled assets and runtime files - Move input.css and style.css to web/static/ - Add toast.ts - Functional toast notification system - Add theme.ts - Functional theme management system - All code uses functional programming (no classes, no OOP) - TypeScript provides full type safety Separates frontend code from backend for better organization.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
// Theme management functionality
|
||||
|
||||
type ThemeType =
|
||||
| 'tokyo-night'
|
||||
| 'dracula'
|
||||
| 'nord'
|
||||
| 'solarized-dark'
|
||||
| 'monokai'
|
||||
| 'one-dark-pro'
|
||||
| 'material-dark'
|
||||
| 'catppuccin-mocha'
|
||||
| 'catppuccin-macchiato'
|
||||
| 'catppuccin-frappe'
|
||||
| 'catppuccin-latte';
|
||||
|
||||
const DEFAULT_THEME: ThemeType = 'tokyo-night';
|
||||
const THEME_STORAGE_KEY = 'theme';
|
||||
const TOKEN_STORAGE_KEY = 'token';
|
||||
|
||||
// Apply theme to document body
|
||||
const applyTheme = (theme: ThemeType): void => {
|
||||
document.body.className = `theme-${theme}`;
|
||||
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
||||
};
|
||||
|
||||
// Load theme from localStorage or use default
|
||||
const loadTheme = (): void => {
|
||||
const storedTheme = localStorage.getItem(THEME_STORAGE_KEY) as ThemeType | null;
|
||||
const theme = storedTheme || DEFAULT_THEME;
|
||||
applyTheme(theme);
|
||||
};
|
||||
|
||||
// Handle theme change from user selection
|
||||
const changeTheme = async (): Promise<void> => {
|
||||
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
|
||||
if (!themeSelect) return;
|
||||
|
||||
const theme = themeSelect.value as ThemeType;
|
||||
applyTheme(theme);
|
||||
|
||||
// Save to server if logged in
|
||||
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/theme', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ theme })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('Theme save failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Theme save failed', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load user's theme from server if logged in
|
||||
const loadUserTheme = async (): Promise<void> => {
|
||||
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
||||
if (!token) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/profile', {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.theme) {
|
||||
applyTheme(data.theme);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Silently fail - user will get default theme
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize theme system
|
||||
const initializeTheme = (): void => {
|
||||
loadTheme();
|
||||
loadUserTheme();
|
||||
|
||||
// Set theme select value to current theme
|
||||
const themeSelect = document.getElementById('theme-select') as HTMLSelectElement;
|
||||
if (themeSelect) {
|
||||
const currentTheme = localStorage.getItem(THEME_STORAGE_KEY) || DEFAULT_THEME;
|
||||
themeSelect.value = currentTheme;
|
||||
}
|
||||
|
||||
// Setup smooth scroll for anchor links
|
||||
setupSmoothScroll();
|
||||
};
|
||||
|
||||
// Setup smooth scrolling for anchor links
|
||||
const setupSmoothScroll = (): void => {
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const href = anchor.getAttribute('href');
|
||||
if (!href) return;
|
||||
|
||||
const target = document.querySelector(href);
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Auto-initialize when DOM is ready
|
||||
if (typeof document !== 'undefined') {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initializeTheme);
|
||||
} else {
|
||||
initializeTheme();
|
||||
}
|
||||
}
|
||||
|
||||
// Make changeTheme available globally for HTML onchange attribute
|
||||
(window as any).changeTheme = changeTheme;
|
||||
Reference in New Issue
Block a user