Skip to content

Commit ea0f470

Browse files
feat: clean up layout
1 parent d8da9b2 commit ea0f470

File tree

2 files changed

+167
-162
lines changed

2 files changed

+167
-162
lines changed

src/components/Analysis/StreamAnalysis.tsx

Lines changed: 139 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { BoardController } from 'src/components/Board/BoardController'
2727
import { PromotionOverlay } from 'src/components/Board/PromotionOverlay'
2828
import { GameInfo } from 'src/components/Common/GameInfo'
2929
import { AnalysisSidebar } from 'src/components/Analysis'
30+
import { ConfigurableScreens } from 'src/components/Analysis/ConfigurableScreens'
3031
import { MAIA_MODELS } from 'src/constants/common'
3132

3233
interface Props {
@@ -179,56 +180,6 @@ export const StreamAnalysis: React.FC<Props> = ({
179180
// Check if we're using dummy game data (waiting for real game data)
180181
const isWaitingForGameData = game.id === ''
181182

182-
// Stream status component
183-
const StreamStatus = () => (
184-
<div className="mb-2 flex items-center justify-between rounded bg-background-1 px-4 py-2">
185-
<div className="flex items-center gap-2">
186-
<div
187-
className={`h-2 w-2 rounded-full ${
188-
streamState.isLive
189-
? 'animate-pulse bg-red-500'
190-
: streamState.isConnected
191-
? 'bg-green-500'
192-
: 'bg-gray-500'
193-
}`}
194-
/>
195-
<span className="text-sm font-medium">
196-
{isWaitingForGameData && streamState.isConnected
197-
? 'Waiting for game data...'
198-
: streamState.isLive
199-
? 'LIVE'
200-
: streamState.isConnected
201-
? 'Connected'
202-
: streamState.error
203-
? 'Disconnected'
204-
: 'Connecting...'}
205-
</span>
206-
{streamState.error && (
207-
<span className="text-xs text-red-400">({streamState.error})</span>
208-
)}
209-
</div>
210-
<div className="flex items-center gap-2">
211-
{streamState.error && (
212-
<button
213-
onClick={onReconnect}
214-
className="rounded bg-human-4 px-2 py-1 text-xs text-white transition hover:bg-human-4/80"
215-
>
216-
Reconnect
217-
</button>
218-
)}
219-
<button
220-
onClick={() => {
221-
onStopStream()
222-
router.push('/analysis')
223-
}}
224-
className="rounded bg-background-2 px-2 py-1 text-xs text-secondary transition hover:bg-background-3"
225-
>
226-
Exit Stream
227-
</button>
228-
</div>
229-
</div>
230-
)
231-
232183
const NestedGameInfo = () => (
233184
<div className="flex w-full flex-col">
234185
<div className="hidden md:block">
@@ -336,123 +287,137 @@ export const StreamAnalysis: React.FC<Props> = ({
336287
exit="exit"
337288
style={{ willChange: 'transform, opacity' }}
338289
>
339-
<div className="flex h-full w-[90%] flex-col gap-2">
340-
<StreamStatus />
341-
<div className="flex h-full flex-row gap-2">
342-
<motion.div
343-
id="navigation"
344-
className="desktop-left-column-container flex flex-col gap-2 overflow-hidden"
345-
variants={itemVariants}
346-
style={{ willChange: 'transform, opacity' }}
290+
<div className="flex h-full w-[90%] flex-row gap-2">
291+
<motion.div
292+
id="navigation"
293+
className="desktop-left-column-container flex flex-col gap-2 overflow-hidden"
294+
variants={itemVariants}
295+
style={{ willChange: 'transform, opacity' }}
296+
>
297+
<GameInfo
298+
title="Live Analysis"
299+
icon="live_tv"
300+
type="analysis"
301+
streamState={{
302+
isLive: streamState.isLive,
303+
isConnected: streamState.isConnected,
304+
error: streamState.error,
305+
}}
347306
>
348-
<GameInfo title="Live Analysis" icon="live_tv" type="analysis">
349-
<NestedGameInfo />
350-
</GameInfo>
351-
<div className="flex h-1/2 w-full flex-1 flex-col gap-2">
352-
<div className="flex h-full flex-col overflow-y-scroll">
353-
<MovesContainer
354-
game={game}
355-
termination={game.termination}
356-
type="analysis"
357-
showAnnotations={true}
358-
disableKeyboardNavigation={false}
359-
disableMoveClicking={false}
360-
/>
361-
<BoardController
362-
gameTree={analysisController.gameTree}
363-
orientation={analysisController.orientation}
364-
setOrientation={analysisController.setOrientation}
365-
currentNode={analysisController.currentNode}
366-
plyCount={analysisController.plyCount}
367-
goToNode={analysisController.goToNode}
368-
goToNextNode={analysisController.goToNextNode}
369-
goToPreviousNode={analysisController.goToPreviousNode}
370-
goToRootNode={analysisController.goToRootNode}
371-
disableKeyboardNavigation={false}
372-
disableNavigation={false}
373-
/>
374-
</div>
307+
<NestedGameInfo />
308+
</GameInfo>
309+
<div className="flex h-1/2 w-full flex-1 flex-col gap-2">
310+
<div className="flex h-full flex-col overflow-y-scroll">
311+
<MovesContainer
312+
game={game}
313+
termination={game.termination}
314+
type="analysis"
315+
showAnnotations={true}
316+
disableKeyboardNavigation={false}
317+
disableMoveClicking={false}
318+
/>
319+
<BoardController
320+
gameTree={analysisController.gameTree}
321+
orientation={analysisController.orientation}
322+
setOrientation={analysisController.setOrientation}
323+
currentNode={analysisController.currentNode}
324+
plyCount={analysisController.plyCount}
325+
goToNode={analysisController.goToNode}
326+
goToNextNode={analysisController.goToNextNode}
327+
goToPreviousNode={analysisController.goToPreviousNode}
328+
goToRootNode={analysisController.goToRootNode}
329+
disableKeyboardNavigation={false}
330+
disableNavigation={false}
331+
/>
375332
</div>
376-
</motion.div>
377-
<motion.div
378-
className="desktop-middle-column-container flex flex-col gap-2"
379-
variants={itemVariants}
380-
style={{ willChange: 'transform, opacity' }}
381-
>
382-
<div className="flex w-full flex-col overflow-hidden rounded">
383-
<PlayerInfo
384-
name={
385-
analysisController.orientation === 'white'
386-
? game.blackPlayer.name
387-
: game.whitePlayer.name
388-
}
389-
rating={
390-
analysisController.orientation === 'white'
391-
? game.blackPlayer.rating
392-
: game.whitePlayer.rating
393-
}
394-
color={
395-
analysisController.orientation === 'white' ? 'black' : 'white'
396-
}
397-
termination={game.termination.winner}
333+
</div>
334+
</motion.div>
335+
<motion.div
336+
className="desktop-middle-column-container flex flex-col gap-2"
337+
variants={itemVariants}
338+
style={{ willChange: 'transform, opacity' }}
339+
>
340+
<div className="flex w-full flex-col overflow-hidden rounded">
341+
<PlayerInfo
342+
name={
343+
analysisController.orientation === 'white'
344+
? game.blackPlayer.name
345+
: game.whitePlayer.name
346+
}
347+
rating={
348+
analysisController.orientation === 'white'
349+
? game.blackPlayer.rating
350+
: game.whitePlayer.rating
351+
}
352+
color={
353+
analysisController.orientation === 'white' ? 'black' : 'white'
354+
}
355+
termination={game.termination.winner}
356+
/>
357+
<div className="desktop-board-container relative flex aspect-square">
358+
<GameBoard
359+
game={game}
360+
availableMoves={analysisController.availableMoves}
361+
setCurrentSquare={setCurrentSquare}
362+
shapes={(() => {
363+
const baseShapes = [...analysisController.arrows]
364+
if (hoverArrow) {
365+
baseShapes.push(hoverArrow)
366+
}
367+
return baseShapes
368+
})()}
369+
currentNode={analysisController.currentNode as GameNode}
370+
orientation={analysisController.orientation}
371+
onPlayerMakeMove={onPlayerMakeMove}
372+
goToNode={analysisController.goToNode}
373+
gameTree={game.tree}
398374
/>
399-
<div className="desktop-board-container relative flex aspect-square">
400-
<GameBoard
401-
game={game}
402-
availableMoves={analysisController.availableMoves}
403-
setCurrentSquare={setCurrentSquare}
404-
shapes={(() => {
405-
const baseShapes = [...analysisController.arrows]
406-
if (hoverArrow) {
407-
baseShapes.push(hoverArrow)
408-
}
409-
return baseShapes
410-
})()}
411-
currentNode={analysisController.currentNode as GameNode}
412-
orientation={analysisController.orientation}
413-
onPlayerMakeMove={onPlayerMakeMove}
414-
goToNode={analysisController.goToNode}
415-
gameTree={game.tree}
375+
{promotionFromTo ? (
376+
<PromotionOverlay
377+
player={currentPlayer}
378+
file={promotionFromTo[1].slice(0, 1)}
379+
onPlayerSelectPromotion={onPlayerSelectPromotion}
416380
/>
417-
{promotionFromTo ? (
418-
<PromotionOverlay
419-
player={currentPlayer}
420-
file={promotionFromTo[1].slice(0, 1)}
421-
onPlayerSelectPromotion={onPlayerSelectPromotion}
422-
/>
423-
) : null}
424-
</div>
425-
<PlayerInfo
426-
name={
427-
analysisController.orientation === 'white'
428-
? game.whitePlayer.name
429-
: game.blackPlayer.name
430-
}
431-
rating={
432-
analysisController.orientation === 'white'
433-
? game.whitePlayer.rating
434-
: game.blackPlayer.rating
435-
}
436-
color={
437-
analysisController.orientation === 'white' ? 'white' : 'black'
438-
}
439-
termination={game.termination.winner}
440-
showArrowLegend={true}
441-
/>
381+
) : null}
442382
</div>
443-
</motion.div>
444-
<AnalysisSidebar
445-
hover={hover}
446-
makeMove={makeMove}
447-
controller={analysisController}
448-
setHoverArrow={setHoverArrow}
449-
analysisEnabled={true}
450-
handleToggleAnalysis={() => {
451-
// Analysis toggle not needed for stream - always enabled
452-
}}
453-
itemVariants={itemVariants}
383+
<PlayerInfo
384+
name={
385+
analysisController.orientation === 'white'
386+
? game.whitePlayer.name
387+
: game.blackPlayer.name
388+
}
389+
rating={
390+
analysisController.orientation === 'white'
391+
? game.whitePlayer.rating
392+
: game.blackPlayer.rating
393+
}
394+
color={
395+
analysisController.orientation === 'white' ? 'white' : 'black'
396+
}
397+
termination={game.termination.winner}
398+
showArrowLegend={true}
399+
/>
400+
</div>
401+
<ConfigurableScreens
402+
currentMaiaModel={analysisController.currentMaiaModel}
403+
setCurrentMaiaModel={analysisController.setCurrentMaiaModel}
404+
launchContinue={launchContinue}
405+
MAIA_MODELS={MAIA_MODELS}
406+
game={game}
407+
currentNode={analysisController.currentNode as GameNode}
454408
/>
455-
</div>
409+
</motion.div>
410+
<AnalysisSidebar
411+
hover={hover}
412+
makeMove={makeMove}
413+
controller={analysisController}
414+
setHoverArrow={setHoverArrow}
415+
analysisEnabled={true}
416+
handleToggleAnalysis={() => {
417+
// Analysis toggle not needed for stream - always enabled
418+
}}
419+
itemVariants={itemVariants}
420+
/>
456421
</div>
457422
</motion.div>
458423
)
@@ -466,7 +431,6 @@ export const StreamAnalysis: React.FC<Props> = ({
466431
style={{ willChange: 'transform, opacity' }}
467432
>
468433
<div className="flex h-full flex-1 flex-col justify-center gap-1">
469-
<StreamStatus />
470434
<motion.div
471435
className="flex w-full flex-col items-start justify-start gap-1"
472436
variants={itemVariants}
@@ -479,6 +443,11 @@ export const StreamAnalysis: React.FC<Props> = ({
479443
currentMaiaModel={analysisController.currentMaiaModel}
480444
setCurrentMaiaModel={analysisController.setCurrentMaiaModel}
481445
MAIA_MODELS={MAIA_MODELS}
446+
streamState={{
447+
isLive: streamState.isLive,
448+
isConnected: streamState.isConnected,
449+
error: streamState.error,
450+
}}
482451
>
483452
<NestedGameInfo />
484453
</GameInfo>
@@ -535,6 +504,14 @@ export const StreamAnalysis: React.FC<Props> = ({
535504
/>
536505
</div>
537506
</div>
507+
<ConfigurableScreens
508+
currentMaiaModel={analysisController.currentMaiaModel}
509+
setCurrentMaiaModel={analysisController.setCurrentMaiaModel}
510+
launchContinue={launchContinue}
511+
MAIA_MODELS={MAIA_MODELS}
512+
game={game}
513+
currentNode={analysisController.currentNode as GameNode}
514+
/>
538515
</motion.div>
539516
</div>
540517
</motion.div>

src/components/Common/GameInfo.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ interface Props {
1212
MAIA_MODELS?: string[]
1313
showGameListButton?: boolean
1414
onGameListClick?: () => void
15+
streamState?: {
16+
isLive: boolean
17+
isConnected: boolean
18+
error: string | null
19+
}
1520
}
1621

1722
export const GameInfo: React.FC<Props> = ({
@@ -24,6 +29,7 @@ export const GameInfo: React.FC<Props> = ({
2429
MAIA_MODELS,
2530
showGameListButton,
2631
onGameListClick,
32+
streamState,
2733
}: Props) => {
2834
const { startTour } = useTour()
2935

@@ -38,6 +44,28 @@ export const GameInfo: React.FC<Props> = ({
3844
{icon}
3945
</span>
4046
<h2 className="text-lg font-semibold md:text-xl">{title}</h2>
47+
{streamState && (
48+
<div className="flex items-center gap-1.5">
49+
<div
50+
className={`h-2 w-2 rounded-full ${
51+
streamState.isLive
52+
? 'animate-pulse bg-red-500'
53+
: streamState.isConnected
54+
? 'bg-green-500'
55+
: 'bg-gray-500'
56+
}`}
57+
/>
58+
<span className="text-xs font-medium text-secondary">
59+
{streamState.isLive
60+
? 'LIVE'
61+
: streamState.isConnected
62+
? 'Connected'
63+
: streamState.error
64+
? 'Disconnected'
65+
: 'Connecting...'}
66+
</span>
67+
</div>
68+
)}
4169
{currentMaiaModel && setCurrentMaiaModel && (
4270
<div className="flex items-center gap-1 text-sm md:hidden">
4371
using

0 commit comments

Comments
 (0)