Skip to content

Commit 927ea1a

Browse files
Merge pull request #6 from Anurup-R-Krishnan/feature/sanctuary-rebuild-v2-bunnies-pick-theme-4475909853667411811
Feature/sanctuary rebuild v2 bunnies pick theme
2 parents 0a2f79e + bc9a2ed commit 927ea1a

28 files changed

Lines changed: 1878 additions & 873 deletions

apps/web/index.css

Lines changed: 0 additions & 56 deletions
This file was deleted.

apps/web/index.html

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,25 @@
1-
<!doctype html>
2-
<html lang="en" class="scroll-smooth">
3-
<head>
4-
<meta charset="UTF-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6-
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: https://*.clerk.com https://*.clerk.accounts.dev; script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' blob: https://*.clerk.com https://*.clerk.accounts.dev; style-src 'self' 'unsafe-inline' blob: https://fonts.googleapis.com; style-src-elem 'self' 'unsafe-inline' blob: https://fonts.googleapis.com; img-src 'self' blob: data: https:; connect-src 'self' blob: https://*.clerk.com https://*.clerk.accounts.dev wss://*.clerk.com; frame-src 'self' https://*.clerk.com https://*.clerk.accounts.dev; font-src 'self' blob: https://fonts.gstatic.com data:;">
7-
<meta name="description" content="Sanctuary - Your personal reading haven. A beautiful, modern EPUB reader for focused reading." />
8-
<meta name="theme-color" content="#8B7355" />
9-
<meta name="apple-mobile-web-app-capable" content="yes" />
10-
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
11-
<meta name="apple-mobile-web-app-title" content="Sanctuary" />
12-
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
13-
<link rel="apple-touch-icon" href="/icon.svg" />
14-
<title>Sanctuary - Book Reader</title>
15-
<link rel="stylesheet" href="/index.css" />
16-
<link rel="preconnect" href="https://fonts.googleapis.com" />
17-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18-
</head>
19-
<body class="antialiased">
20-
<noscript>
21-
<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:system-ui;background:#faf8f3;color:#2d2a26;padding:2rem;text-align:center;">
22-
<div>
23-
<h1 style="font-size:1.5rem;margin-bottom:0.5rem;">JavaScript Required</h1>
24-
<p style="opacity:0.7;">Please enable JavaScript to use Sanctuary Book Reader.</p>
25-
</div>
26-
</div>
27-
</noscript>
28-
<div id="root"></div>
29-
<script type="module" src="/src/index.tsx"></script>
30-
</body>
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<!-- Preconnect for faster font loading -->
8+
<link rel="preconnect" href="https://fonts.googleapis.com" />
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10+
<!--
11+
Fonts:
12+
- Fredoka: Headings/Logos (chunky, rounded, playful)
13+
- Sniglet: Headings/Logos (alternative playful)
14+
- Courier Prime: Body/UI Text (vintage typewriter)
15+
- Special Elite: Body/UI Text (alternative vintage typewriter)
16+
- Playfair Display: Accents/Quotes (elegant serif)
17+
-->
18+
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&family=Fredoka:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,700;1,400;1,700&family=Sniglet:wght@400;800&family=Special+Elite&display=swap" rel="stylesheet" />
19+
<title>Sanctuary</title>
20+
</head>
21+
<body>
22+
<div id="root"></div>
23+
<script type="module" src="/src/index.tsx"></script>
24+
</body>
3125
</html>

apps/web/src/App.tsx

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import ReaderView from "./components/pages/ReaderView";
2020
import SettingsView from "./components/pages/SettingsView";
2121
import StatsView from "./components/pages/StatsView";
2222
import ClerkAuth from "./components/pages/Auth";
23+
import ScrapbookLayout from "./components/layout/ScrapbookLayout";
2324
import { ReaderErrorBoundary } from "./components/ui/ReaderErrorBoundary";
2425

2526
const App: React.FC = () => {
@@ -53,7 +54,6 @@ const App: React.FC = () => {
5354
}));
5455

