Skip to content

Commit 75d3d40

Browse files
committed
feat(version): 1.1.0
1 parent 1176874 commit 75d3d40

File tree

19 files changed

+9404
-6765
lines changed

19 files changed

+9404
-6765
lines changed

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "peterhanania-portfolio",
33
"author": "Peter Hanania",
4-
"version": "1.0.6",
4+
"version": "1.1.0",
55
"license": "MIT",
66
"private": true,
77
"repository": {
@@ -37,8 +37,13 @@
3737
"astro-compress": "2.0.1",
3838
"astro-icon": "0.8.1",
3939
"astro-tooltips": "^0.6.2",
40+
"baffle": "^0.3.6",
41+
"canvas-confetti": "^1.9.3",
4042
"clsx": "1.2.1",
43+
"framer-motion": "^12.16.0",
44+
"lottie-web": "^5.13.0",
4145
"preact": "10.16.0",
46+
"react-lottie-player": "^2.1.0",
4247
"react-schemaorg": "2.0.0",
4348
"react-tippy": "^1.4.0",
4449
"react-use-lanyard": "0.3.0",
@@ -58,6 +63,7 @@
5863
"@swc/register": "^0.1.10",
5964
"@testing-library/jest-dom": "^5.17.0",
6065
"@testing-library/react": "^14.0.0",
66+
"@types/canvas-confetti": "^1.9.0",
6167
"@types/eslint": "^8.44.1",
6268
"@types/fs-extra": "^11.0.1",
6369
"@types/inquirer": "^9.0.3",

packages/frontend/constants/technologies.tsx

Lines changed: 3377 additions & 59 deletions
Large diffs are not rendered by default.
350 KB
Loading
1.78 MB
Binary file not shown.

packages/frontend/src/components/about/index.tsx

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import confetti from 'canvas-confetti';
12
import { useEffect, useState } from 'preact/hooks';
23

34
export default function ({ aria, name, current_section }) {
@@ -27,6 +28,37 @@ export default function ({ aria, name, current_section }) {
2728
headlineSpans.forEach((span, index) => {
2829
setTimeout(() => {
2930
(span as HTMLElement).style.opacity = '1';
31+
32+
// Trigger confetti when the "websites" span appears (last span)
33+
if (index === headlineSpans.length - 1) {
34+
setTimeout(() => {
35+
// Launch confetti celebration!
36+
confetti({
37+
particleCount: 100,
38+
spread: 70,
39+
origin: { y: 0.6 },
40+
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8']
41+
});
42+
43+
// Add a second burst for extra celebration
44+
setTimeout(() => {
45+
confetti({
46+
particleCount: 50,
47+
angle: 60,
48+
spread: 55,
49+
origin: { x: 0, y: 0.6 },
50+
colors: ['#FF6B6B', '#4ECDC4', '#45B7D1']
51+
});
52+
confetti({
53+
particleCount: 50,
54+
angle: 120,
55+
spread: 55,
56+
origin: { x: 1, y: 0.6 },
57+
colors: ['#FFA07A', '#98D8C8', '#FF6B6B']
58+
});
59+
}, 500);
60+
}, 300); // Small delay after the last word appears
61+
}
3062
}, index * 200);
3163
});
3264
}
@@ -39,14 +71,28 @@ export default function ({ aria, name, current_section }) {
3971
const timer = setInterval(() => {
4072
if (start >= endValue) {
4173
clearInterval(timer);
74+
75+
// Add small confetti burst when each counter finishes
76+
if (elementId === 'users') {
77+
// When the last counter finishes
78+
setTimeout(() => {
79+
confetti({
80+
particleCount: 30,
81+
spread: 45,
82+
origin: { y: 0.8 },
83+
colors: ['#45B7D1', '#98D8C8']
84+
});
85+
}, 200);
86+
}
4287
}
4388
element.innerHTML = String(start);
4489
start++;
4590
}, intervalTime);
4691
}
4792

48-
countUp('experience', 5, 500);
49-
countUp('projects', 25, 500);
93+
countUp('experience', 7, 500);
94+
countUp('projects', 40, 500);
95+
countUp('users', 1000000, 500);
5096

5197
return () => {
5298
window.removeEventListener('scroll', checkVisibility);
@@ -88,16 +134,20 @@ export default function ({ aria, name, current_section }) {
88134
<div className="statistics">
89135
<div className="group">
90136
<span className="number" id="experience">
91-
5
137+
7
92138
</span>
93139
<span className="text">YEARS EXPERIENCE</span>
94140
</div>
95141
<div className="group">
96142
<span className="number" id="projects">
97-
25+
143+
40+
98144
</span>
99145
<span className="text">PROJECTS COMPLETED</span>
100146
</div>
147+
<div className="group">
148+
<span className="number">1M+</span>
149+
<span className="text">MONTHLY ACTIVE USERS</span>
150+
</div>
101151
</div>
102152
</section>
103153
);

packages/frontend/src/components/comissions/index.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import AccessibleButton from '../../accessibility/button';
22

33
const services = [
4-
'Web Hosting',
5-
'Domain Management',
6-
'Coding Consultation',
7-
'VPS Setup',
8-
'Frontend Development',
9-
'Backend Development',
10-
'Mobile App Development',
11-
'Database Management',
12-
'SEO Optimization'
4+
'Premium Web Hosting & Infrastructure',
5+
'Strategic Domain Management',
6+
'Expert Coding Consultation',
7+
'Custom VPS Configuration & Deployment',
8+
'Modern Frontend Development',
9+
'Robust Backend Development',
10+
'Cross-Platform Mobile App Development',
11+
'Advanced Database Architecture',
12+
'SEO & Performance Optimization'
1313
];
1414

1515
export default function ({ aria, name }: { aria: boolean; name: string }) {

packages/frontend/src/components/experience/index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,31 @@ export default function ({ aria, name }: { aria: boolean; name: string }) {
2929
</div>
3030
<div>
3131
<p className="light-text">Mastering</p>
32-
<p className="subtitle">Next.js</p>
32+
<p className="subtitle">AWS</p>
3333
</div>
3434
<div>
3535
<p className="light-text">Expert in</p>
36-
<p className="subtitle">NestJS</p>
36+
<p className="subtitle">Typescript</p>
3737
</div>
3838
<div>
3939
<p className="light-text">Exploring</p>
40-
<p className="subtitle">Java</p>
40+
<p className="subtitle">Hugging Face</p>
4141
</div>
4242
<div>
4343
<p className="light-text">Learning</p>
44-
<p className="subtitle">Go</p>
44+
<p className="subtitle">SST</p>
4545
</div>
4646
<div>
4747
<p className="light-text">Passionate about</p>
48-
<p className="subtitle">Redis</p>
48+
<p className="subtitle">React Native</p>
4949
</div>
5050
<div>
5151
<p className="light-text">Devoted to</p>
5252
<p className="subtitle">PostgreSQL</p>
5353
</div>
5454
<div>
55-
<p className="light-text">Enthusiast of</p>
56-
<p className="subtitle">Typescript</p>
55+
<p className="light-text">Experienced with</p>
56+
<p className="subtitle">Shopify</p>
5757
</div>
5858

5959
<div>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* eslint-disable linebreak-style */
2+
/* eslint-disable prettier/prettier */
3+
4+
import baffle from 'baffle';
5+
import clsx from 'clsx';
6+
import { motion } from 'framer-motion';
7+
import { useEffect, useState } from 'react';
8+
9+
const FlippingText = ({ words, className }: { words: string[]; className?: string }) => {
10+
const [currentWordIndex, setCurrentWordIndex] = useState(0);
11+
12+
// Word cycling - exactly like the open source
13+
useEffect(() => {
14+
const wordInterval = setInterval(() => {
15+
setCurrentWordIndex((prevIndex) => {
16+
const nextIndex = (prevIndex + 1) % words.length;
17+
return nextIndex;
18+
});
19+
}, 4000);
20+
21+
return () => clearInterval(wordInterval);
22+
}, [words.length]);
23+
24+
// Baffle effect - EXACTLY like the open source code
25+
useEffect(() => {
26+
const baffleElements = document.querySelectorAll('[data-baffle-flip]');
27+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28+
const baffleInstances: any[] = [];
29+
30+
baffleElements.forEach((el) => {
31+
const b = baffle(el);
32+
b.set({
33+
characters: '█▓▒░█▓▒░<>$%@',
34+
speed: 75
35+
});
36+
b.start();
37+
baffleInstances.push(b);
38+
});
39+
40+
const runBaffleAnimation = () => {
41+
baffleInstances.forEach((b, index) => {
42+
setTimeout(() => {
43+
b.reveal(1500); // Reveal duration in milliseconds
44+
}, index * 500); // 0.5 seconds delay for each element
45+
});
46+
};
47+
48+
// Run the baffle animation initially
49+
runBaffleAnimation();
50+
51+
// Re-run the baffle animation every 4 seconds
52+
const interval = setInterval(() => {
53+
baffleInstances.forEach((b) => b.start()); // Reset to scrambled state
54+
runBaffleAnimation(); // Reveal again
55+
}, 4000);
56+
57+
return () => {
58+
clearInterval(interval);
59+
// Clean up baffle instances
60+
baffleInstances.forEach((b) => {
61+
try {
62+
b.destroy?.();
63+
} catch (error) {
64+
console.warn('Baffle cleanup failed:', error);
65+
}
66+
});
67+
};
68+
}, []);
69+
70+
return (
71+
<span className={clsx('relative inline-block flipping-text', className)}>
72+
{words.map((word, index) => (
73+
<span
74+
key={index}
75+
className="tracking-tighter"
76+
data-baffle-flip
77+
style={{
78+
display: currentWordIndex === index ? 'inline' : 'none'
79+
}}
80+
>
81+
{word}
82+
</span>
83+
))}
84+
<motion.span
85+
layout
86+
className="absolute -right-4 bottom-2 inline-block rounded-full bg-black"
87+
style={{
88+
width: '0.25em',
89+
height: '0.25em'
90+
}}
91+
animate={{
92+
backgroundColor: ['#60a5fa', '#22c55e', '#3b82f6']
93+
}}
94+
transition={{
95+
duration: 2,
96+
repeat: Infinity,
97+
repeatType: 'reverse'
98+
}}
99+
/>
100+
</span>
101+
);
102+
};
103+
104+
export default FlippingText;

0 commit comments

Comments
 (0)