Skip to content

Commit 3af1562

Browse files
committed
feat: optimize icons and tags
1 parent 7a804ef commit 3af1562

File tree

7 files changed

+262
-208
lines changed

7 files changed

+262
-208
lines changed

App.tsx

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
2-
import React, { useState, useEffect, useRef } from 'react';
3-
import { CHAPTERS } from './constants';
4-
import Sidebar from './components/Sidebar';
5-
import ChapterSection from './components/ChapterSection';
6-
import VueLogo from './components/VueLogo';
7-
import EndScreen from './components/EndScreen';
1+
import React, { useState, useEffect, useRef } from "react";
2+
import { CHAPTERS } from "./constants";
3+
import Sidebar from "./components/Sidebar";
4+
import ChapterSection from "./components/ChapterSection";
5+
import VueLogo from "./components/VueLogo";
6+
import EndScreen from "./components/EndScreen";
87

98
const App: React.FC = () => {
109
const [activeChapter, setActiveChapter] = useState(1);
1110
const scrollContainerRef = useRef<HTMLDivElement>(null);
1211

1312
const handleScroll = () => {
1413
if (!scrollContainerRef.current) return;
15-
14+
1615
// Logic to determine active chapter based on scroll position
1716
// We check which chapter element is closest to the top of the viewport
1817
const containerTop = scrollContainerRef.current.scrollTop;
@@ -33,33 +32,63 @@ const App: React.FC = () => {
3332
const scrollToChapter = (id: number) => {
3433
const element = document.getElementById(`chapter-${id}`);
3534
if (element && scrollContainerRef.current) {
36-
// We scroll the container, not the window
37-
const top = element.offsetTop;
38-
scrollContainerRef.current.scrollTo({ top, behavior: 'smooth' });
35+
// We scroll the container, not the window
36+
const top = element.offsetTop;
37+
scrollContainerRef.current.scrollTo({ top, behavior: "smooth" });
3938
}
4039
};
4140

4241
// Initial scroll to hero
4342
const startReading = () => {
44-
scrollToChapter(1);
43+
scrollToChapter(1);
4544
};
4645

4746
return (
4847
<div className="relative h-screen w-screen overflow-hidden bg-[#FFF8E7] text-[#2c3e50]">
4948
{/* Background Grid Pattern */}
5049
<div className="fixed inset-0 pointer-events-none z-0 opacity-5">
51-
<div className="absolute inset-0" style={{
52-
backgroundImage: 'radial-gradient(#000 1px, transparent 1px)',
53-
backgroundSize: '24px 24px'
54-
}}></div>
50+
<div
51+
className="absolute inset-0"
52+
style={{
53+
backgroundImage: "radial-gradient(#000 1px, transparent 1px)",
54+
backgroundSize: "24px 24px",
55+
}}
56+
></div>
5557
</div>
5658

5759
{/* Github Corner Ribbon */}
58-
<a href="https://github.com/CoderSerio/vue-source-book" target="_blank" className="fixed top-0 right-0 z-[60] group" aria-label="View source on GitHub">
59-
<svg width="80" height="80" viewBox="0 0 250 250" style={{ fill: '#151513', color: '#fff', position: 'absolute', top: 0, border: 0, right: 0 }} aria-hidden="true">
60+
<a
61+
href="https://github.com/CoderSerio/vue-source-book"
62+
target="_blank"
63+
className="fixed top-0 right-0 z-[60] group"
64+
aria-label="View source on GitHub"
65+
>
66+
<svg
67+
width="80"
68+
height="80"
69+
viewBox="0 0 250 250"
70+
style={{
71+
fill: "#151513",
72+
color: "#fff",
73+
position: "absolute",
74+
top: 0,
75+
border: 0,
76+
right: 0,
77+
}}
78+
aria-hidden="true"
79+
>
6080
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
61-
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style={{ transformOrigin: '130px 106px' }} className="group-hover:animate-[wave_0.5s_ease-in-out_infinite]"></path>
62-
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" className="opacity-90"></path>
81+
<path
82+
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
83+
fill="currentColor"
84+
style={{ transformOrigin: "130px 106px" }}
85+
className="group-hover:animate-[wave_0.5s_ease-in-out_infinite]"
86+
></path>
87+
<path
88+
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
89+
fill="currentColor"
90+
className="opacity-90"
91+
></path>
6392
</svg>
6493
<style>{`
6594
@keyframes wave {
@@ -70,61 +99,60 @@ const App: React.FC = () => {
7099
`}</style>
71100
</a>
72101

73-
<Sidebar currentChapterId={activeChapter} scrollToChapter={scrollToChapter} />
102+
<Sidebar
103+
currentChapterId={activeChapter}
104+
scrollToChapter={scrollToChapter}
105+
/>
74106

75107
{/* Main Scroll Snap Container - Added strict scroll rules */}
76-
<div
108+
<div
77109
ref={scrollContainerRef}
78110
onScroll={handleScroll}
79111
className="h-full w-full overflow-y-scroll snap-y snap-mandatory scroll-smooth no-scrollbar"
80-
style={{ scrollBehavior: 'smooth' }}
112+
style={{ scrollBehavior: "smooth" }}
81113
>
82114
{/* Hero Section (Snap Item) */}
83115
<section className="min-h-screen snap-start snap-always flex flex-col items-center justify-center text-center p-6 relative">
84-
{/* Decorative Blobs */}
85-
<div className="absolute top-20 right-10 w-64 h-64 bg-green-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none"></div>
86-
<div className="absolute bottom-20 left-10 w-72 h-72 bg-blue-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none" style={{animationDelay: '1s'}}></div>
87-
88-
<div className="bg-white p-12 rounded-[3rem] sketchy-border border-4 max-w-4xl relative z-10 transform hover:scale-[1.01] transition-transform duration-500">
89-
<div className="flex justify-center mb-8">
90-
<VueLogo className="transform scale-150" />
91-
</div>
92-
93-
<h1 className="text-6xl md:text-8xl font-bold mb-4 tracking-tighter font-hand">
94-
SOURCE BOOK
95-
</h1>
96-
97-
<div className="w-full h-1 bg-black my-6 rounded-full opacity-20"></div>
98-
99-
<h2 className="text-2xl md:text-4xl font-bold text-gray-600 mb-8 font-mono">
100-
The Vue.js Chronicles
101-
</h2>
102-
103-
<p className="text-xl mb-10 max-w-lg mx-auto font-medium text-gray-500">
104-
An interactive, hand-drawn journey through Reactivity, VDOM, Compilers, and Vapor Mode.
105-
</p>
106-
107-
<button
108-
onClick={startReading}
109-
className="bg-[#42b883] text-white text-2xl px-12 py-4 rounded-full font-bold hover:bg-[#35495e] border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] active:translate-y-1 active:shadow-none transition-all"
110-
>
111-
Start Journey ↓
112-
</button>
113-
</div>
116+
{/* Decorative Blobs */}
117+
<div className="absolute top-20 right-10 w-64 h-64 bg-green-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none"></div>
118+
<div
119+
className="absolute bottom-20 left-10 w-72 h-72 bg-blue-200 rounded-full mix-blend-multiply filter blur-2xl opacity-60 animate-pulse pointer-events-none"
120+
style={{ animationDelay: "1s" }}
121+
></div>
122+
123+
<div className="bg-white p-12 rounded-[3rem] sketchy-border border-4 max-w-4xl relative z-10 transform hover:scale-[1.01] transition-transform duration-500">
124+
<div className="flex justify-center mb-8">
125+
<VueLogo className="transform scale-150" />
126+
</div>
127+
128+
<h1 className="text-6xl md:text-8xl font-bold mb-4 tracking-tighter font-hand">
129+
SOURCE BOOK
130+
</h1>
131+
132+
<div className="w-full h-1 bg-black my-6 rounded-full opacity-20"></div>
133+
134+
<h2 className="text-2xl md:text-4xl font-bold text-gray-600 mb-8 font-mono">
135+
The Vue.js Chronicles
136+
</h2>
137+
138+
<p className="text-xl mb-10 max-w-lg mx-auto font-medium text-gray-500">
139+
An interactive, hand-drawn journey through Reactivity, VDOM,
140+
Compilers, and Vapor Mode.
141+
</p>
142+
143+
<button
144+
onClick={startReading}
145+
className="bg-[#42b883] text-white text-2xl px-12 py-4 rounded-full font-bold hover:bg-[#35495e] border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] active:translate-y-1 active:shadow-none transition-all"
146+
>
147+
Start Journey ↓
148+
</button>
149+
</div>
114150
</section>
115151

116-
{/* Render Chapters */}
117152
{CHAPTERS.map((chapter, index) => (
118-
<ChapterSection
119-
key={chapter.id}
120-
data={chapter}
121-
isActive={activeChapter === chapter.id}
122-
nextChapterId={index < CHAPTERS.length - 1 ? CHAPTERS[index + 1].id : null}
123-
scrollToChapter={scrollToChapter}
124-
/>
153+
<ChapterSection key={chapter.id} data={chapter} />
125154
))}
126155

127-
{/* End Screen */}
128156
<EndScreen />
129157
</div>
130158
</div>

components/ChapterSection.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,16 @@ import ReadingSection from "./ReadingSection";
66

77
interface ChapterSectionProps {
88
data: ChapterData;
9-
isActive: boolean;
10-
nextChapterId: number | null;
11-
scrollToChapter: (id: number) => void;
129
}
1310

14-
const ChapterSection: React.FC<ChapterSectionProps> = ({
15-
data,
16-
isActive,
17-
nextChapterId,
18-
scrollToChapter,
19-
}) => {
20-
// State for challenges within this chapter
11+
const ChapterSection: React.FC<ChapterSectionProps> = ({ data }) => {
2112
const [completedPages, setCompletedPages] = useState<Record<string, boolean>>(
2213
{}
2314
);
2415
const [userCodes, setUserCodes] = useState<Record<string, string>>({});
2516
const [errors, setErrors] = useState<Record<string, string | null>>({});
26-
// 每个 page.id 当前解锁到第几个提示(1-based,0 表示还没看提示)
2717
const [hintLevels, setHintLevels] = useState<Record<string, number>>({});
2818

29-
// Helper to increment hint level
3019
const incrementHintLevel = (pageId: string, maxLevel: number) => {
3120
setHintLevels((prev) => {
3221
const current = prev[pageId] ?? 0;
@@ -115,9 +104,12 @@ const ChapterSection: React.FC<ChapterSectionProps> = ({
115104
<h3 className="text-3xl font-bold mb-2 font-hand">
116105
{page.challenge?.subtitle}
117106
</h3>
118-
<p className="text-gray-600 text-lg leading-snug font-hand">
119-
{page.challenge?.description}
120-
</p>
107+
<div
108+
className="text-gray-600 text-lg leading-snug font-hand"
109+
dangerouslySetInnerHTML={{
110+
__html: page.challenge?.description || "",
111+
}}
112+
/>
121113
</div>
122114

123115
<CodePlayground
@@ -146,7 +138,12 @@ const ChapterSection: React.FC<ChapterSectionProps> = ({
146138
</button>
147139
) : (
148140
<div className="bg-purple-50 p-3 rounded-lg border border-purple-200 text-purple-900 text-sm max-w-md animate-in fade-in slide-in-from-bottom-2">
149-
<strong>Wizard says:</strong> {currentHintText}
141+
<strong>Wizard says:</strong>{" "}
142+
<span
143+
dangerouslySetInnerHTML={{
144+
__html: currentHintText || "",
145+
}}
146+
/>
150147
</div>
151148
)}
152149
</div>

0 commit comments

Comments
 (0)