Skip to content

Commit 0b816e9

Browse files
authored
Merge pull request #42 from khaykingleb/move-to-react-router-v7
New website layout
2 parents e89b06a + d995508 commit 0b816e9

26 files changed

+776
-693
lines changed

.cursorrules

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
* Use Remix V2 syntax
22
* Use TSDoc specification for docstrings
33
* Use TailwindCSS for styling
4+
* Use DaisyUI for components

app/components/atoms/Copyright.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
*/
66
export const Copyright = () => {
77
return (
8-
<div className="font-gill-sans mb-2 text-center">
9-
<p>&copy; 2024 Gleb Khaykin</p>
8+
<div className="mt-2 text-center text-sm">
9+
<p>&copy; 2025 Gleb Khaykin</p>
1010
</div>
1111
);
1212
};

app/components/atoms/LoadingSpinner.tsx

-12
This file was deleted.
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useEffect, useRef } from "react";
2+
3+
/**
4+
* A component that renders an animated ASCII art donut
5+
* Based on the donut math by Andy Sloane
6+
* @see https://www.a1k0n.net/2011/07/20/donut-math.html
7+
* @returns The ASCII art donut component
8+
*/
9+
export function AsciiDonut() {
10+
const canvasRef = useRef<HTMLPreElement>(null);
11+
12+
useEffect(() => {
13+
const theta_spacing = 0.07;
14+
const phi_spacing = 0.02;
15+
16+
const R1 = 1;
17+
const R2 = 2;
18+
const K2 = 5;
19+
20+
/**
21+
* Render a single frame of the ASCII art donut
22+
* @param A - The angle for the torus rotation
23+
* @param B - The angle for the torus rotation
24+
* @returns The ASCII art string for the frame
25+
*/
26+
function renderFrame(A: number, B: number) {
27+
const screenWidth = 100;
28+
const screenHeight = 100;
29+
const K1 = (screenWidth * K2 * 3) / (8 * (R1 + R2));
30+
31+
const cosA = Math.cos(A),
32+
sinA = Math.sin(A);
33+
const cosB = Math.cos(B),
34+
sinB = Math.sin(B);
35+
36+
const chars = ".,-~:;=!*#$@".split("");
37+
const output: string[] = new Array(screenWidth * screenHeight).fill(" ");
38+
const zbuffer: number[] = new Array(screenWidth * screenHeight).fill(0);
39+
40+
for (let theta = 0; theta < 2 * Math.PI; theta += theta_spacing) {
41+
const costheta = Math.cos(theta),
42+
sintheta = Math.sin(theta);
43+
44+
for (let phi = 0; phi < 2 * Math.PI; phi += phi_spacing) {
45+
const cosphi = Math.cos(phi),
46+
sinphi = Math.sin(phi);
47+
48+
const circlex = R2 + R1 * costheta;
49+
const circley = R1 * sintheta;
50+
51+
const x =
52+
circlex * (cosB * cosphi + sinA * sinB * sinphi) -
53+
circley * cosA * sinB;
54+
const y =
55+
circlex * (sinB * cosphi - sinA * cosB * sinphi) +
56+
circley * cosA * cosB;
57+
const z = K2 + cosA * circlex * sinphi + circley * sinA;
58+
const ooz = 1 / z;
59+
60+
const xp = Math.floor(screenWidth / 2 + K1 * ooz * x);
61+
const yp = Math.floor(screenHeight / 2 - K1 * ooz * y);
62+
63+
const L =
64+
cosphi * costheta * sinB -
65+
cosA * costheta * sinphi -
66+
sinA * sintheta +
67+
cosB * (cosA * sintheta - costheta * sinA * sinphi);
68+
69+
if (L > 0) {
70+
const idx = xp + yp * screenWidth;
71+
if (xp >= 0 && xp < screenWidth && yp >= 0 && yp < screenHeight) {
72+
if (ooz > zbuffer[idx]) {
73+
zbuffer[idx] = ooz;
74+
const luminance_index = Math.floor(L * 8);
75+
output[idx] =
76+
chars[
77+
Math.min(Math.max(luminance_index, 0), chars.length - 1)
78+
];
79+
}
80+
}
81+
}
82+
}
83+
}
84+
85+
return output.reduce((acc, char, i) => {
86+
if (i % screenWidth === 0) return acc + "\n" + char;
87+
return acc + char;
88+
}, "");
89+
}
90+
91+
let A_val = 0;
92+
let B_val = 0;
93+
94+
const animate = () => {
95+
if (canvasRef.current) {
96+
canvasRef.current.textContent = renderFrame(A_val, B_val);
97+
A_val += 0.005;
98+
B_val += 0.003;
99+
requestAnimationFrame(animate);
100+
}
101+
};
102+
103+
animate();
104+
}, []);
105+
106+
return (
107+
<pre
108+
ref={canvasRef}
109+
className="fixed left-[60%] top-1/2 -translate-x-1/2 -translate-y-1/2 font-mono text-[0.5rem] font-extralight leading-[0.5rem]"
110+
style={{ fontFamily: "Courier New" }}
111+
/>
112+
);
113+
}

