Skip to content

Commit 6feba5e

Browse files
Updated layout and design
1 parent cfba831 commit 6feba5e

File tree

4 files changed

+162
-57
lines changed

4 files changed

+162
-57
lines changed

public/servers.json

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,42 @@
55
{ "name": "Ohio, USA", "lat": 40.0, "lng": -83.0 },
66
{ "name": "N. California, USA", "lat": 37.35, "lng": -121.96 },
77
{ "name": "Oregon, USA", "lat": 45.52, "lng": -122.67 },
8+
{ "name": "Los Angeles, USA", "lat": 34.05, "lng": -118.24 },
9+
{ "name": "Chicago, USA", "lat": 41.88, "lng": -87.63 },
10+
{ "name": "Dallas, USA", "lat": 32.78, "lng": -96.80 },
811
{ "name": "Montreal, Canada", "lat": 45.5, "lng": -73.57 },
912
{ "name": "Toronto, Canada", "lat": 43.65, "lng": -79.38 },
1013
{ "name": "São Paulo, Brazil", "lat": -23.55, "lng": -46.63 },
14+
{ "name": "Santiago, Chile", "lat": -33.45, "lng": -70.67 },
15+
{ "name": "Bogotá, Colombia", "lat": 4.71, "lng": -74.07 },
1116
{ "name": "Ireland", "lat": 53.35, "lng": -6.26 },
1217
{ "name": "London, UK", "lat": 51.51, "lng": -0.13 },
1318
{ "name": "Frankfurt, Germany", "lat": 50.11, "lng": 8.68 },
1419
{ "name": "Paris, France", "lat": 48.86, "lng": 2.35 },
1520
{ "name": "Stockholm, Sweden", "lat": 59.33, "lng": 18.07 },
21+
{ "name": "Helsinki, Finland", "lat": 60.17, "lng": 24.94 },
1622
{ "name": "Milan, Italy", "lat": 45.46, "lng": 9.19 },
23+
{ "name": "Amsterdam, Netherlands", "lat": 52.37, "lng": 4.9 },
24+
{ "name": "Madrid, Spain", "lat": 40.42, "lng": -3.70 },
25+
{ "name": "Warsaw, Poland", "lat": 52.23, "lng": 21.01 },
26+
{ "name": "Zurich, Switzerland", "lat": 47.37, "lng": 8.54 },
1727
{ "name": "Cape Town, South Africa", "lat": -33.92, "lng": 18.42 },
28+
{ "name": "Johannesburg, South Africa", "lat": -26.20, "lng": 28.04 },
1829
{ "name": "Bahrain", "lat": 26.24, "lng": 50.59 },
1930
{ "name": "UAE", "lat": 25.27, "lng": 55.30 },
31+
{ "name": "Doha, Qatar", "lat": 25.28, "lng": 51.53 },
32+
{ "name": "Tel Aviv, Israel", "lat": 32.08, "lng": 34.78 },
2033
{ "name": "Mumbai, India", "lat": 19.08, "lng": 72.88 },
2134
{ "name": "Hyderabad, India", "lat": 17.38, "lng": 78.48 },
2235
{ "name": "Singapore", "lat": 1.35, "lng": 103.82 },
2336
{ "name": "Jakarta, Indonesia", "lat": -6.21, "lng": 106.82 },
37+
{ "name": "Kuala Lumpur, Malaysia", "lat": 3.14, "lng": 101.69 },
2438
{ "name": "Tokyo, Japan", "lat": 35.68, "lng": 139.76 },
2539
{ "name": "Osaka, Japan", "lat": 34.69, "lng": 135.5 },
2640
{ "name": "Seoul, South Korea", "lat": 37.57, "lng": 126.98 },
2741
{ "name": "Hong Kong", "lat": 22.32, "lng": 114.17 },
2842
{ "name": "Sydney, Australia", "lat": -33.87, "lng": 151.21 },
2943
{ "name": "Melbourne, Australia", "lat": -37.81, "lng": 144.96 },
30-
{ "name": "Amsterdam, Netherlands", "lat": 52.37, "lng": 4.9 },
31-
{ "name": "Madrid, Spain", "lat": 40.42, "lng": -3.70 },
32-
{ "name": "Warsaw, Poland", "lat": 52.23, "lng": 21.01 },
33-
{ "name": "Zurich, Switzerland", "lat": 47.37, "lng": 8.54 },
34-
{ "name": "Johannesburg, South Africa", "lat": -26.20, "lng": 28.04 },
35-
{ "name": "Los Angeles, USA", "lat": 34.05, "lng": -118.24 },
36-
{ "name": "Chicago, USA", "lat": 41.88, "lng": -87.63 },
37-
{ "name": "Dallas, USA", "lat": 32.78, "lng": -96.80 },
38-
{ "name": "Santiago, Chile", "lat": -33.45, "lng": -70.67 }
44+
{ "name": "Auckland, New Zealand", "lat": -36.85, "lng": 174.76 }
3945
]
4046
}

