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:
Michael Chihlas
2026-01-28 21:19:57 -05:00
parent 4cee013733
commit 5a0dff1da9
9 changed files with 455 additions and 40 deletions

View 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