Skip to content

Commit b43a351

Browse files
committed
docs: ✏️ landing improved
1 parent 78808b5 commit b43a351

5 files changed

Lines changed: 70 additions & 225 deletions

File tree

documentation/src/components/landing/about/about.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ export const About = () => {
137137
<CheckCircle2 className="size-5 text-emerald-300" />
138138
</div>
139139
<div>
140-
<h3 className="text-base font-extrabold tracking-tight text-zinc-100">{problem.solutionTitle}</h3>
140+
<h3 className="text-base font-extrabold tracking-tight text-zinc-100">
141+
{problem.solutionTitle}
142+
</h3>
141143
<p className="mt-1 text-sm leading-6 text-zinc-400">{problem.solutionDescription}</p>
142144
</div>
143145
</div>
Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,12 @@
11
import { Animation } from "./animation.types";
22

3-
export const dotAnimation: Animation = ({ dot, timeline }) => {
4-
timeline.from(
5-
dot,
6-
{
7-
opacity: 0,
8-
scale: 0,
9-
},
10-
0,
11-
);
12-
timeline.to(
13-
dot,
14-
{
15-
opacity: 1,
16-
scale: 1,
17-
},
18-
1,
19-
);
20-
timeline.to(
21-
dot,
22-
{
23-
opacity: 1,
24-
scale: 1,
25-
},
26-
5,
27-
);
28-
timeline.to(
29-
dot,
30-
{
31-
opacity: 1,
32-
scale: 1,
33-
},
34-
8.5,
35-
);
36-
timeline.to(
37-
dot,
38-
{
39-
opacity: 0,
40-
scale: 0,
41-
},
42-
9.5,
43-
);
3+
export const dotAnimation: Animation = ({ dot, timeline, pathIndex }) => {
4+
// High pathIndex = top of screen (shorter paths), low pathIndex = bottom (longer paths)
5+
const t = 1 - (pathIndex - 1) / 31;
6+
const appearAt = 0.8 + t * 0.4;
7+
const fadeOutAt = 5.4 + t * 1.6;
8+
9+
timeline.set(dot, { autoAlpha: 0, scale: 0 }, 0);
10+
timeline.to(dot, { autoAlpha: 1, scale: 1, duration: 0.6 }, appearAt);
11+
timeline.to(dot, { autoAlpha: 0, scale: 0, duration: 0.6 }, fadeOutAt);
4412
};
Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,12 @@
11
import { Animation } from "./animation.types";
22

3-
export const labelAnimation: Animation = ({ label, timeline }) => {
4-
timeline.from(
5-
label,
6-
{
7-
opacity: 0,
8-
},
9-
0,
10-
);
11-
timeline.to(
12-
label,
13-
{
14-
opacity: 0,
15-
},
16-
5,
17-
);
18-
timeline.to(
19-
label,
20-
{
21-
opacity: 1,
22-
},
23-
7,
24-
);
25-
timeline.to(
26-
label,
27-
{
28-
opacity: 1,
29-
},
30-
7,
31-
);
32-
timeline.to(
33-
label,
34-
{
35-
opacity: 0,
36-
},
37-
9,
38-
);
3+
export const labelAnimation: Animation = ({ label, timeline, pathIndex }) => {
4+
// High pathIndex = top of screen (shorter paths), low pathIndex = bottom (longer paths)
5+
const t = 1 - (pathIndex - 1) / 31;
6+
const appearAt = 4.2 + t * 1.2;
7+
const disappearAt = 5.2 + t * 1.5;
8+
9+
timeline.set(label, { autoAlpha: 0 }, 0);
10+
timeline.to(label, { autoAlpha: 1, duration: 0.5 }, appearAt);
11+
timeline.to(label, { autoAlpha: 0, duration: 0.5 }, disappearAt);
3912
};

documentation/src/components/landing/hero/paths/dot/dot.tsx

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,7 @@ import { useWindowSize } from "@site/src/hooks/use-window-size";
33

44
import { Animation } from "../animations/animation.types";
55
import { dotAnimation } from "../animations/dot.animation";
6-
// import { labelAnimation } from "../animations/label.animation";
7-
// import { glowAnimation } from "../animations/glow.animation";
8-
9-
// function getMatrix(element: HTMLDivElement) {
10-
// const { transform } = element.style;
11-
// const re = /translate3d\((?<x>.*?)px, (?<y>.*?)px, (?<z>.*?)px/;
12-
// const results = re.exec(transform);
13-
14-
// if (!results) {
15-
// return {
16-
// x: 0,
17-
// y: 0,
18-
// z: 0,
19-
// };
20-
// }
21-
22-
// return {
23-
// x: results.groups.x as unknown as number,
24-
// y: results.groups.y as unknown as number,
25-
// z: results.groups.z as unknown as number,
26-
// };
27-
// }
6+
import { labelAnimation } from "../animations/label.animation";
287

298
export const Dot = ({
309
id,
@@ -48,51 +27,23 @@ export const Dot = ({
4827
end: 1,
4928
},
5029
duration: animation.duration,
51-
// TOO SLOW
52-
// onUpdate: () => {
53-
// const item = document.querySelector(animation.item) as HTMLDivElement;
54-
// const gradient = document.querySelector(`#gradient${animation.index}`) as SVGPathElement;
55-
// const halo = document.querySelector(`#halo${animation.index}`) as SVGPathElement;
56-
// const paths = document.querySelector(`#paths`) as SVGElement;
57-
// const element = document.querySelector(animation.path) as SVGPathElement;
58-
59-
// if (!item || !gradient || !halo || !element || !paths) {
60-
// return;
61-
// }
62-
63-
// const { x, y } = getMatrix(item);
64-
65-
// const svgViewportWidth = 2000;
66-
// const svgViewportHeight = 1000;
67-
// const svgWidth = paths.getBoundingClientRect().width;
68-
// const svgHeight = paths.getBoundingClientRect().height;
69-
// const topOffset = 185;
70-
// const leftOffset = -5;
71-
// const scaleX = svgViewportWidth / svgWidth;
72-
// const scaleY = svgViewportHeight / svgHeight;
73-
74-
// halo.setAttribute("cx", `${x * scaleX + leftOffset}`);
75-
// halo.setAttribute("cy", `${y * scaleY + topOffset}`);
76-
77-
// gradient.setAttribute("cx", `${x * scaleX + leftOffset}`);
78-
// gradient.setAttribute("cy", `${y * scaleY + topOffset}`);
79-
// },
8030
});
8131

8232
dotAnimation(animation);
83-
// labelAnimation(animation);
84-
// glowAnimation(animation);
33+
labelAnimation(animation);
8534
}, [animation, id, width]);
8635

8736
return (
8837
<div id={`idItem${id}`} className="absolute">
8938
<div
9039
id={`idDot${id}`}
91-
className={`w-[2px] h-[2px] rounded-[100%] left-1/2 -translate-x-1/2 top-0 opacity-0 ${tool.dotClassName}`}
40+
className={`w-[2px] h-[2px] rounded-[100%] left-1/2 -translate-x-1/2 top-0 will-change-[opacity,transform] ${tool.dotClassName}`}
41+
style={{ visibility: "hidden" }}
9242
/>
9343
<span
9444
id={`idLabel${id}`}
95-
className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-full pt-[0px] text-[10px] text-zinc-300 dark:text-zinc-500 opacity-0 whitespace-nowrap"
45+
className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-full pt-[0px] text-[10px] text-zinc-300 dark:text-zinc-500 will-change-[opacity,visibility] whitespace-nowrap"
46+
style={{ visibility: "hidden" }}
9647
>
9748
{tool.name}
9849
</span>

documentation/src/components/landing/hero/paths/paths.tsx

Lines changed: 43 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,45 @@
1-
/* eslint-disable react/no-array-index-key */
2-
import { gsap } from "gsap";
31
import { useIsMounted } from "@better-hooks/lifecycle";
42
import { useWindowSize } from "@site/src/hooks/use-window-size";
3+
/* eslint-disable react/no-array-index-key */
4+
import { gsap } from "gsap";
55
import { MotionPathPlugin } from "gsap/dist/MotionPathPlugin";
66
import { useCallback, useLayoutEffect, useState } from "react";
77

88
import { Animation } from "./animations/animation.types";
9-
import { Glow } from "./glow/glow";
109
import { Dot } from "./dot/dot";
10+
import { Glow } from "./glow/glow";
1111

1212
const tools: Array<{ name: string; dotClassName: string }> = [
13-
{
14-
name: "form data",
15-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
16-
},
17-
{
18-
name: "rest",
19-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
20-
},
21-
{
22-
name: "websockets",
23-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
24-
},
25-
{
26-
name: "sse",
27-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
28-
},
29-
{
30-
name: "firebase",
31-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
32-
},
33-
{
34-
name: "realtime",
35-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
36-
},
37-
{
38-
name: "upload",
39-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
40-
},
41-
{
42-
name: "download",
43-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
44-
},
45-
{
46-
name: "stream",
47-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
48-
},
49-
{
50-
name: "form data",
51-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
52-
},
53-
{
54-
name: "rest",
55-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
56-
},
57-
{
58-
name: "websockets",
59-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
60-
},
61-
{
62-
name: "sse",
63-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
64-
},
65-
{
66-
name: "firebase",
67-
dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500",
68-
},
13+
{ name: "request", dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500" },
14+
{ name: "event", dotClassName: "bg-amber-400 shadow-neon shadow-amber-400" },
15+
{ name: "stream", dotClassName: "bg-amber-500 shadow-neon shadow-amber-500" },
16+
{ name: "tool call", dotClassName: "bg-rose-500 shadow-neon shadow-rose-500" },
17+
{ name: "agent run", dotClassName: "bg-pink-500 shadow-neon shadow-pink-500" },
18+
{ name: "message", dotClassName: "bg-yellow-400 shadow-neon shadow-yellow-400" },
19+
{ name: "notification", dotClassName: "bg-orange-400 shadow-neon shadow-orange-400" },
20+
{ name: "form data", dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500" },
21+
{ name: "image", dotClassName: "bg-amber-500 shadow-neon shadow-amber-500" },
22+
{ name: "PDF", dotClassName: "bg-yellow-600 shadow-neon shadow-yellow-600" },
23+
{ name: "video", dotClassName: "bg-orange-400 shadow-neon shadow-orange-400" },
24+
{ name: "spreadsheet", dotClassName: "bg-amber-400 shadow-neon shadow-amber-400" },
25+
{ name: "vector", dotClassName: "bg-rose-400 shadow-neon shadow-rose-400" },
26+
{ name: "upload", dotClassName: "bg-yellow-400 shadow-neon shadow-yellow-400" },
27+
{ name: "download", dotClassName: "bg-yellow-500 shadow-neon shadow-yellow-500" },
28+
{ name: "embedding", dotClassName: "bg-pink-500 shadow-neon shadow-pink-500" },
29+
{ name: "JSON", dotClassName: "bg-yellow-400 shadow-neon shadow-yellow-400" },
30+
{ name: "binary", dotClassName: "bg-yellow-600 shadow-neon shadow-yellow-600" },
31+
{ name: "realtime", dotClassName: "bg-yellow-300 shadow-neon shadow-yellow-300" },
32+
{ name: "prompt", dotClassName: "bg-pink-400 shadow-neon shadow-pink-400" },
33+
{ name: "completion", dotClassName: "bg-rose-400 shadow-neon shadow-rose-400" },
34+
{ name: "vector", dotClassName: "bg-pink-400 shadow-neon shadow-pink-400" },
35+
{ name: "XML", dotClassName: "bg-yellow-600 shadow-neon shadow-yellow-600" },
36+
{ name: "inference", dotClassName: "bg-rose-500 shadow-neon shadow-rose-500" },
37+
{ name: "CSV", dotClassName: "bg-yellow-400 shadow-neon shadow-yellow-400" },
38+
{ name: "Excel", dotClassName: "bg-amber-500 shadow-neon shadow-amber-500" },
39+
{ name: "audio", dotClassName: "bg-amber-400 shadow-neon shadow-amber-400" },
40+
{ name: "chat", dotClassName: "bg-pink-400 shadow-neon shadow-pink-400" },
41+
{ name: "document", dotClassName: "bg-yellow-600 shadow-neon shadow-yellow-600" },
42+
{ name: "HTML", dotClassName: "bg-amber-400 shadow-neon shadow-amber-400" },
6943
];
7044

7145
const paths = [
@@ -107,43 +81,23 @@ const max = paths.length - 1;
10781

10882
gsap.registerPlugin(MotionPathPlugin);
10983

110-
function getRandomNumber(excludedNumbers: number[]): number {
111-
const possibleNumbers = Array.from({ length: 15 }, (_, i) => i).filter((num) => !excludedNumbers.includes(num));
112-
if (possibleNumbers.length === 0) {
113-
return 0;
114-
}
115-
const randomIndex = Math.floor(Math.random() * possibleNumbers.length);
116-
return possibleNumbers[randomIndex];
117-
}
84+
const DURATION = 12;
11885

11986
const generateAnimationData = () => {
12087
const availableIndexes = [...Array(max).keys()];
121-
122-
const previousDelays: Array<{ pathIndex: number; delay: number }> = [];
88+
const staggerInterval = DURATION / tools.length;
12389

12490
return tools.map((tool, index) => {
125-
const itemIndex = availableIndexes[Math.floor(Math.random() * availableIndexes.length)];
126-
availableIndexes.splice(itemIndex, 1); // prevent duplicates
91+
const randomPosition = Math.floor(Math.random() * availableIndexes.length);
92+
const itemIndex = availableIndexes[randomPosition];
93+
availableIndexes.splice(randomPosition, 1);
12794

128-
// Index should be max and it has to start counting from 1 over again
12995
const pathIndex = itemIndex - Math.floor(itemIndex / max) * max + 1;
130-
const duration = 16;
131-
132-
const neighbors = previousDelays.filter((d) => d.pathIndex === pathIndex - 1 || d.pathIndex === pathIndex + 1);
133-
134-
// Cannot be in the offset range of neighbors delays
135-
// To prevent overlapping of the dots
136-
const delay = getRandomNumber(
137-
neighbors
138-
.map((d) => d.delay)
139-
.reduce((acc, curr) => {
140-
const numbers = [curr - 2, curr - 1, curr, curr + 1, curr + 2].filter((n) => n >= 0);
141-
142-
return [...new Set([...acc, ...numbers])];
143-
}, [] as number[]),
144-
);
14596

146-
previousDelays.push({ pathIndex, delay });
97+
// Evenly staggered with slight jitter so dots never start at the exact same time
98+
const baseDelay = index * staggerInterval;
99+
const jitter = (Math.random() - 0.5) * staggerInterval * 0.6;
100+
const delay = Math.max(0, baseDelay + jitter);
147101

148102
const timeline = gsap.timeline({
149103
repeat: -1,
@@ -158,17 +112,14 @@ const generateAnimationData = () => {
158112
const label = `#idLabel${index}`;
159113

160114
const props: Parameters<Animation>[0] = {
161-
// IDS
162115
path,
163116
item,
164117
dot,
165118
glow,
166119
label,
167-
// Settings
168120
timeline,
169121
delay,
170-
duration,
171-
// Other
122+
duration: DURATION,
172123
pathIndex,
173124
index,
174125
};

0 commit comments

Comments
 (0)