Skip to content

Commit 5fe16d0

Browse files
committed
feat: bubble wrap
1 parent 7c86861 commit 5fe16d0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+241
-64
lines changed

public/apps/apps.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@
145145
"title": "Magic 8-Ball",
146146
"description": "Ask a yes/no question and let the Magic 8-Ball reveal your fate!",
147147
"icon": "QuestionIcon"
148+
},
149+
{
150+
"slug": "bubble-wrap",
151+
"to": "/apps/bubble-wrap",
152+
"title": "Bubble Wrap",
153+
"description": "Pop some virtual bubble wrap to relieve stress.",
154+
"icon": "CirclesFourIcon"
148155
}
149156
]
150157
},

src/components/AnimatedRoutes.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import LightsOutPage from '../pages/apps/LightsOutPage'; // Import LightsOutPage
6060
import NonogramPage from '../pages/apps/NonogramPage'; // Import NonogramPage
6161
import WhackABugPage from '../pages/apps/WhackABugPage';
6262
import SimonSaysPage from '../pages/apps/SimonSaysPage';
63+
import BubbleWrapPage from '../pages/apps/BubbleWrapPage';
6364
import SettingsPage from '../pages/SettingsPage';
6465

6566
import UsefulLinksPage from '../pages/UsefulLinksPage';
@@ -569,6 +570,10 @@ function AnimatedRoutes() {
569570
path="/apps::simon"
570571
element={<Navigate to="/apps/simon-says" replace />}
571572
/>
573+
<Route
574+
path="/apps::pop"
575+
element={<Navigate to="/apps/bubble-wrap" replace />}
576+
/>
572577
{/* End of hardcoded redirects */}
573578
<Route
574579
path="/apps/ip"
@@ -1159,6 +1164,20 @@ function AnimatedRoutes() {
11591164
</motion.div>
11601165
}
11611166
/>
1167+
<Route
1168+
path="/apps/bubble-wrap"
1169+
element={
1170+
<motion.div
1171+
initial="initial"
1172+
animate="in"
1173+
exit="out"
1174+
variants={pageVariants}
1175+
transition={pageTransition}
1176+
>
1177+
<BubbleWrapPage />
1178+
</motion.div>
1179+
}
1180+
/>
11621181
{/* D&D specific 404 page */}
11631182
<Route
11641183
path="/stories/*"

src/pages/apps/AsciiConverterPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function AsciiConverterPage() {
130130
>
131131
<ArrowLeftIcon size={24} /> Back to Apps
132132
</Link>
133-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
133+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
134134
<span className="codex-color">fc</span>
135135
<span className="separator-color">::</span>
136136
<span className="apps-color">apps</span>

src/pages/apps/Base64ConverterPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function Base64ConverterPage() {
8181
>
8282
<ArrowLeftIcon size={24} /> Back to Apps
8383
</Link>
84-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
84+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
8585
<span className="codex-color">fc</span>
8686
<span className="separator-color">::</span>
8787
<span className="apps-color">apps</span>

src/pages/apps/BubbleWrapPage.js

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import React, {useState, useEffect} from 'react';
2+
import {Link} from 'react-router-dom';
3+
import {ArrowLeftIcon, CirclesFourIcon} from '@phosphor-icons/react';
4+
import colors from '../../config/colors';
5+
import useSeo from '../../hooks/useSeo';
6+
7+
const BUBBLE_COUNT = 100; // Number of bubbles
8+
9+
const BubbleWrapPage = () => {
10+
useSeo({
11+
title: 'Bubble Wrap | Fezcodex',
12+
description: 'Pop some virtual bubble wrap to relieve stress.',
13+
keywords: ['Fezcodex', 'bubble wrap', 'stress relief', 'pop', 'game'],
14+
ogTitle: 'Bubble Wrap | Fezcodex',
15+
ogDescription: 'Pop some virtual bubble wrap to relieve stress.',
16+
ogImage: 'https://fezcode.github.io/logo512.png',
17+
twitterCard: 'summary_large_image',
18+
twitterTitle: 'Bubble Wrap | Fezcodex',
19+
twitterDescription: 'Pop some virtual bubble wrap to relieve stress.',
20+
twitterImage: 'https://fezcode.github.io/logo512.png',
21+
});
22+
23+
const [bubbles, setBubbles] = useState(Array(BUBBLE_COUNT).fill(false));
24+
const [popCount, setPopCount] = useState(0);
25+
26+
// Audio context for simple pop sound
27+
const [audioContext, setAudioContext] = useState(null);
28+
29+
useEffect(() => {
30+
setAudioContext(new (window.AudioContext || window.webkitAudioContext)());
31+
}, []);
32+
33+
const playPopSound = () => {
34+
if (!audioContext) return;
35+
36+
const oscillator = audioContext.createOscillator();
37+
const gainNode = audioContext.createGain();
38+
39+
oscillator.connect(gainNode);
40+
gainNode.connect(audioContext.destination);
41+
42+
oscillator.type = 'square'; // Change to square wave for a harsher sound
43+
oscillator.frequency.value = 100 + Math.random() * 200; // Lower frequency for more bass
44+
45+
const now = audioContext.currentTime;
46+
gainNode.gain.setValueAtTime(0.5, now); // Start louder
47+
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.08); // Faster and shorter decay
48+
49+
oscillator.start(now);
50+
oscillator.stop(now + 0.08); // Very short duration
51+
};
52+
53+
const popBubble = (index) => {
54+
if (bubbles[index]) return; // Already popped
55+
56+
const newBubbles = [...bubbles];
57+
newBubbles[index] = true;
58+
setBubbles(newBubbles);
59+
setPopCount((prev) => prev + 1);
60+
playPopSound();
61+
};
62+
63+
const resetBubbles = () => {
64+
setBubbles(Array(BUBBLE_COUNT).fill(false));
65+
setPopCount(0);
66+
};
67+
68+
const cardStyle = {
69+
backgroundColor: colors['app-alpha-10'],
70+
borderColor: colors['app-alpha-50'],
71+
color: colors.app,
72+
};
73+
74+
return (
75+
<div className="py-16 sm:py-24">
76+
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
77+
<Link
78+
to="/apps"
79+
className="text-article hover:underline flex items-center justify-center gap-2 text-lg mb-4"
80+
>
81+
<ArrowLeftIcon size={24}/> Back to Apps
82+
</Link>
83+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
84+
<span className="codex-color">fc</span>
85+
<span className="separator-color">::</span>
86+
<span className="apps-color">apps</span>
87+
<span className="separator-color">::</span>
88+
<span className="single-app-color">pop</span>
89+
</h1>
90+
<hr className="border-gray-700"/>
91+
<div className="flex justify-center items-center mt-16">
92+
<div
93+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform overflow-hidden h-full w-full max-w-3xl"
94+
style={cardStyle}
95+
>
96+
<div
97+
className="absolute top-0 left-0 w-full h-full opacity-10"
98+
style={{
99+
backgroundImage:
100+
'radial-gradient(circle, white 1px, transparent 1px)',
101+
backgroundSize: '10px 10px',
102+
}}
103+
></div>
104+
<div className="relative z-10 p-1 text-center">
105+
<h1 className="text-3xl font-arvo font-normal mb-4 text-app flex items-center justify-center gap-2">
106+
<CirclesFourIcon size={32}/> Bubble Wrap
107+
</h1>
108+
<hr className="border-gray-700 mb-6"/>
109+
110+
<div className="flex justify-between items-center mb-6 px-4">
111+
<div className="text-xl font-bold">Popped: {popCount}</div>
112+
<button
113+
onClick={resetBubbles}
114+
className="px-4 py-2 rounded-md text-sm font-arvo font-normal border transition-colors duration-300 hover:bg-white/10"
115+
style={{borderColor: cardStyle.color, color: cardStyle.color}}
116+
>
117+
Get a fresh sheet
118+
</button>
119+
</div>
120+
121+
<div className="grid grid-cols-5 sm:grid-cols-10 gap-2 justify-items-center">
122+
{bubbles.map((isPopped, index) => (
123+
<button
124+
key={index}
125+
onClick={() => popBubble(index)}
126+
className={`w-12 h-12 rounded-full border-2 transition-all duration-100 relative overflow-hidden focus:outline-none
127+
${isPopped
128+
? 'bg-transparent scale-95 opacity-50 border-gray-600'
129+
: 'bg-white/10 hover:bg-white/20 scale-100 cursor-pointer'
130+
}`}
131+
style={{
132+
borderColor: isPopped ? 'gray' : cardStyle.color,
133+
boxShadow: isPopped ? 'inset 0 0 5px rgba(0,0,0,0.5)' : '0 4px 6px rgba(0,0,0,0.3)'
134+
}}
135+
>
136+
{!isPopped && (
137+
<div className="absolute top-2 left-2 w-3 h-3 bg-white rounded-full opacity-30 blur-[1px]"></div>
138+
)}
139+
</button>
140+
))}
141+
</div>
142+
143+
</div>
144+
</div>
145+
</div>
146+
</div>
147+
</div>
148+
);
149+
};
150+
151+
export default BubbleWrapPage;

src/pages/apps/CardGamePage.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ const CardGamePage = () => {
148148
if (!card)
149149
return (
150150
<div
151-
className={`card-placeholder ${isNext ? 'next-card-placeholder' : ''}`}
151+
className={`playing-card-placeholder ${isNext ? 'next-playing-card-placeholder' : ''}`}
152152
>
153153
?
154154
</div>
@@ -157,14 +157,14 @@ const CardGamePage = () => {
157157
const isRed = card.suit === '♥' || card.suit === '♦';
158158
return (
159159
<div
160-
className={`card ${isRed ? 'red' : 'black'} ${isNext ? 'next-card' : ''}`}
160+
className={`playing-card ${isRed ? 'red' : 'black'} ${isNext ? 'next-playing-card' : ''}`}
161161
>
162-
<div className="card-corner top-left">
162+
<div className="playing-card-corner top-left">
163163
<span className="rank">{card.rank}</span>
164164
<span className="suit">{card.suit}</span>
165165
</div>
166-
<div className="card-suit-center">{card.suit}</div>
167-
<div className="card-corner bottom-right">
166+
<div className="playing-card-suit-center">{card.suit}</div>
167+
<div className="playing-card-corner bottom-right">
168168
<span className="rank">{card.rank}</span>
169169
<span className="suit">{card.suit}</span>
170170
</div>
@@ -181,7 +181,7 @@ const CardGamePage = () => {
181181
>
182182
<ArrowLeftIcon size={24} /> Back to Apps
183183
</Link>
184-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
184+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
185185
<span className="codex-color">fc</span>
186186
<span className="separator-color">::</span>
187187
<span className="apps-color">apps</span>

src/pages/apps/CaseConverterPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function CaseConverterPage() {
8080
>
8181
<ArrowLeftIcon size={24} /> Back to Apps
8282
</Link>
83-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
83+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
8484
<span className="codex-color">fc</span>
8585
<span className="separator-color">::</span>
8686
<span className="apps-color">apps</span>

src/pages/apps/CodenameGeneratorPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ const CodenameGeneratorPage = () => {
287287
>
288288
<ArrowLeftIcon size={24} /> Back to Apps
289289
</Link>
290-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
290+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
291291
<span className="codex-color">fc</span>
292292
<span className="separator-color">::</span>
293293
<span className="apps-color">apps</span>

src/pages/apps/ColorContrastCheckerPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const ColorContrastCheckerPage = () => {
108108
>
109109
<ArrowLeftIcon size={24} /> Back to Apps
110110
</Link>
111-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
111+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
112112
<span className="codex-color">fc</span>
113113
<span className="separator-color">::</span>
114114
<span className="apps-color">apps</span>

src/pages/apps/ColorPaletteGeneratorPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function ColorPaletteGeneratorPage() {
6363
>
6464
<ArrowLeftIcon size={24} /> Back to Apps
6565
</Link>
66-
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
66+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
6767
<span className="codex-color">fc</span>
6868
<span className="separator-color">::</span>
6969
<span className="apps-color">apps</span>

0 commit comments

Comments
 (0)