@@ -22,7 +22,6 @@ interface GameData {
2222 url ?: string
2323}
2424
25- // Sample predefined games
2625const SAMPLE_GAMES : GameData [ ] = [
2726 {
2827 id : 'sample1' ,
@@ -109,7 +108,6 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
109108 }
110109 }
111110
112- // Preset rotation options
113111 const rotationOptions = [
114112 'rotate(-2deg)' ,
115113 'rotate(-1deg)' ,
@@ -118,17 +116,14 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
118116 'rotate(2deg)' ,
119117 ]
120118
121- // Chess icon options
122119 const chessIcons = [
123120 'chess_knight' ,
124121 'chess_bishop' ,
125- 'chess_king' ,
126122 'chess_rook' ,
127123 'chess_pawn' ,
128124 'chess' ,
129125 ]
130126
131- // Select rotation and icon based on game ID hash
132127 const hash = ( ( ) => {
133128 let h = 0
134129 for ( let i = 0 ; i < game . id . length ; i ++ ) {
@@ -140,7 +135,7 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
140135 const rotation = rotationOptions [ hash % rotationOptions . length ]
141136 const chessIcon = chessIcons [ hash % chessIcons . length ]
142137
143- const truncateName = ( name : string , maxLength = 12 ) => {
138+ const truncateName = ( name : string , maxLength = 15 ) => {
144139 return name . length > maxLength
145140 ? name . substring ( 0 , maxLength - 1 ) + '…'
146141 : name
@@ -162,52 +157,48 @@ const GameChip: React.FC<GameChipProps> = ({ game, onClick }) => {
162157 className = "group relative flex h-14 min-w-48 max-w-48 cursor-pointer flex-row items-center gap-3 rounded bg-white/[3%] px-4 py-2 backdrop-blur-sm transition-all duration-200 hover:bg-white/[6%] focus:outline-none focus:ring-2 focus:ring-white/20"
163158 style = { { transform : rotation } }
164159 >
165- { /* Live Indicator */ }
166160 { game . isLive && (
167161 < div className = "absolute -right-1 -top-1 flex items-center gap-1 rounded-full bg-red-500/90 px-2 py-0.5" >
168162 < div className = "h-1 w-1 animate-pulse rounded-full bg-white" />
169163 < span className = "text-[8px] font-semibold text-white" > LIVE</ span >
170164 </ div >
171165 ) }
172- { /* Chess Icon */ }
166+
173167 < span className = "material-symbols-outlined material-symbols-filled text-2xl text-white/30 transition-all duration-200 group-hover:text-white/60" >
174168 { chessIcon }
175169 </ span >
176170
177- { /* Text Content */ }
178171 { isBroadcast ? (
179172 < div className = "flex flex-1 flex-col justify-center gap-0.5" >
180173 < span className = "text-[10px] font-medium text-white/30 transition-all duration-200 group-hover:text-white/70" >
181174 Broadcast
182175 </ span >
183176 < span className = "truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white" >
184- { truncateName ( game . black . name , 16 ) }
177+ { truncateName ( game . black . name , 18 ) }
185178 </ span >
186179 </ div >
187180 ) : (
188181 < div className = "flex flex-1 flex-col justify-center gap-0.5" >
189182 < span className = "truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white" >
190- { truncateName ( game . white . name , 13 ) }
183+ { truncateName ( game . white . name , 16 ) }
191184 </ span >
192185 < span className = "truncate text-xs font-medium text-white/40 transition-all duration-200 group-hover:text-white" >
193- vs { truncateName ( game . black . name , 10 ) }
186+ vs { truncateName ( game . black . name , 14 ) }
194187 </ span >
195188 </ div >
196189 ) }
197190 </ div >
198191 )
199192}
200193
201- export const LiveChessBoardShowcase : React . FC = ( ) => {
194+ export const GameCarousel : React . FC = ( ) => {
202195 const router = useRouter ( )
203- const [ error , setError ] = useState < string | null > ( null )
204196 const [ games , setGames ] = useState < GameData [ ] > ( SAMPLE_GAMES )
205197 const [ isPaused , setIsPaused ] = useState ( false )
206198 const abortController = useRef < AbortController | null > ( null )
207199 const carouselRef = useRef < HTMLDivElement > ( null )
208200
209201 const handleGameStart = useCallback ( ( gameData : StreamedGame ) => {
210- // Update the games list with live game data
211202 setGames ( ( prevGames ) => {
212203 const newGames = [ ...prevGames ]
213204 const liveGameData : GameData = {
@@ -224,15 +215,13 @@ export const LiveChessBoardShowcase: React.FC = () => {
224215 url : `/analysis/stream/${ gameData . id } ` ,
225216 }
226217
227- // Check if this live game already exists
228218 const existingLiveIndex = newGames . findIndex (
229219 ( g ) => g . id === `live-${ gameData . id } ` ,
230220 )
221+
231222 if ( existingLiveIndex !== - 1 ) {
232- // Update existing live game
233223 newGames [ existingLiveIndex ] = liveGameData
234224 } else {
235- // Replace a sample game at index 2 (3rd position) with live game data
236225 const targetIndex = 2
237226 if ( targetIndex < newGames . length && ! newGames [ targetIndex ] . isLive ) {
238227 newGames [ targetIndex ] = liveGameData
@@ -247,21 +236,17 @@ export const LiveChessBoardShowcase: React.FC = () => {
247236 } , [ ] )
248237
249238 const handleStreamComplete = useCallback ( ( ) => {
250- console . log ( 'Live board showcase - Stream completed' )
251239 fetchNewGame ( )
252240 } , [ ] )
253241
254242 const fetchNewGame = useCallback ( async ( ) => {
255243 try {
256- setError ( null )
257244 const tvGame = await fetchLichessTVGame ( )
258245
259- // Stop current stream if any
260246 if ( abortController . current ) {
261247 abortController . current . abort ( )
262248 }
263249
264- // Start new stream
265250 abortController . current = new AbortController ( )
266251
267252 streamLichessGameMoves (
@@ -272,13 +257,11 @@ export const LiveChessBoardShowcase: React.FC = () => {
272257 abortController . current . signal ,
273258 ) . catch ( ( err ) => {
274259 if ( err . name !== 'AbortError' ) {
275- console . error ( 'Live board streaming error:' , err )
276- setError ( 'Connection lost' )
260+ console . error ( 'Live game streaming error:' , err )
277261 }
278262 } )
279263 } catch ( err ) {
280- console . error ( 'Error fetching new live game:' , err )
281- setError ( 'Failed to load live game' )
264+ console . error ( 'Error fetching live game:' , err )
282265 }
283266 } , [ handleGameStart , handleMove , handleStreamComplete ] )
284267
@@ -291,7 +274,6 @@ export const LiveChessBoardShowcase: React.FC = () => {
291274 topBroadcasts . active [ 0 ] ,
292275 )
293276
294- // Add broadcast to games list
295277 setGames ( ( prevGames ) => {
296278 const newGames = [ ...prevGames ]
297279 const broadcastGameData : GameData = {
@@ -308,15 +290,13 @@ export const LiveChessBoardShowcase: React.FC = () => {
308290 url : `/broadcast/${ broadcastData . tour . id } /${ broadcastData . rounds [ 0 ] ?. id } ` ,
309291 }
310292
311- // Check if this broadcast already exists
312293 const existingBroadcastIndex = newGames . findIndex (
313294 ( g ) => g . id === `broadcast-${ broadcastData . tour . id } ` ,
314295 )
296+
315297 if ( existingBroadcastIndex !== - 1 ) {
316- // Update existing broadcast
317298 newGames [ existingBroadcastIndex ] = broadcastGameData
318299 } else {
319- // Replace a sample game at index 7 (8th position) with broadcast data
320300 const targetIndex = 7
321301 if (
322302 targetIndex < newGames . length &&
@@ -333,7 +313,6 @@ export const LiveChessBoardShowcase: React.FC = () => {
333313 }
334314 } , [ ] )
335315
336- // Auto-scroll effect
337316 useEffect ( ( ) => {
338317 if ( isPaused || ! carouselRef . current ) return
339318
@@ -343,10 +322,8 @@ export const LiveChessBoardShowcase: React.FC = () => {
343322 const halfWidth = scrollWidth / 2
344323
345324 if ( scrollLeft >= halfWidth - 10 ) {
346- // Reset to beginning without animation when we reach the duplicate section
347325 carouselRef . current . scrollTo ( { left : 0 , behavior : 'auto' } )
348326 } else {
349- // Smooth continuous scroll
350327 carouselRef . current . scrollBy ( { left : 0.5 , behavior : 'auto' } )
351328 }
352329 }
@@ -357,11 +334,9 @@ export const LiveChessBoardShowcase: React.FC = () => {
357334 } , [ isPaused ] )
358335
359336 useEffect ( ( ) => {
360- // Initial fetch
361337 fetchNewGame ( )
362338 fetchBroadcast ( )
363339
364- // Cleanup on unmount
365340 return ( ) => {
366341 if ( abortController . current ) {
367342 abortController . current . abort ( )
@@ -379,58 +354,44 @@ export const LiveChessBoardShowcase: React.FC = () => {
379354 )
380355
381356 return (
382- < div className = "relative mb-10 w-full overflow-y-visible" >
383- < motion . div
384- ref = { carouselRef }
385- className = "flex gap-10 overflow-x-hidden overflow-y-visible py-2 pl-4"
386- initial = { { opacity : 0 } }
387- animate = { { opacity : 1 } }
388- transition = { { duration : 0.5 } }
389- onMouseEnter = { ( ) => setIsPaused ( true ) }
390- onMouseLeave = { ( ) => setIsPaused ( false ) }
391- style = { {
392- scrollbarWidth : 'none' ,
393- msOverflowStyle : 'none' ,
394- } }
395- >
396- { /* Render games twice for seamless loop */ }
397- { [ ...games , ...games ] . map ( ( game , index ) => (
398- < GameChip
399- key = { `${ game . id } -${ index } ` }
400- game = { game }
401- onClick = { ( ) => handleGameClick ( game ) }
402- />
403- ) ) }
404- </ motion . div >
405-
406- { error && (
407- < div className = "mt-2 text-center" >
408- < p className = "text-xs text-red-400" > { error } </ p >
409- < button
410- onClick = { fetchNewGame }
411- className = "mt-1 text-[10px] font-medium text-white/60 hover:text-white/80 hover:underline"
412- >
413- Retry
414- </ button >
415- </ div >
416- ) }
417- </ div >
357+ < section className = "relative w-full overflow-y-visible py-6" >
358+ < div className = "relative mb-10 w-full overflow-y-visible" >
359+ < motion . div
360+ ref = { carouselRef }
361+ className = "flex gap-10 overflow-x-hidden overflow-y-visible py-2 pl-4"
362+ initial = { { opacity : 0 } }
363+ animate = { { opacity : 1 } }
364+ transition = { { duration : 0.5 } }
365+ onMouseEnter = { ( ) => setIsPaused ( true ) }
366+ onMouseLeave = { ( ) => setIsPaused ( false ) }
367+ style = { {
368+ scrollbarWidth : 'none' ,
369+ msOverflowStyle : 'none' ,
370+ } }
371+ >
372+ { [ ...games , ...games ] . map ( ( game , index ) => (
373+ < GameChip
374+ key = { `${ game . id } -${ index } ` }
375+ game = { game }
376+ onClick = { ( ) => handleGameClick ( game ) }
377+ />
378+ ) ) }
379+ </ motion . div >
380+ </ div >
381+ </ section >
418382 )
419383}
420384
421- // Hide scrollbar for webkit browsers
422- const styles = `
423- .carousel-container::-webkit-scrollbar {
424- display: none;
425- }
426- `
427-
428385if (
429386 typeof document !== 'undefined' &&
430387 ! document . getElementById ( 'carousel-styles' )
431388) {
432389 const styleSheet = document . createElement ( 'style' )
433390 styleSheet . id = 'carousel-styles'
434- styleSheet . innerText = styles
391+ styleSheet . innerText = `
392+ .carousel-container::-webkit-scrollbar {
393+ display: none;
394+ }
395+ `
435396 document . head . appendChild ( styleSheet )
436397}
0 commit comments