Skip to content

Commit 2cb6ca1

Browse files
committed
Fix major UI issues and begin Help page updates
- Fix header: smaller football SVG (w-6 h-6) and add LinkedIn icon - Fix footer: replace SB logo with football SVG - Fix Season Stats: improve data loading with better error handling and console logging - Fix EnhancedVisualizations: update season labels to reflect completed 2024-2025 season - Begin Help page updates: new sections structure and getting started content - All changes reflect September 2025 context (completed season)
1 parent 2c3795c commit 2cb6ca1

18 files changed

Lines changed: 262 additions & 29 deletions

.DS_Store

10 KB
Binary file not shown.

MatrixRain.tsx

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
"use client";
2+
3+
import { useEffect, useRef, useCallback } from "react";
4+
import { animate as anime, createTimeline, createTimer, Timeline, Target } from 'animejs';
5+
import { animeEasings, durations } from "@/lib/easings";
6+
import { useReducedMotion } from "@/hooks/useReducedMotion";
7+
8+
export default function MatrixRain() {
9+
const canvasRef = useRef<HTMLCanvasElement>(null);
10+
const particlesRef = useRef<HTMLDivElement>(null);
11+
const animationRef = useRef<Timeline | null>(null);
12+
const prefersReducedMotion = useReducedMotion();
13+
14+
const createFloatingLayers = useCallback(() => {
15+
if (!particlesRef.current || prefersReducedMotion) return;
16+
17+
const layers = 3;
18+
const particlesPerLayer = 5;
19+
20+
for (let layer = 0; layer < layers; layer++) {
21+
const layerDiv = document.createElement("div");
22+
layerDiv.className = "matrix-layer";
23+
layerDiv.style.position = "absolute";
24+
layerDiv.style.inset = "0";
25+
layerDiv.style.pointerEvents = "none";
26+
particlesRef.current.appendChild(layerDiv);
27+
28+
for (let i = 0; i < particlesPerLayer; i++) {
29+
const particle = document.createElement("div");
30+
particle.className = "matrix-particle";
31+
particle.style.position = "absolute";
32+
particle.style.width = "2px";
33+
particle.style.height = `${Math.random() * 100 + 50}px`;
34+
particle.style.background = `linear-gradient(to bottom, transparent, rgba(0, 255, 0, ${0.1 + layer * 0.1}), transparent)`;
35+
particle.style.left = `${Math.random() * 100}%`;
36+
particle.style.top = "-100px";
37+
particle.style.filter = `blur(${layer}px)`;
38+
layerDiv.appendChild(particle);
39+
40+
anime(particle, {
41+
translateY: window.innerHeight + 200,
42+
duration: durations.slowest * (3 + layer),
43+
delay: Math.random() * durations.slowest * 2,
44+
easing: "linear",
45+
loop: true
46+
});
47+
48+
anime(particle, {
49+
opacity: [0, 0.5, 0],
50+
duration: durations.slowest * (3 + layer),
51+
delay: Math.random() * durations.slowest * 2,
52+
ease: animeEasings.smoothInOut,
53+
loop: true
54+
});
55+
}
56+
57+
anime(layerDiv, {
58+
translateZ: `${layer * -100}px`,
59+
duration: 0
60+
});
61+
}
62+
}, [prefersReducedMotion]);
63+
64+
const animateCanvasOpacity = useCallback(() => {
65+
if (!canvasRef.current || prefersReducedMotion) return;
66+
67+
animationRef.current = createTimeline({
68+
loop: true,
69+
});
70+
71+
animationRef.current
72+
.add(
73+
canvasRef.current,
74+
{
75+
opacity: [0.3, 0.5],
76+
duration: durations.slowest * 2,
77+
ease: animeEasings.smoothInOut,
78+
}
79+
)
80+
.add(
81+
canvasRef.current,
82+
{
83+
opacity: [0.5, 0.3],
84+
duration: durations.slowest * 2,
85+
ease: animeEasings.smoothInOut,
86+
}
87+
);
88+
}, [prefersReducedMotion]);
89+
90+
useEffect(() => {
91+
if (prefersReducedMotion) return;
92+
93+
const canvas = canvasRef.current;
94+
if (!canvas) return;
95+
96+
const ctx = canvas.getContext("2d");
97+
if (!ctx) return;
98+
99+
const resizeCanvas = () => {
100+
canvas.width = window.innerWidth;
101+
canvas.height = window.innerHeight;
102+
};
103+
104+
resizeCanvas();
105+
window.addEventListener("resize", resizeCanvas);
106+
107+
const fontSize = 14;
108+
const columns = Math.floor(canvas.width / fontSize);
109+
const drops: number[] = Array(columns).fill(1);
110+
111+
const matrixChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789@#$%^&*()*&^%+-/~{[|`]}";
112+
const charArray = matrixChars.split("");
113+
114+
ctx.font = `${fontSize}px JetBrains Mono, monospace`;
115+
116+
const drawMatrix = () => {
117+
ctx.fillStyle = "rgba(10, 10, 10, 0.08)";
118+
ctx.fillRect(0, 0, canvas.width, canvas.height);
119+
120+
for (let i = 0; i < drops.length; i++) {
121+
if (Math.random() > 0.975) {
122+
const text = charArray[Math.floor(Math.random() * charArray.length)];
123+
const x = i * fontSize;
124+
const y = drops[i] * fontSize;
125+
126+
const intensity = Math.random();
127+
if (intensity > 0.9) {
128+
ctx.fillStyle = "rgba(0, 255, 255, 0.8)";
129+
} else if (intensity > 0.7) {
130+
ctx.fillStyle = "rgba(0, 255, 0, 0.5)";
131+
} else {
132+
ctx.fillStyle = "rgba(0, 255, 0, 0.15)";
133+
}
134+
135+
ctx.fillText(text, x, y);
136+
137+
if (y > canvas.height && Math.random() > 0.95) {
138+
drops[i] = 0;
139+
}
140+
141+
drops[i]++;
142+
}
143+
}
144+
};
145+
146+
const matrixAnimation = createTimer({
147+
duration: Infinity,
148+
onUpdate: drawMatrix,
149+
frameRate: 30
150+
});
151+
152+
createFloatingLayers();
153+
animateCanvasOpacity();
154+
155+
return () => {
156+
window.removeEventListener("resize", resizeCanvas);
157+
matrixAnimation.pause();
158+
if (animationRef.current) {
159+
animationRef.current.pause();
160+
}
161+
};
162+
}, [prefersReducedMotion, animateCanvasOpacity, createFloatingLayers]);
163+
164+
useEffect(() => {
165+
if (!particlesRef.current) return;
166+
167+
const handleMouseMove = (e: MouseEvent) => {
168+
if (prefersReducedMotion) return;
169+
170+
const x = (e.clientX / window.innerWidth - 0.5) * 20;
171+
const y = (e.clientY / window.innerHeight - 0.5) * 20;
172+
173+
const layers = particlesRef.current?.querySelectorAll(".matrix-layer");
174+
if (layers) {
175+
anime(layers, {
176+
translateX: (_el: Target, i: number) => x * (i + 1) * 0.5,
177+
translateY: (_el: Target, i: number) => y * (i + 1) * 0.5,
178+
duration: durations.slow,
179+
easing: animeEasings.smoothOut
180+
});
181+
}
182+
};
183+
184+
window.addEventListener("mousemove", handleMouseMove);
185+
186+
return () => {
187+
window.removeEventListener("mousemove", handleMouseMove);
188+
};
189+
}, [prefersReducedMotion]);
190+
191+
if (prefersReducedMotion) {
192+
return (
193+
<div className="fixed inset-0 pointer-events-none z-0">
194+
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-green-900/5 to-transparent" />
195+
</div>
196+
);
197+
}
198+
199+
return (
200+
<div className="fixed inset-0 pointer-events-none z-0">
201+
<canvas
202+
ref={canvasRef}
203+
className="absolute inset-0"
204+
style={{ opacity: 0.4 }}
205+
/>
206+
<div
207+
ref={particlesRef}
208+
className="absolute inset-0"
209+
style={{ perspective: "1000px", transformStyle: "preserve-3d" }}
210+
/>
211+
<div className="absolute inset-0 bg-gradient-radial from-transparent via-transparent to-black/50" />
212+
</div>
213+
);
214+
}
3.31 KB
Binary file not shown.
152 Bytes
Binary file not shown.
6.26 KB
Binary file not shown.
8.95 KB
Binary file not shown.
7.34 KB
Binary file not shown.
Binary file not shown.

backend/chroma_db/chroma.sqlite3

0 Bytes
Binary file not shown.
152 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)