Skip to content

Commit 9392ebb

Browse files
committed
Fix terminal animations and standardize typing content across all pages
- Fix ProfessionalSection terminal fade-out issue (terminal now properly disappears when navigation appears) - Standardize typing animation content structure across all pages (HeroSection, AboutSection, ProfessionalSection, ProjectsSection, ContentSection, ContactSection) - Add centralized font size and color management in utils.ts (getTypingFontSize, getLineText, getLineColor) - Fix TypeScript errors and linting issues - Update HeroSection typing content to split long narrative into two lines with different colors - Change Tech section color in AboutSection from sunset orange to yellow - Ensure all terminal animations work consistently across all pages - Fix syntax errors and build issues
1 parent 9e1299d commit 9392ebb

File tree

7 files changed

+327
-350
lines changed

7 files changed

+327
-350
lines changed

src/components/sections/AboutSection.tsx

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useState, useEffect } from 'react'
55
import { motion, AnimatePresence } from 'framer-motion'
66
import Badge from '@/components/ui/Badge'
77
import { usePathname } from 'next/navigation'
8-
import { getTerminalPath } from '@/lib/utils'
8+
import { getTerminalPath, getTypingFontSize, getLineText, getLineColor } from '@/lib/utils'
99

1010
// Define types for our content
1111
interface FavoriteItem {
@@ -31,10 +31,12 @@ interface AboutContent {
3131
}
3232

3333
// Typing animation content
34-
const typingContent = {
35-
greeting: "Hey, It's me, David! Just making sure you haven't gotten lost!",
36-
description: "Welcome to my digital living room! Here you'll find the stories that shaped me and the quirks that make me... well, me! Click around to discover what makes me tick beyond just lines of code."
37-
};
34+
const typingContent = [
35+
{ greeting: "Hey, It's me, David! Just making sure you haven't gotten lost!", color: "text-primary-blue" },
36+
{ intro: "Welcome to my digital living room!", color: "text-primary-sunset-orange" },
37+
{ body: "Here you'll find the stories that shaped me and the quirks that make me... well, me!", color: "text-primary-yellow" },
38+
{ narrative: "Click around to discover what makes me tick beyond just lines of code.", color: "text-primary-magenta" }
39+
];
3840

3941
// About content - restructured for better engagement
4042
const aboutContent: AboutContent = {
@@ -145,9 +147,9 @@ const categoryConfig: Record<string, { icon: string; color: string; bgColor: str
145147
},
146148
"Tech": {
147149
icon: "💻",
148-
color: "text-primary-sunset-orange",
149-
bgColor: "bg-primary-sunset-orange/20", // using custom sunset orange
150-
cardBg: "bg-primary-sunset-orange/30", // using custom sunset orange
150+
color: "text-primary-yellow",
151+
bgColor: "bg-primary-yellow/20", // using custom yellow
152+
cardBg: "bg-primary-yellow/30", // using custom yellow
151153
emoji: "⚙️"
152154
}
153155
};
@@ -160,39 +162,42 @@ export default function AboutSection() {
160162
const [activeCategory, setActiveCategory] = useState<string>("Personal");
161163

162164
// Typing animation states
163-
const [greetingText, setGreetingText] = useState("");
164-
const [descriptionText, setDescriptionText] = useState("");
165-
const [currentTypingIndex, setCurrentTypingIndex] = useState(0);
165+
const [currentLineIndex, setCurrentLineIndex] = useState(0);
166+
const [currentCharIndex, setCurrentCharIndex] = useState(0);
167+
const [completedLines, setCompletedLines] = useState<string[]>([]);
166168

167-
// Typing effect
169+
// Typing effect using setTimeout
168170
useEffect(() => {
169-
let timeout: NodeJS.Timeout;
171+
if (currentLineIndex >= typingContent.length) return;
170172

171-
// Type greeting text
172-
if (currentTypingIndex === 0) {
173-
if (greetingText.length < typingContent.greeting.length) {
174-
timeout = setTimeout(() => {
175-
setGreetingText(typingContent.greeting.slice(0, greetingText.length + 1));
176-
}, 50);
177-
} else {
178-
// Move to next text after a pause
179-
timeout = setTimeout(() => {
180-
setCurrentTypingIndex(1);
181-
}, 500);
182-
}
183-
}
173+
const currentLine = typingContent[currentLineIndex];
174+
const currentLineText = getLineText(currentLine);
184175

185-
// Type description text
186-
else if (currentTypingIndex === 1) {
187-
if (descriptionText.length < typingContent.description.length) {
188-
timeout = setTimeout(() => {
189-
setDescriptionText(typingContent.description.slice(0, descriptionText.length + 1));
190-
}, 20);
191-
}
176+
if (currentCharIndex < currentLineText.length) {
177+
const timeout = setTimeout(() => {
178+
setCurrentCharIndex(prev => prev + 1);
179+
}, Math.floor(Math.random() * 40) + 40);
180+
181+
return () => clearTimeout(timeout);
182+
} else {
183+
// Line is complete, move to next line after delay
184+
const timeout = setTimeout(() => {
185+
setCompletedLines(prev => [...prev, currentLineText]);
186+
setCurrentLineIndex(prev => prev + 1);
187+
setCurrentCharIndex(0);
188+
}, 1000);
189+
190+
return () => clearTimeout(timeout);
192191
}
193-
194-
return () => clearTimeout(timeout);
195-
}, [greetingText, descriptionText, currentTypingIndex]);
192+
}, [currentLineIndex, currentCharIndex]);
193+
194+
// Get current typed text
195+
const getCurrentTypedText = () => {
196+
if (currentLineIndex >= typingContent.length) return '';
197+
const currentLine = typingContent[currentLineIndex];
198+
const currentLineText = getLineText(currentLine);
199+
return currentLineText.slice(0, currentCharIndex);
200+
};
196201

197202
// Helper function to split comma-separated values into arrays
198203
const splitValues = (value: string, label?: string): string[] => {
@@ -229,22 +234,39 @@ export default function AboutSection() {
229234
</div>
230235
</div>
231236

232-
<div className="font-mono text-left">
233-
{/* Greeting Text */}
234-
<div className="text-2xl md:text-3xl text-primary-sunset-orange font-bold mb-4">
235-
{greetingText}
236-
{greetingText.length < typingContent.greeting.length && (
237+
<div className="font-mono text-left space-y-4">
238+
{/* Display completed lines */}
239+
{completedLines.map((lineText, index) => {
240+
const lineConfig = typingContent[index];
241+
if (!lineConfig) return null;
242+
243+
const lineType = Object.keys(lineConfig).find(key => key !== 'color') || '';
244+
const fontSize = getTypingFontSize(lineType);
245+
const color = getLineColor(lineConfig);
246+
247+
return (
248+
<div key={index} className={`${fontSize} ${color}`}>
249+
{lineText}
250+
</div>
251+
);
252+
})}
253+
254+
{/* Current typing line */}
255+
{currentLineIndex < typingContent.length && (
256+
<div className={`${(() => {
257+
const currentLine = typingContent[currentLineIndex];
258+
const lineType = Object.keys(currentLine).find(key => key !== 'color') || '';
259+
return getTypingFontSize(lineType);
260+
})()} ${getLineColor(typingContent[currentLineIndex])}`}>
261+
{getCurrentTypedText()}
237262
<span className="animate-pulse"></span>
238-
)}
239-
</div>
263+
</div>
264+
)}
240265

241-
{/* Description Text */}
242-
{greetingText === typingContent.greeting && (
266+
{/* Show cursor at the end when all lines are typed */}
267+
{currentLineIndex >= typingContent.length && (
243268
<div className="text-lg text-primary-blue">
244-
{descriptionText}
245-
{descriptionText.length < typingContent.description.length && (
246-
<span className="animate-pulse"></span>
247-
)}
269+
<span className="animate-pulse"></span>
248270
</div>
249271
)}
250272
</div>

src/components/sections/ContactSection.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import Card, { CardHeader, CardContent, CardFooter } from '@/components/ui/Card'
66
import Badge from '@/components/ui/Badge'
77
import Button from '@/components/ui/Button'
88
import { usePathname } from 'next/navigation'
9-
import { getTerminalPath } from '@/lib/utils'
9+
import { getTerminalPath, getTypingFontSize, getLineText, getLineColor } from '@/lib/utils'
1010

1111
// Typing animation content
1212
const typingContent = [
13-
{ intro: "Let's connect! 🤝", color: "text-primary-blue" },
14-
{ description: "I'm always interested in new opportunities, collaborations, and interesting conversations.", color: "text-primary-magenta" },
15-
{ instructions: "Choose your preferred method below or use the contact form.", color: "text-primary-sunset-orange" },
16-
{ action: "I look forward to hearing from you!", color: "text-primary-yellow" }
13+
{ greeting: "Still can't get enough, huh? Let's connect! 🤝", color: "text-primary-sunset-orange" },
14+
{ intro: "I'm always interested in new opportunities, collaborations, and interesting conversations.", color: "text-primary-magenta" },
15+
{ body: "I've got several options for you get in touch with me, scroll down and pick your preferred method!", color: "text-primary-yellow" },
16+
{ narrative: "I can't wait to hear from you! Cheers!", color: "text-primary-blue" }
1717
];
1818

1919
// Contact methods
@@ -87,11 +87,6 @@ export default function ContactSection() {
8787
setCaptchaValue(`${num1} + ${num2} = ?`);
8888
}, []);
8989

90-
// Helper function to get text content from any line object
91-
const getLineText = (line: {intro?: string, description?: string, instructions?: string, action?: string}) => {
92-
return line.intro || line.description || line.instructions || line.action || '';
93-
};
94-
9590
// Copy to clipboard function
9691
const copyToClipboard = async (text: string) => {
9792
try {
@@ -295,16 +290,24 @@ export default function ContactSection() {
295290
const lineConfig = typingContent[index];
296291
if (!lineConfig) return null;
297292

293+
const lineType = Object.keys(lineConfig).find(key => key !== 'color') || '';
294+
const fontSize = getTypingFontSize(lineType);
295+
const color = getLineColor(lineConfig);
296+
298297
return (
299-
<div key={index} className={`text-lg ${lineConfig.color}`}>
298+
<div key={index} className={`${fontSize} ${color}`}>
300299
{lineText}
301300
</div>
302301
);
303302
})}
304303

305304
{/* Current typing line */}
306305
{currentLineIndex < typingContent.length && (
307-
<div className={`text-lg ${typingContent[currentLineIndex].color}`}>
306+
<div className={`${(() => {
307+
const currentLine = typingContent[currentLineIndex];
308+
const lineType = Object.keys(currentLine).find(key => key !== 'color') || '';
309+
return getTypingFontSize(lineType);
310+
})()} ${getLineColor(typingContent[currentLineIndex])}`}>
308311
{getCurrentTypedText()}
309312
<span className="animate-pulse"></span>
310313
</div>

src/components/sections/ContentSection.tsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Badge from '@/components/ui/Badge'
77
import Button from '@/components/ui/Button'
88
import ContentPlaceholder from './ContentPlaceholder'
99
import { usePathname } from 'next/navigation'
10-
import { getTerminalPath } from '@/lib/utils'
10+
import { getTerminalPath, getTypingFontSize, getLineText, getLineColor } from '@/lib/utils'
1111

1212
// Content item interface
1313
interface ContentItem {
@@ -29,10 +29,13 @@ const SHOW_PLACEHOLDER = true;
2929

3030
// Typing animation content
3131
const typingContent = [
32-
{ intro: "Welcome to my content library! 📚", color: "text-primary-blue" },
33-
{ description: "Here you'll find articles, videos, tutorials, and resources I've created or found valuable.", color: "text-primary-magenta" },
34-
{ instructions: "Use the interactive terminal below to explore content by keywords or browse everything.", color: "text-primary-sunset-orange" },
35-
{ action: "Happy exploring!", color: "text-primary-yellow" }
32+
{ greeting: "Hey, there you are! I was starting to wonder where you wandered off to... 😄", color: "text-primary-sunset-orange" },
33+
{ intro: "Well, if you want to see how I learned to build this site—or what inspired those wild projects on my Projects page—you're in exactly the right place.", color: "text-primary-blue" },
34+
{ welcome: "Welcome to my Content Library!", color: "text-primary-magenta" },
35+
{ body: "Here you'll find articles and videos, both from me and from folks way smarter than me, covering my favorite tech rabbit holes—and a few other sources I've found especially mind-expanding.", color: "text-primary-yellow" },
36+
{ personal: "Want to know more about me beyond the curtain of code? You will find posts and videos on all kinds of topics that matter to me beyond tech.", color: "text-primary-blue" },
37+
{ outro: "If you like my style or want recommendations for more cool stuff, just say the word—I'm always happy to share my favorite corners of the web.", color: "text-primary-magenta" },
38+
{ farewell: "Alright, terminal is all yours. Happy exploring! 🚀", color: "text-primary-sunset-orange" }
3639
];
3740

3841
// Content database
@@ -221,7 +224,7 @@ export default function ContentSection() {
221224
const pathname = usePathname()
222225
const terminalPath = getTerminalPath(pathname)
223226

224-
// Typing animation states - moved to top to fix hooks rule
227+
// Typing animation states
225228
const [currentLineIndex, setCurrentLineIndex] = useState(0);
226229
const [currentCharIndex, setCurrentCharIndex] = useState(0);
227230
const [completedLines, setCompletedLines] = useState<string[]>([]);
@@ -232,22 +235,19 @@ export default function ContentSection() {
232235
{ type: "system", content: "Content Explorer v1.0.0" },
233236
{ type: "system", content: "Type 'help' for available commands or start searching with 'search [keywords]'" }
234237
]);
235-
const [filteredContent, setFilteredContent] = useState<Array<ContentItem>>([]);
236238
const [showResults, setShowResults] = useState(false);
237239
const [currentPage, setCurrentPage] = useState(1);
238240
const itemsPerPage = 6;
239241

240242
const terminalRef = useRef<HTMLDivElement>(null);
241243
const inputRef = useRef<HTMLInputElement>(null);
242244

243-
// Helper function to get text content from any line object
244-
const getLineText = (line: {intro?: string, description?: string, instructions?: string, action?: string}) => {
245-
return line.intro || line.description || line.instructions || line.action || '';
246-
};
247-
245+
// Content filtering states
246+
const [filteredContent, setFilteredContent] = useState<Array<ContentItem>>([]);
247+
248248
// Typing effect using setTimeout
249249
useEffect(() => {
250-
if (SHOW_PLACEHOLDER || currentLineIndex >= typingContent.length) return;
250+
if (currentLineIndex >= typingContent.length) return;
251251

252252
const currentLine = typingContent[currentLineIndex];
253253
const currentLineText = getLineText(currentLine);
@@ -457,7 +457,7 @@ export default function ContentSection() {
457457
};
458458

459459
return (
460-
<section className="py-20 bg-primary-navy relative overflow-hidden">
460+
<section id="content" className="py-20 bg-primary-navy relative overflow-hidden">
461461
{/* Background Pattern */}
462462
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_20%,rgba(34,211,238,0.03),transparent_40%)]"></div>
463463
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_80%,rgba(236,73,153,0.03),transparent_40%)]"></div>
@@ -487,16 +487,24 @@ export default function ContentSection() {
487487
const lineConfig = typingContent[index];
488488
if (!lineConfig) return null;
489489

490+
const lineType = Object.keys(lineConfig).find(key => key !== 'color') || '';
491+
const fontSize = getTypingFontSize(lineType);
492+
const color = getLineColor(lineConfig);
493+
490494
return (
491-
<div key={index} className={`text-lg ${lineConfig.color}`}>
495+
<div key={index} className={`${fontSize} ${color}`}>
492496
{lineText}
493497
</div>
494498
);
495499
})}
496500

497501
{/* Current typing line */}
498502
{currentLineIndex < typingContent.length && (
499-
<div className={`text-lg ${typingContent[currentLineIndex].color}`}>
503+
<div className={`${(() => {
504+
const currentLine = typingContent[currentLineIndex];
505+
const lineType = Object.keys(currentLine).find(key => key !== 'color') || '';
506+
return getTypingFontSize(lineType);
507+
})()} ${getLineColor(typingContent[currentLineIndex])}`}>
500508
{getCurrentTypedText()}
501509
<span className="animate-pulse"></span>
502510
</div>

0 commit comments

Comments
 (0)