5556
// Library & Stats Hooks
56-
// Guest and Clerk users both persist through the API with scoped identities.
5757
const persistent = true;
5858

5959
useBookStoreController({ persistent });
@@ -143,87 +143,81 @@ const App: React.FC = () => {
143143
const root = document.documentElement;
144144
root.classList.toggle("dark", theme === Theme.DARK);
145145
root.classList.toggle("reduce-motion", reduceMotion);
146-
147-
const bgColor = theme === Theme.DARK ? "#0f0e0d" : "#fefcf8";
148-
document.body.style.backgroundColor = bgColor;
149-
document.body.style.transition = reduceMotion ? "none" : "background-color 0.3s ease";
150146
}, [theme, reduceMotion]);
151147

152-
// Render - Explicit Auth Screen (only when user asks to sign in)
148+
// Render - Explicit Auth Screen (Scrapbook Themed)
153149
if (showAuthScreen && !isLoaded) {
154150
return (
155-
<div className="min-h-screen flex flex-col items-center justify-center bg-light-primary dark:bg-dark-primary">
156-
<div className="relative mb-6">
157-
<div className="absolute inset-0 bg-gradient-to-br from-light-accent/20 to-amber-500/20 dark:from-dark-accent/20 dark:to-amber-400/20 rounded-3xl blur-3xl scale-150" />
158-
<div className="relative w-20 h-20 rounded-3xl bg-gradient-to-br from-light-accent to-amber-600 dark:from-dark-accent dark:to-amber-500 flex items-center justify-center shadow-2xl">
159-
<BookOpen className="w-9 h-9 text-white animate-pulse-soft" strokeWidth={1.5} />
160-
</div>
161-
</div>
162-
<div className="text-center space-y-2">
163-
<h2 className="text-xl font-semibold text-light-text dark:text-dark-text">Sanctuary</h2>
164-
<p className="text-sm text-light-text-muted dark:text-dark-text-muted">Preparing your reading sanctuary...</p>
151+
<ScrapbookLayout>
152+
<div className="min-h-screen flex flex-col items-center justify-center pointer-events-none">
153+
<div className="bg-white p-8 rounded-full shadow-scrap-deep animate-pulse-soft border-4 border-scrap-navy pointer-events-auto">
154+
<BookOpen className="w-12 h-12 text-scrap-navy" strokeWidth={2} />
155+
</div>
156+
<p className="mt-6 text-scrap-navy font-head text-xl font-bold bg-scrap-cream px-4 py-1 rounded-lg border border-scrap-navy shadow-sm transform -rotate-2">
157+
Gathering supplies...
158+
</p>
165159
</div>
166-
</div>
160+
</ScrapbookLayout>
167161
);
168162
}
169163

170164
if (showAuthScreen) {
171-
return <ClerkAuth onContinueAsGuest={() => {
172-
setIsGuest(true);
173-
setShowAuthScreen(false);
174-
}} />;
165+
return (
166+
<ScrapbookLayout>
167+
<ClerkAuth onContinueAsGuest={() => {
168+
setIsGuest(true);
169+
setShowAuthScreen(false);
170+
}} />
171+
</ScrapbookLayout>
172+
);
175173
}
176174

177175
const isReader = view === View.READER;
178176

