Skip to content

Commit 9ab84f6

Browse files
committed
feat: more styles.
1 parent d849cb5 commit 9ab84f6

File tree

10 files changed

+407
-31
lines changed

10 files changed

+407
-31
lines changed

src/components/CommandPalette.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ const CommandPalette = ({
7878
toggleHellenic,
7979
isGlitch,
8080
toggleGlitch,
81+
toggleRain,
82+
isGarden,
83+
toggleGarden,
84+
isAutumn,
85+
toggleAutumn,
86+
isRain,
8187
} = useVisualSettings();
8288

8389
const { unlockAchievement } = useAchievements();
@@ -231,6 +237,7 @@ const CommandPalette = ({
231237
);
232238
break;
233239
case 'doBarrelRoll':
240+
unlockAchievement('do_a_barrel_roll');
234241
document.body.classList.add('do-a-barrel-roll');
235242
addToast({
236243
title: 'Wheeeee!',
@@ -377,6 +384,30 @@ const CommandPalette = ({
377384
duration: 2000,
378385
});
379386
break;
387+
case 'toggleGardenMode':
388+
toggleGarden();
389+
addToast({
390+
title: !isGarden ? 'Garden Mode On' : 'Garden Mode Off',
391+
message: !isGarden ? 'Bloom where you are planted.' : 'Winter is coming.',
392+
duration: 2000,
393+
});
394+
break;
395+
case 'toggleAutumnMode':
396+
toggleAutumn();
397+
addToast({
398+
title: !isAutumn ? 'Autumn Mode On' : 'Autumn Mode Off',
399+
message: !isAutumn ? 'The leaves are falling.' : 'Spring has sprung.',
400+
duration: 2000,
401+
});
402+
break;
403+
case 'toggleRainMode':
404+
toggleRain();
405+
addToast({
406+
title: !isRain ? 'Rain Mode On' : 'Rain Mode Off',
407+
message: !isRain ? 'It\'s raining, it\'s pouring.' : 'The sun is out.',
408+
duration: 2000,
409+
});
410+
break;
380411
case 'showTime': {
381412
unlockAchievement('time_teller');
382413
openGenericModal('Current Time', <LiveClock />);

src/components/DigitalFlowers.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { motion } from 'framer-motion';
3+
4+
const FLOWER_TYPES = [
5+
// Type 1: Simple Daisy-like
6+
(color) => (
7+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
8+
<path d="M50 50 Q50 20 50 10 Q50 20 60 40 Z" fill={color} />
9+
<path d="M50 50 Q80 50 90 50 Q80 50 60 60 Z" fill={color} />
10+
<path d="M50 50 Q50 80 50 90 Q50 80 40 60 Z" fill={color} />
11+
<path d="M50 50 Q20 50 10 50 Q20 50 40 40 Z" fill={color} />
12+
<path d="M50 50 Q30 30 20 20 Q30 30 45 45 Z" fill={color} />
13+
<path d="M50 50 Q70 30 80 20 Q70 30 55 45 Z" fill={color} />
14+
<path d="M50 50 Q70 70 80 80 Q70 70 55 55 Z" fill={color} />
15+
<path d="M50 50 Q30 70 20 80 Q30 70 45 55 Z" fill={color} />
16+
<circle cx="50" cy="50" r="10" fill="#fbbf24" />
17+
</svg>
18+
),
19+
// Type 2: Tulip-like
20+
(color) => (
21+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
22+
<path d="M30 40 Q30 80 50 90 Q70 80 70 40 Q50 50 30 40 Z" fill={color} />
23+
<path d="M30 40 Q40 20 50 40 Q60 20 70 40" fill={color} />
24+
<path d="M50 90 L50 150" stroke="#166534" strokeWidth="4" />
25+
</svg>
26+
),
27+
// Type 3: Round
28+
(color) => (
29+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
30+
<circle cx="50" cy="50" r="30" fill={color} opacity="0.8" />
31+
<circle cx="50" cy="50" r="20" fill="#fff" opacity="0.3" />
32+
<path d="M50 80 L50 150" stroke="#166534" strokeWidth="4" />
33+
</svg>
34+
)
35+
];
36+
37+
const COLORS = ['#f472b6', '#c084fc', '#60a5fa', '#f87171', '#fbbf24'];
38+
39+
const DigitalFlowers = () => {
40+
const [flowers, setFlowers] = useState([]);
41+
42+
useEffect(() => {
43+
// Generate random flowers
44+
const newFlowers = Array.from({ length: 40 }).map((_, i) => ({
45+
id: i,
46+
left: Math.random() * 100, // percentage
47+
size: 60 + Math.random() * 80, // px (Increased size from 40-100 to 60-140)
48+
delay: Math.random() * 2, // seconds
49+
type: Math.floor(Math.random() * FLOWER_TYPES.length),
50+
color: COLORS[Math.floor(Math.random() * COLORS.length)],
51+
rotation: Math.random() * 30 - 15, // degrees
52+
}));
53+
setFlowers(newFlowers);
54+
}, []);
55+
56+
return (
57+
<div className="fixed bottom-0 left-0 w-full h-0 z-50 pointer-events-none">
58+
{flowers.map((flower) => (
59+
<motion.div
60+
key={flower.id}
61+
initial={{ y: 200, opacity: 0, rotate: flower.rotation }}
62+
animate={{
63+
y: 0,
64+
opacity: 1,
65+
rotate: [flower.rotation - 5, flower.rotation + 5, flower.rotation - 5]
66+
}}
67+
transition={{
68+
y: { duration: 1.5, delay: flower.delay, type: 'spring', stiffness: 50 },
69+
opacity: { duration: 1.5, delay: flower.delay },
70+
rotate: {
71+
duration: 3 + Math.random() * 2,
72+
repeat: Infinity,
73+
ease: "easeInOut",
74+
delay: flower.delay + 0.5
75+
}
76+
}}
77+
style={{
78+
position: 'absolute',
79+
left: `${flower.left}%`,
80+
bottom: -20,
81+
width: flower.size,
82+
height: flower.size * 1.5, // Make them taller
83+
transformOrigin: 'bottom center',
84+
}}
85+
>
86+
{FLOWER_TYPES[flower.type](flower.color)}
87+
</motion.div>
88+
))}
89+
</div>
90+
);
91+
};
92+
93+
export default DigitalFlowers;

src/components/DigitalLeaves.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { motion } from 'framer-motion';
3+
4+
const LEAF_TYPES = [
5+
// Type 1: Maple Leafish
6+
(color) => (
7+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
8+
<path d="M50 10 Q60 30 80 30 Q70 50 90 60 Q60 60 50 90 Q40 60 10 60 Q30 50 20 30 Q40 30 50 10 Z" fill={color} />
9+
<path d="M50 20 L50 80" stroke={color} strokeWidth="2" strokeOpacity="0.5" />
10+
</svg>
11+
),
12+
// Type 2: Oak Leafish
13+
(color) => (
14+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
15+
<path d="M50 10 Q70 10 70 30 Q80 30 80 50 Q70 50 70 70 Q60 90 50 90 Q40 90 30 70 Q30 50 20 50 Q20 30 30 30 Q30 10 50 10 Z" fill={color} />
16+
<path d="M50 15 L50 85" stroke={color} strokeWidth="2" strokeOpacity="0.5" />
17+
</svg>
18+
),
19+
// Type 3: Simple Ellipse Leaf
20+
(color) => (
21+
<svg viewBox="0 0 100 100" width="100%" height="100%" className="overflow-visible">
22+
<path d="M50 10 Q90 50 50 90 Q10 50 50 10 Z" fill={color} />
23+
<path d="M50 10 L50 90" stroke={color} strokeWidth="2" strokeOpacity="0.5" />
24+
</svg>
25+
)
26+
];
27+
28+
const COLORS = ['#eab308', '#f97316', '#ef4444', '#a16207', '#d97706']; // Yellows, Oranges, Reds, Browns
29+
30+
const DigitalLeaves = () => {
31+
const [leaves, setLeaves] = useState([]);
32+
33+
useEffect(() => {
34+
// Generate random leaves
35+
const newLeaves = Array.from({ length: 50 }).map((_, i) => ({
36+
id: i,
37+
left: Math.random() * 100, // percentage
38+
size: 30 + Math.random() * 40, // px
39+
delay: Math.random() * 5, // seconds
40+
duration: 10 + Math.random() * 10, // seconds to fall
41+
type: Math.floor(Math.random() * LEAF_TYPES.length),
42+
color: COLORS[Math.floor(Math.random() * COLORS.length)],
43+
rotation: Math.random() * 360,
44+
}));
45+
setLeaves(newLeaves);
46+
}, []);
47+
48+
return (
49+
<div className="fixed top-0 left-0 w-full h-full z-50 pointer-events-none overflow-hidden">
50+
{leaves.map((leaf) => (
51+
<motion.div
52+
key={leaf.id}
53+
initial={{
54+
y: -100,
55+
x: 0,
56+
opacity: 0,
57+
rotate: leaf.rotation
58+
}}
59+
animate={{
60+
y: '110vh', // Fall off screen
61+
x: [0, 50, -50, 20, 0], // Swaying motion
62+
opacity: [0, 1, 1, 0], // Fade in then out at bottom
63+
rotate: leaf.rotation + 360 // Spin while falling
64+
}}
65+
transition={{
66+
y: {
67+
duration: leaf.duration,
68+
repeat: Infinity,
69+
delay: leaf.delay,
70+
ease: "linear"
71+
},
72+
x: {
73+
duration: leaf.duration,
74+
repeat: Infinity,
75+
delay: leaf.delay,
76+
ease: "easeInOut"
77+
},
78+
opacity: {
79+
duration: leaf.duration,
80+
repeat: Infinity,
81+
delay: leaf.delay,
82+
times: [0, 0.1, 0.9, 1]
83+
},
84+
rotate: {
85+
duration: leaf.duration,
86+
repeat: Infinity,
87+
delay: leaf.delay,
88+
ease: "linear"
89+
}
90+
}}
91+
style={{
92+
position: 'absolute',
93+
left: `${leaf.left}%`,
94+
width: leaf.size,
95+
height: leaf.size,
96+
}}
97+
>
98+
{LEAF_TYPES[leaf.type](leaf.color)}
99+
</motion.div>
100+
))}
101+
</div>
102+
);
103+
};
104+
105+
export default DigitalLeaves;

src/components/Layout.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { useLocation } from 'react-router-dom';
88
import Search from './Search';
99
import CommandPalette from './CommandPalette';
1010
import { useCommandPalette } from '../context/CommandPaletteContext';
11+
import { useVisualSettings } from '../context/VisualSettingsContext';
12+
import DigitalFlowers from './DigitalFlowers';
13+
import DigitalLeaves from './DigitalLeaves';
14+
import NaturalRain from './NaturalRain';
1115

1216
import { DndProvider } from '../context/DndContext';
1317

@@ -21,6 +25,7 @@ const Layout = ({
2125
}) => {
2226
const [isSidebarOpen, setIsSidebarOpen] = useState(window.innerWidth > 768);
2327
const { isPaletteOpen, setIsPaletteOpen } = useCommandPalette();
28+
const { isGarden, isAutumn, isRain } = useVisualSettings();
2429
const location = useLocation();
2530

2631
useEffect(() => {
@@ -55,6 +60,9 @@ const Layout = ({
5560

5661
return (
5762
<>
63+
{isGarden && <DigitalFlowers />}
64+
{isAutumn && <DigitalLeaves />}
65+
{isRain && <NaturalRain />}
5866
<CommandPalette
5967
isOpen={isPaletteOpen}
6068
setIsOpen={setIsPaletteOpen}

src/components/NaturalRain.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { motion } from 'framer-motion';
3+
4+
const NaturalRain = () => {
5+
const [raindrops, setRaindrops] = useState([]);
6+
7+
useEffect(() => {
8+
// Generate random raindrops
9+
const newRaindrops = Array.from({ length: 100 }).map((_, i) => ({
10+
id: i,
11+
left: Math.random() * 100, // percentage
12+
size: 1 + Math.random() * 2, // px (width/height of drop)
13+
height: 10 + Math.random() * 20, // px (length of the rain streak)
14+
delay: Math.random() * 5, // seconds
15+
duration: 1 + Math.random() * 1.5, // seconds to fall
16+
opacity: 0.2 + Math.random() * 0.6,
17+
color: `rgba(173, 216, 230, ${0.3 + Math.random() * 0.7})`, // Light blue, semi-transparent
18+
}));
19+
setRaindrops(newRaindrops);
20+
}, []);
21+
22+
return (
23+
<div className="fixed top-0 left-0 w-full h-full z-50 pointer-events-none overflow-hidden">
24+
{raindrops.map((drop) => (
25+
<motion.div
26+
key={drop.id}
27+
initial={{
28+
y: -drop.height, // Start above the screen
29+
x: 0,
30+
opacity: drop.opacity,
31+
}}
32+
animate={{
33+
y: '110vh', // Fall off screen
34+
x: Math.random() * 20 - 10, // Slight horizontal drift
35+
opacity: [drop.opacity, drop.opacity, 0], // Fade out at bottom
36+
}}
37+
transition={{
38+
y: {
39+
duration: drop.duration,
40+
repeat: Infinity,
41+
delay: drop.delay,
42+
ease: "linear",
43+
},
44+
x: {
45+
duration: drop.duration,
46+
repeat: Infinity,
47+
delay: drop.delay,
48+
ease: "easeInOut",
49+
},
50+
opacity: {
51+
duration: drop.duration,
52+
repeat: Infinity,
53+
delay: drop.delay,
54+
times: [0, 0.9, 1], // Fade out near the end
55+
},
56+
}}
57+
style={{
58+
position: 'absolute',
59+
left: `${drop.left}%`,
60+
width: drop.size,
61+
height: drop.height,
62+
backgroundColor: drop.color,
63+
borderRadius: '50%', // Make them more like drops
64+
filter: 'blur(0.5px)', // Soften the edges
65+
}}
66+
/>
67+
))}
68+
</div>
69+
);
70+
};
71+
72+
export default NaturalRain;

src/config/achievements.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import {
2929
DiamondIcon,
3030
EyeIcon,
3131
TargetIcon,
32-
BrainIcon
32+
BrainIcon,
33+
YinYangIcon,
34+
ArrowsClockwiseIcon
3335
} from '@phosphor-icons/react';
3436

3537
export const ACHIEVEMENTS = [
@@ -664,6 +666,20 @@ export const ACHIEVEMENTS = [
664666
icon: <PhoneIcon size={32} weight="duotone" />,
665667
category: 'Secret',
666668
},
669+
{
670+
id: 'zen',
671+
title: 'Zen',
672+
description: 'Turned it into a proper garden.',
673+
icon: <YinYangIcon size={32} weight="duotone" />,
674+
category: 'Secret',
675+
},
676+
{
677+
id: 'do_a_barrel_roll',
678+
title: 'My Head Spinning',
679+
description: 'You spin my head right round, right round.',
680+
icon: <ArrowsClockwiseIcon size={32} weight="duotone" />,
681+
category: 'Secret',
682+
},
667683
{
668684
id: 'clean_slate',
669685
title: 'Clean Slate',

0 commit comments

Comments
 (0)