Add dark mode, export preview, and keyboard navigation
- Add theme store with light/dark/system modes and ThemeToggle component - Prevent flash of wrong theme on initial load via inline script - Add ExportPreviewModal for previewing session exports before download - Add copy-to-clipboard functionality to session export - Implement keyboard shortcuts for tree navigation (1-9 options, Esc back, Enter continue) - Display keyboard hints in tree navigation UI - Fix findNode to safely handle undefined structure parameter - Update page title to "Apoklisis" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
53
frontend/src/store/themeStore.ts
Normal file
53
frontend/src/store/themeStore.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
|
||||
type Theme = 'light' | 'dark' | 'system'
|
||||
|
||||
interface ThemeState {
|
||||
theme: Theme
|
||||
resolvedTheme: 'light' | 'dark'
|
||||
setTheme: (theme: Theme) => void
|
||||
}
|
||||
|
||||
const getSystemTheme = (): 'light' | 'dark' => {
|
||||
if (typeof window === 'undefined') return 'light'
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
const applyTheme = (theme: Theme): 'light' | 'dark' => {
|
||||
const resolved = theme === 'system' ? getSystemTheme() : theme
|
||||
const root = document.documentElement
|
||||
|
||||
if (resolved === 'dark') {
|
||||
root.classList.add('dark')
|
||||
} else {
|
||||
root.classList.remove('dark')
|
||||
}
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
export const useThemeStore = create<ThemeState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
theme: 'system',
|
||||
resolvedTheme: getSystemTheme(),
|
||||
|
||||
setTheme: (theme: Theme) => {
|
||||
const resolvedTheme = applyTheme(theme)
|
||||
set({ theme, resolvedTheme })
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'theme-storage',
|
||||
onRehydrateStorage: () => (state) => {
|
||||
// Apply theme on initial load after rehydration
|
||||
if (state) {
|
||||
applyTheme(state.theme)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
export default useThemeStore
|
||||
Reference in New Issue
Block a user