Files
NowChess-Frontend/src/app/pages/game/game.component.html
T
2026-05-14 23:38:06 +00:00

207 lines
7.9 KiB
HTML

<app-promotion-dialog
[isOpen]="facade.isPromotionDialogOpen"
(promotionSelected)="facade.onPromotionSelected($event)"
(closed)="facade.onPromotionClosed()" />
<div class="game-shell">
<div class="page">
<!-- Breadcrumb -->
<nav class="crumb" aria-label="Breadcrumb">
<a routerLink="/" class="crumb-link">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 18 9 12 15 6"/>
</svg>
Back to lobby
</a>
<span class="crumb-sep">/</span>
<span class="crumb-current">1 vs 1 Game</span>
</nav>
<!-- Game header -->
<header class="game-header">
<div class="game-title">
<h1>
1 vs 1 Game
@if (facade.game) {
<span class="tag-rated">Live</span>
}
</h1>
<div class="game-meta-strip">
<span class="game-id">
ID <strong>{{ facade.gameId }}</strong>
<button class="copy-btn" type="button" title="Copy game ID" (click)="copyGameId()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
</button>
</span>
@if (facade.state) {
<span class="meta-dot"></span>
<span>Move {{ moveNumber }}</span>
}
</div>
</div>
<div class="header-actions">
<button class="btn-ghost" type="button" (click)="flipBoard()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<polyline points="17 1 21 5 17 9"/>
<path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/>
<path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
Flip
</button>
<button class="btn" type="button" (click)="copyUrl()">Share</button>
</div>
</header>
<!-- Loading / error states -->
@if (facade.loading) {
<div class="state-message">
<span class="status-pulse"></span>
Loading game…
</div>
} @else if (facade.state) {
@if (facade.errorMessage) {
<div class="state-message state-error">{{ facade.errorMessage }}</div>
}
<!-- Game completed banner -->
@if (facade.isGameFinished && facade.gameCompletionMessage) {
<div class="completion-banner">
<span class="completion-title">{{ facade.gameCompletionMessage }}</span>
<a routerLink="/" class="completion-link">Start new game</a>
</div>
}
<!-- Main layout -->
<div class="layout">
<!-- BOARD COLUMN -->
<div class="board-col">
<!-- Opponent (top) -->
<app-player-card
[name]="blackPlayerName"
[initial]="blackPlayerInitial"
color="black"
[isActive]="!flipped ? facade.state.turn === 'black' : facade.state.turn === 'white'"
[clockDisplay]="!flipped ? blackClock : whiteClock"
[isLowTime]="!flipped ? isLowTimeBlack : isLowTimeWhite" />
<!-- Status strip -->
<div class="status-strip">
<div class="status-left">
<span class="status-pulse"></span>
<span class="status-text" [innerHTML]="statusMessage"></span>
</div>
<span class="status-side">
{{ facade.state.turn === 'white' ? 'WHITE' : 'BLACK' }} TO MOVE
</span>
</div>
<!-- Board -->
<div class="board-wrap">
<app-chess-board
[fen]="facade.state.fen"
[selectedSquare]="facade.selectedSquare"
[highlightedSquares]="facade.highlightedSquares"
[boardTheme]="boardTheme"
(squareSelected)="facade.onBoardSquareSelected($event)" />
</div>
<!-- Current player (bottom) -->
<app-player-card
[name]="whitePlayerName"
[initial]="whitePlayerInitial"
color="white"
[isActive]="!flipped ? facade.state.turn === 'white' : facade.state.turn === 'black'"
[clockDisplay]="!flipped ? whiteClock : blackClock"
[isLowTime]="!flipped ? isLowTimeWhite : isLowTimeBlack" />
<!-- Board action buttons -->
<app-board-actions-bar
[undoAvailable]="facade.state.undoAvailable"
[isGameFinished]="facade.isGameFinished"
(takeback)="onTakeback()"
(offerDraw)="onOfferDraw()"
(resign)="onResign()" />
</div>
<!-- SIDE COLUMN -->
<aside class="side">
<!-- Move history (collapsible) -->
<details class="side-card" open>
<summary class="side-card-summary">
<span class="side-card-title">Move History</span>
<span class="side-card-meta">{{ facade.state.moves.length }} plies</span>
<svg class="chev" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"/>
</svg>
</summary>
<app-move-history
[moves]="facade.state.moves"
(navigate)="onMoveNavigate($event)" />
</details>
<!-- Play move (collapsible) -->
<details class="side-card" open>
<summary class="side-card-summary">
<span class="side-card-title">Play Move</span>
<svg class="chev" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"/>
</svg>
</summary>
<div class="side-card-body">
<div class="uci-row">
<input
id="uci-input"
type="text"
class="uci-input"
placeholder="e.g. e2e4"
autocomplete="off"
[value]="facade.moveInput"
(input)="facade.moveInput = $any($event.target).value"
(keydown.enter)="facade.submitMove()" />
<button class="btn btn-primary" type="button" (click)="facade.submitMove()">Send</button>
</div>
<p class="uci-hint">Click a piece on the board to see legal targets.</p>
</div>
</details>
<!-- Board design (collapsible) -->
<details class="side-card">
<summary class="side-card-summary">
<span class="side-card-title">Board Design</span>
<svg class="chev" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"/>
</svg>
</summary>
<div class="side-card-body">
<div class="seg" role="tablist" aria-label="Board theme">
<button class="seg-btn" [class.active]="boardTheme === 'arabian'" role="tab" (click)="setBoardTheme('arabian')">Arabian</button>
<button class="seg-btn" [class.active]="boardTheme === 'classic'" role="tab" (click)="setBoardTheme('classic')">Classic</button>
</div>
</div>
</details>
<!-- Export (collapsible) -->
<app-export-panel [fen]="facade.state.fen" [pgn]="facade.state.pgn" />
</aside>
</div>
}
</div>
</div>
<!-- Toast notification -->
@if (toastMessage) {
<div class="toast show">{{ toastMessage }}</div>
}