app/components/molecules/MenuItems.tsx

-42
This file was deleted.

app/components/molecules/MobileMenuItems.tsx

-45
This file was deleted.
+48-47
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,60 @@
1-
import { AiFillInstagram } from "react-icons/ai";
21
import { FaGithub, FaLinkedin, FaTelegram } from "react-icons/fa";
32
import { FaSquareXTwitter } from "react-icons/fa6";
43
import { ImMail4 } from "react-icons/im";
54

5+
interface SocialMediaProps {
6+
size?: number;
7+
displayLabels?: boolean;
8+
}
9+
610
/**
711
* Social media component
812
*
913
* @returns Social media component
1014
*/
11-
export const SocialMedia = () => {
15+
export const SocialMedia = ({ size = 24 }: SocialMediaProps) => {
16+
const links = [
17+
{
18+
href: "https://github.com/khaykingleb",
19+
label: "khaykingleb",
20+
icon: FaGithub,
21+
},
22+
{
23+
href: "https://linkedin.com/in/khaykingleb",
24+
label: "khaykingleb",
25+
icon: FaLinkedin,
26+
},
27+
{
28+
href: "https://twitter.com/khaykingleb",
29+
label: "@khaykingleb",
30+
icon: FaSquareXTwitter,
31+
},
32+
{
33+
href: "https://t.me/khaykingleb_blog",
34+
label: "@khaykingleb_blog",
35+
icon: FaTelegram,
36+
},
37+
{
38+
href: "mailto:[email protected]",
39+
40+
icon: ImMail4,
41+
},
42+
];
43+
1244
return (
13-
<div className="mt-1 flex space-x-3">
14-
<a
15-
href="https://www.linkedin.com/in/khaykingleb"
16-
target="_blank"
17-
rel="noopener noreferrer"
18-
aria-label="LinkedIn"
19-
>
20-
<FaLinkedin className="h-6 w-6" />
21-
</a>
22-
<a
23-
href="https://github.com/khaykingleb"
24-
target="_blank"
25-
rel="noopener noreferrer"
26-
aria-label="GitHub"
27-
>
28-
<FaGithub className="h-6 w-6" />
29-
</a>
30-
<a
31-
href="https://t.me/khaykingleb_blog"
32-
target="_blank"
33-
rel="noopener noreferrer"
34-
aria-label="Telegram"
35-
>
36-
<FaTelegram className="h-6 w-6" />
37-
</a>
38-
<a
39-
href="https://x.com/khaykingleb"
40-
target="_blank"
41-
rel="noopener noreferrer"
42-
aria-label="X"
43-
>
44-
<FaSquareXTwitter className="h-6 w-6" />
45-
</a>
46-
<a
47-
href="https://instagram.com/khaykingleb"
48-
target="_blank"
49-
rel="noopener noreferrer"
50-
aria-label="Instagram"
51-
>
52-
<AiFillInstagram className="-mt-0.5 h-7 w-7" />
53-
</a>
54-
<a href="mailto:[email protected]" aria-label="Email">
55-
<ImMail4 className="h-6 w-6" />
56-
</a>
57-
</div>
45+
<>
46+
{links.map((link) => (
47+
<a
48+
key={link.href}
49+
href={link.href}
50+
className="flex items-center space-x-2 hover:text-primary"
51+
target="_blank"
52+
rel="noopener noreferrer"
53+
>
54+
<link.icon style={{ width: size, height: size }} />
55+
<span className="text-base">{link.label}</span>
56+
</a>
57+
))}
58+
</>
5859
);
5960
};

0 commit comments

Comments
 (0)