src/components/Globe3D.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ const Globe3D = () => {
7272
scene.add(globeGroup);
7373

7474
// --- Globe Layers ---
75+
76+
// MODIFICATION: Add an invisible sphere that occludes (hides) objects on the far side.
77+
// It's invisible because colorWrite is false, but it writes to the depth buffer.
78+
const occluder = new THREE.Mesh(
79+
new THREE.SphereGeometry(globeRadius, 64, 64),
80+
new THREE.MeshBasicMaterial({ colorWrite: false, depthWrite: true })
81+
);
82+
globeGroup.add(occluder);
7583

7684
globeGroup.add(new THREE.Mesh(
7785
new THREE.SphereGeometry(globeRadius, 64, 64),
@@ -104,7 +112,8 @@ const Globe3D = () => {
104112
}
105113
}
106114
const gridGeom = new THREE.BufferGeometry().setFromPoints(gridPoints);
107-
const gridMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.05 });
115+
// MODIFICATION: Set depthTest to false to make the grid visible through the globe.
116+
const gridMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.05, depthTest: false });
108117
const gridLines = new THREE.LineSegments(gridGeom, gridMaterial);
109118
globeGroup.add(gridLines);
110119

@@ -116,7 +125,8 @@ const Globe3D = () => {
116125
]).then(([countriesData, serverJson]) => {
117126
const servers: Server[] = serverJson.servers;
118127

119-
const outlineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.4 });
128+
// This material will now be properly occluded by the invisible sphere.
129+
const outlineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.75 });
120130
const outlinePoints: THREE.Vector3[] = [];
121131
countriesData.features.forEach((feature: any) => {
122132
if (feature.geometry?.type === 'LineString') {
@@ -158,6 +168,7 @@ const Globe3D = () => {
158168
const curve = new THREE.QuadraticBezierCurve3(start.vector, controlPoint, end.vector);
159169

160170
const tubeGeom = new THREE.TubeGeometry(curve, 32, 0.005, 8, false);
171+
// This material will now be properly occluded by the invisible sphere.
161172
const trailMaterial = new THREE.MeshBasicMaterial({ map: trailTexture, transparent: true, blending: THREE.AdditiveBlending, color: 0xffffff });
162173
globeGroup.add(new THREE.Mesh(tubeGeom, trailMaterial));
163174

src/components/Hero.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const Hero = () => {
4949
- `top-24` leaves space for a potential navbar.
5050
- `left-4` removes the large implicit margin.
5151
*/}
52-
<div className="absolute top-24 left-4 p-2 border border-green-400/30 rounded-lg bg-black/50 backdrop-blur-sm z-10 w-100% max-w-sm lg:max-w-md hidden lg:block">
52+
<div className="absolute top-24 left-20 p-2 border border-green-400/30 rounded-lg bg-black/50 backdrop-blur-sm z-10 w-100% max-w-sm lg:max-w-md hidden lg:block">
5353
<pre className="text-sm text-green-400 font-mono whitespace-pre-wrap">
5454
{`import { useState } from "react";
5555
@@ -76,14 +76,14 @@ export function hireDeveloper() {
7676
- `pointer-events-none` on the container allows clicks to pass through.
7777
- The Globe3D component itself now has `pointer-events-auto` via className.
7878
*/}
79-
<div className="absolute inset-y-0 right-0 w-[40vw] flex items-center justify-center pointer-events-none z-10">
79+
<div className="absolute inset-y-0 -right-20 top-28 w-[40vw] flex items-center justify-center pointer-events-none z-10">
8080
<div className="w-full aspect-square relative mr-16">
8181
<div className="absolute inset-0 bg-green-500/10 rounded-full blur-3xl"></div>
8282
{/* Globe component is inside a div to manage pointer events */}
8383
<div className="absolute inset-0 pointer-events-auto">
8484
<Globe3D />
8585
</div>
86-
<div className="absolute top-4 right-4 text-green-400 font-mono text-xs">
86+
<div className="absolute top-8 right-12 text-green-400 font-mono text-xs">
8787
// Global Network
8888
</div>
8989
</div>
Lines changed: 131 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,72 @@
1-
21
import { useEffect, useRef } from 'react';
32

4-
const MatrixBackground = () => {
3+
// --- For reusability, we can define props for customization ---
4+
interface ConsoleLogBackgroundProps {
5+
/** The font size of the log text */
6+
fontSize?: number;
7+
/** The interval in milliseconds to add a new line */
8+
newLineInterval?: number;
9+
/** Opacity of the canvas element */
10+
opacity?: number;
11+
}
12+
13+
// --- Define a type for our log lines for better structure ---
14+
interface LogLine {
15+
text: string;
16+
color: string;
17+
}
18+
19+
// --- A helper function to generate realistic, dynamic log lines ---
20+
const generateLogLine = (): LogLine => {
21+
const logLevels = [
22+
{ prefix: '[INFO]', color: '#888' },
23+
{ prefix: '[SUCCESS]', color: '#2E7D32' }, // Darker Green
24+
{ prefix: '[WARN]', color: '#ED6C02' }, // Orange
25+
{ prefix: '[ERROR]', color: '#D32F2F' }, // Red
26+
{ prefix: '[DEBUG]', color: '#0288D1' }, // Blue
27+
];
28+
29+
const actions = [
30+
'Initializing', 'Compiling', 'Fetching', 'Rendering', 'Connecting to', 'Authenticating',
31+
'Validating', 'Deploying', 'Optimizing', 'Resolving dependencies for'
32+
];
33+
34+
const subjects = [
35+
'CoreModule', 'AuthService', 'API_Endpoint:/v1/users', 'WebSocket', 'AssetPipeline',
36+
'Component:Header', 'ServiceWorker', 'GraphQL_Query:GetUser', 'Stylesheet:main.css'
37+
];
38+
39+
const statuses = ['...DONE', '...OK', '...FAILED', '...IN_PROGRESS'];
40+
41+
const level = logLevels[Math.floor(Math.random() * logLevels.length)];
42+
const action = actions[Math.floor(Math.random() * actions.length)];
43+
const subject = subjects[Math.floor(Math.random() * subjects.length)];
44+
const status = statuses[Math.floor(Math.random() * statuses.length)];
45+
const timestamp = new Date().toISOString();
46+
47+
// Make ERROR lines more distinct
48+
if (level.prefix === '[ERROR]') {
49+
return {
50+
text: `${timestamp} ${level.prefix} Failed to ${action.toLowerCase()} ${subject}. Status: ${status}`,
51+
color: level.color,
52+
};
53+
}
54+
55+
return {
56+
text: `${timestamp} ${level.prefix} ${action} ${subject} ${Math.random() > 0.7 ? status : ''}`,
57+
color: level.color,
58+
};
59+
};
60+
61+
62+
const ConsoleLogBackground = ({
63+
fontSize = 12,
64+
newLineInterval = 300, // Add a new line every 300ms
65+
opacity = 0.15,
66+
}: ConsoleLogBackgroundProps) => {
567
const canvasRef = useRef<HTMLCanvasElement>(null);
68+
// Use a ref for log lines to avoid re-renders on update
69+
const logLinesRef = useRef<LogLine[]>([]);
670

771
useEffect(() => {
872
const canvas = canvasRef.current;
@@ -11,62 +75,86 @@ const MatrixBackground = () => {
1175
const ctx = canvas.getContext('2d');
1276
if (!ctx) return;
1377

14-
const resizeCanvas = () => {
15-
canvas.width = window.innerWidth;
16-
canvas.height = window.innerHeight;
78+
let animationFrameId: number;
79+
let lastLineTime = 0;
80+
const lineHeight = fontSize * 1.5; // Spacing between lines
81+
82+
// --- This function initializes or re-initializes the canvas state ---
83+
const initialize = () => {
84+
canvas.width = window.innerWidth * window.devicePixelRatio;
85+
canvas.height = window.innerHeight * window.devicePixelRatio;
86+
canvas.style.width = `${window.innerWidth}px`;
87+
canvas.style.height = `${window.innerHeight}px`;
88+
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
89+
90+
// Pre-fill the screen with some lines so it's not empty on load
91+
const maxLines = Math.ceil(canvas.height / lineHeight);
92+
if (logLinesRef.current.length === 0) {
93+
for (let i = 0; i < maxLines; i++) {
94+
logLinesRef.current.unshift(generateLogLine());
95+
}
96+
}
1797
};
1898

19-
resizeCanvas();
20-
window.addEventListener('resize', resizeCanvas);
21-
22-
// Matrix characters
23-
const matrix = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789@#$%^&*()*&^%+-/~{[|`]}";
24-
const matrixArray = matrix.split("");
25-
26-
const fontSize = 10;
27-
const columns = canvas.width / fontSize;
28-
const drops: number[] = [];
29-
30-
// Initialize drops
31-
for (let x = 0; x < columns; x++) {
32-
drops[x] = 1;
33-
}
34-
35-
const draw = () => {
36-
// Semi-transparent background for trailing effect
37-
ctx.fillStyle = 'rgba(0, 0, 0, 0.04)';
38-
ctx.fillRect(0, 0, canvas.width, canvas.height);
99+
// --- The core animation function ---
100+
const draw = (timestamp: number) => {
101+
// Add a new line based on the interval
102+
if (timestamp - lastLineTime > newLineInterval) {
103+
lastLineTime = timestamp;
104+
logLinesRef.current.push(generateLogLine());
105+
106+
// Keep the array from growing infinitely
107+
const maxLines = Math.ceil(canvas.height / lineHeight) + 5; // +5 for buffer
108+
if (logLinesRef.current.length > maxLines) {
109+
logLinesRef.current.shift();
110+
}
111+
}
39112

40-
ctx.fillStyle = '#0F0';
113+
// Clear the canvas for the new frame
114+
ctx.clearRect(0, 0, canvas.width, canvas.height);
41115
ctx.font = `${fontSize}px monospace`;
42-
43-
for (let i = 0; i < drops.length; i++) {
44-
const text = matrixArray[Math.floor(Math.random() * matrixArray.length)];
45-
ctx.fillStyle = `rgba(0, 255, 0, ${Math.random() * 0.5 + 0.1})`;
46-
ctx.fillText(text, i * fontSize, drops[i] * fontSize);
47-
48-
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
49-
drops[i] = 0;
50-
}
51-
drops[i]++;
116+
117+
// Draw lines from the bottom up
118+
const lines = logLinesRef.current;
119+
for (let i = 0; i < lines.length; i++) {
120+
const line = lines[lines.length - 1 - i]; // Get line from end of array
121+
const y = canvas.height / window.devicePixelRatio - (i * lineHeight);
122+
123+
// Stop drawing if we're off-screen
124+
if (y < -lineHeight) break;
125+
126+
ctx.fillStyle = line.color;
127+
ctx.fillText(line.text, 10, y);
52128
}
53129
};
54130

55-
const interval = setInterval(draw, 35);
131+
const animate = (timestamp: number) => {
132+
draw(timestamp);
133+
animationFrameId = window.requestAnimationFrame(animate);
134+
};
135+
136+
const handleResize = () => {
137+
// Re-initialize canvas dimensions but keep existing logs
138+
initialize();
139+
};
140+
141+
initialize();
142+
window.addEventListener('resize', handleResize);
143+
animate(0); // Start the animation
56144

57145
return () => {
58-
clearInterval(interval);
59-
window.removeEventListener('resize', resizeCanvas);
146+
window.cancelAnimationFrame(animationFrameId);
147+
window.removeEventListener('resize', handleResize);
60148
};
61-
}, []);
149+
}, [fontSize, newLineInterval]);
62150

63151
return (
64152
<canvas
65153
ref={canvasRef}
66-
className="fixed inset-0 pointer-events-none z-0 opacity-20"
67-
style={{ background: 'transparent' }}
154+
className="fixed inset-0 pointer-events-none z-0"
155+
style={{ background: 'transparent', opacity }}
68156
/>
69157
);
70158
};
71159

72-
export default MatrixBackground;
160+
export default ConsoleLogBackground;

0 commit comments

Comments
 (0)