1- /* eslint-disable react/no-array-index-key */
2- import { gsap } from "gsap" ;
31import { useIsMounted } from "@better-hooks/lifecycle" ;
42import { useWindowSize } from "@site/src/hooks/use-window-size" ;
3+ /* eslint-disable react/no-array-index-key */
4+ import { gsap } from "gsap" ;
55import { MotionPathPlugin } from "gsap/dist/MotionPathPlugin" ;
66import { useCallback , useLayoutEffect , useState } from "react" ;
77
88import { Animation } from "./animations/animation.types" ;
9- import { Glow } from "./glow/glow" ;
109import { Dot } from "./dot/dot" ;
10+ import { Glow } from "./glow/glow" ;
1111
1212const 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
7145const paths = [
@@ -107,43 +81,23 @@ const max = paths.length - 1;
10781
10882gsap . 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
11986const 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