- Notes: per-story rich-text notes panel (right drawer) with TipTap editor, image support, autosave, and full CRUD API - Font picker: 15 Google Fonts selectable from a floating Aa button, persisted to localStorage via --font-body CSS variable - Sticky toolbar: pulled formatting bar out of overflow:hidden wrapper so it sticks below the topbar while scrolling - Prompts: 100 additional built-in prompts (120 total) in a shuffled no-repeat queue; pre-fetch on page load so the AI has time to respond; timeout raised to 45s; error logging + /api/prompts/test debug endpoint; source badge shows whether prompt came from AI or built-in list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
47 lines
1.9 KiB
JavaScript
47 lines
1.9 KiB
JavaScript
import { Routes, Route, Navigate } from 'react-router-dom'
|
|
import { useState, useEffect } from 'react'
|
|
import { getUser } from './lib/api'
|
|
import { ToastProvider } from './components/Toast'
|
|
import { ConfirmProvider } from './components/ConfirmDialog'
|
|
import Login from './pages/Login'
|
|
import Stories from './pages/Stories'
|
|
import EditorPage from './pages/EditorPage'
|
|
import Admin from './pages/Admin'
|
|
import ThemePicker from './components/ThemePicker'
|
|
import FontPicker, { FONTS } from './components/FontPicker'
|
|
|
|
const THEMES = ['grunge', 'gothic', 'forest', 'manuscript', 'noir']
|
|
|
|
export default function App() {
|
|
const [user, setUser] = useState(() => getUser())
|
|
const [theme, setTheme] = useState(() => localStorage.getItem('sw-theme') || 'grunge')
|
|
const [fontId, setFontId] = useState(() => localStorage.getItem('sw-font') || 'lora')
|
|
|
|
useEffect(() => {
|
|
document.documentElement.setAttribute('data-theme', theme)
|
|
localStorage.setItem('sw-theme', theme)
|
|
}, [theme])
|
|
|
|
useEffect(() => {
|
|
const font = FONTS.find(f => f.id === fontId) ?? FONTS[0]
|
|
document.documentElement.style.setProperty('--font-body', font.family)
|
|
localStorage.setItem('sw-font', fontId)
|
|
}, [fontId])
|
|
|
|
return (
|
|
<ToastProvider>
|
|
<ConfirmProvider>
|
|
<Routes>
|
|
<Route path="/login" element={user ? <Navigate to="/" /> : <Login onLogin={setUser} />} />
|
|
<Route path="/admin" element={<Admin />} />
|
|
<Route path="/" element={user ? <Stories onLogout={() => setUser(null)} /> : <Navigate to="/login" />} />
|
|
<Route path="/story/:id" element={user ? <EditorPage /> : <Navigate to="/login" />} />
|
|
<Route path="*" element={<Navigate to="/" />} />
|
|
</Routes>
|
|
{user && <ThemePicker theme={theme} setTheme={setTheme} themes={THEMES} />}
|
|
{user && <FontPicker fontId={fontId} setFont={setFontId} />}
|
|
</ConfirmProvider>
|
|
</ToastProvider>
|
|
)
|
|
}
|