179-
// Render - App
177+
if (isReader && selectedBook) {
178+
return (
179+
<ReaderErrorBoundary onRecover={() => { void handleCloseReader(); }} resetKey={selectedBook.id}>
180+
<ReaderView
181+
book={selectedBook}
182+
onClose={handleCloseReader}
183+
onUpdateProgress={handleReaderProgress}
184+
onAddBookmark={handleAddBookmark}
185+
onRemoveBookmark={handleRemoveBookmark}
186+
getBookContent={getBookContent}
187+
/>
188+
</ReaderErrorBoundary>
189+
);
190+
}
191+
192+
// Render - Main App (Scrapbook Layout)
180193
return (
181-
<div className={`min-h-screen font-sans bg-light-primary dark:bg-dark-primary text-light-text dark:text-dark-text transition-colors duration-300 ${isReader ? "immersive-layout" : "standard-layout"}`}>
194+
<ScrapbookLayout view={view}>
182195
{/* Header */}
183-
{!isReader && (
184-
<Header
185-
theme={theme}
186-
onToggleTheme={toggleTheme}
187-
searchTerm={searchTerm}
188-
onSearch={setSearchTerm}
189-
isGuest={isGuest}
190-
onShowLogin={isGuest ? handleShowLogin : undefined}
191-
onSignOut={isSignedIn ? handleSignOut : undefined}
192-
userEmail={user?.primaryEmailAddress?.emailAddress}
193-
userImage={user?.imageUrl}
194-
/>
195-
)}
196-
197-
{/* Main Content */}
198-
<main className={`relative ${isReader ? "reader-main" : "standard-main"}`}>
199-
<div className={`${isReader ? "" : "page-shell animate-fadeIn"}`}>
196+
<Header
197+
theme={theme}
198+
onToggleTheme={toggleTheme}
199+
searchTerm={searchTerm}
200+
onSearch={setSearchTerm}
201+
isGuest={isGuest}
202+
onShowLogin={isGuest ? handleShowLogin : undefined}
203+
onSignOut={isSignedIn ? handleSignOut : undefined}
204+
userEmail={user?.primaryEmailAddress?.emailAddress}
205+
userImage={user?.imageUrl}
206+
/>
207+
208+
{/* Main Content Area */}
209+
<main className="flex-1 w-full max-w-7xl mx-auto px-4 pb-32">
200210
{view === View.LIBRARY && (
201-
<LibraryGrid
202-
onSelectBook={handleSelectBook}
203-
/>
211+
<LibraryGrid onSelectBook={handleSelectBook} />
204212
)}
213+
{/* We keep Stats and Settings as is for now, they will inherit global font/color styles but might need specific layout tweaks later if requested. The prompt focused on Library/Empty State. */}
205214
{view === View.SETTINGS && <SettingsView />}
206215
{view === View.STATS && <StatsView />}
207-
{view === View.READER && selectedBook && (
208-
<ReaderErrorBoundary onRecover={() => { void handleCloseReader(); }} resetKey={selectedBook.id}>
209-
<ReaderView
210-
book={selectedBook}
211-
onClose={handleCloseReader}
212-
onUpdateProgress={handleReaderProgress}
213-
onAddBookmark={handleAddBookmark}
214-
onRemoveBookmark={handleRemoveBookmark}
215-
getBookContent={getBookContent}
216-
/>
217-
</ReaderErrorBoundary>
218-
)}
219-
</div>
220216
</main>
221217

222218
{/* Navigation */}
223-
{!isReader && (
224-
<Navigation activeView={view} onNavigate={setView} isReaderActive={!!selectedBookId} />
225-
)}
226-
</div>
219+
<Navigation activeView={view} onNavigate={setView} isReaderActive={!!selectedBookId} />
220+
</ScrapbookLayout>
227221
);
228222
};
229223

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, { ReactNode } from "react";
2+
import { motion } from "framer-motion";
3+
4+
interface ScrapbookLayoutProps {
5+
children: ReactNode;
6+
view?: string;
7+
}
8+
9+
const ScrapbookLayout: React.FC<ScrapbookLayoutProps> = ({ children, view }) => {
10+
return (
11+
<div className="relative min-h-screen w-full bg-scrap-cream overflow-hidden">
12+
{/*
13+
============================================================
14+
LAYER 0: Base Texture
15+
============================================================
16+
*/}
17+
<div
18+
className="fixed inset-0 pointer-events-none z-0 opacity-40 mix-blend-multiply"
19+
style={{ backgroundImage: "var(--noise-svg)" }}
20+
/>
21+
22+
{/*
23+
============================================================
24+
LAYER 1: The Big Scraps (Collage Background)
25+
============================================================
26+
*/}
27+
<div className="fixed inset-0 pointer-events-none z-0 overflow-hidden">
28+
29+
{/* Top Left: Celestial Star Map (Abstracted) */}
30+
<motion.div
31+
initial={{ opacity: 0, rotate: -5 }}
32+
animate={{ opacity: 1, rotate: 0 }}
33+
transition={{ duration: 1.5 }}
34+
className="absolute -top-20 -left-20 w-96 h-96 bg-scrap-navy opacity-10 rounded-full blur-3xl transform rotate-12"
35+
/>
36+
<div className="absolute top-10 left-10 w-64 h-64 border-2 border-scrap-navy/10 rounded-full opacity-20 animate-spin-slow" style={{ animationDuration: '60s' }}>
37+
<div className="absolute top-0 left-1/2 w-2 h-2 bg-scrap-navy rounded-full transform -translate-x-1/2" />
38+
<div className="absolute bottom-0 left-1/2 w-1 h-1 bg-scrap-navy rounded-full transform -translate-x-1/2" />
39+
</div>
40+
41+
{/* Right Side: Vintage Sheet Music (CSS Pattern) */}
42+
<motion.div
43+
initial={{ x: 100, opacity: 0 }}
44+
animate={{ x: 0, opacity: 0.6 }}
45+
className="absolute top-1/4 -right-12 w-80 h-[600px] bg-scrap-kraft/30 rotate-3 transform shadow-lg"
46+
style={{
47+
clipPath: "polygon(2% 0%, 100% 0%, 100% 100%, 0% 100%, 5% 90%, 0% 80%, 3% 70%, 0% 60%, 4% 50%, 0% 40%, 3% 30%, 0% 20%, 5% 10%)",
48+
backgroundImage: "repeating-linear-gradient(transparent, transparent 19px, rgba(44, 58, 79, 0.1) 20px)"
49+
}}
50+
>
51+
{/* Music Notes Scatter */}
52+
<div className="absolute top-20 left-10 text-4xl opacity-20 rotate-12">𝄞</div>
53+
<div className="absolute top-40 left-20 text-3xl opacity-20 -rotate-12">𝅘𝅥𝅮</div>
54+
<div className="absolute bottom-20 left-12 text-3xl opacity-20 rotate-6">𝅘𝅥𝅯</div>
55+
</motion.div>
56+
57+
{/* Bottom Left: Night Sky / Navy Paper */}
58+
<motion.div
59+
initial={{ y: 100, opacity: 0 }}
60+
animate={{ y: 0, opacity: 1 }}
61+
className="absolute -bottom-24 -left-12 w-[500px] h-[400px] bg-scrap-navy transform -rotate-2 shadow-scrap-deep"
62+
style={{ clipPath: "polygon(0% 10%, 10% 0%, 100% 5%, 95% 100%, 0% 100%)" }}
63+
>
64+
{/* Stars */}
65+
<div className="absolute top-10 right-20 text-scrap-cream opacity-40 text-xl"></div>
66+
<div className="absolute top-24 right-40 text-scrap-cream opacity-20 text-sm"></div>
67+
<div className="absolute top-40 right-10 text-scrap-cream opacity-30 text-lg"></div>
68+
<div className="absolute top-1/2 left-1/3 w-32 h-32 border border-scrap-cream/10 rounded-full" />
69+
</motion.div>
70+
71+
{/* Center/Random: Coffee Stain */}
72+
<div className="absolute top-1/2 left-1/4 w-40 h-40 border-8 border-[#5D4037]/10 rounded-full transform scale-y-75 -rotate-12 mix-blend-multiply pointer-events-none blur-sm" />
73+
</div>
74+
75+
{/*
76+
============================================================
77+
LAYER 2: Content Container
78+
============================================================
79+
*/}
80+
<div className="relative z-10 w-full min-h-screen flex flex-col">
81+
{children}
82+
</div>
83+
</div>
84+
);
85+
};
86+
87+
export default ScrapbookLayout;

0 commit comments

Comments
 (0)