From d676288315853d9102c8bea641a9540a4aaa7cdf Mon Sep 17 00:00:00 2001 From: shahdlala66 Date: Sun, 26 Apr 2026 22:13:26 +0200 Subject: [PATCH] style: changed colors etc --- angular.json | 4 +- src/app/pages/game/game.component.css | 31 + src/app/pages/game/game.component.html | 2 +- src/app/pages/game/game.component.ts | 21 + src/app/pages/welcome/welcome.component.css | 938 +++++++++++-------- src/app/pages/welcome/welcome.component.html | 419 ++++++--- src/app/pages/welcome/welcome.component.ts | 364 +++++-- src/cityscape.html | 0 src/styles-variables.css | 4 +- 9 files changed, 1179 insertions(+), 604 deletions(-) create mode 100644 src/cityscape.html diff --git a/angular.json b/angular.json index fd5ca34..8b25cc2 100644 --- a/angular.json +++ b/angular.json @@ -51,8 +51,8 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "4kB", - "maximumError": "8kB" + "maximumWarning": "12kB", + "maximumError": "20kB" } ], "outputHashing": "all" diff --git a/src/app/pages/game/game.component.css b/src/app/pages/game/game.component.css index 611d0b5..541ccb3 100644 --- a/src/app/pages/game/game.component.css +++ b/src/app/pages/game/game.component.css @@ -1,6 +1,14 @@ .game-shell { min-height: 100dvh; padding: clamp(var(--size-md), 2vw, var(--size-xl)); + background: linear-gradient(180deg, var(--color-primary-light) 0%, var(--color-secondary-mint) 100%); + color: var(--color-text-primary); +} + +:host-context(html[data-theme='dark']) .game-shell { + background: + radial-gradient(circle at top, rgba(185, 194, 218, 0.16) 0%, transparent 35%), + linear-gradient(180deg, #0f1f2e 0%, #17293d 52%, #0b1420 100%); } .game-card { @@ -13,6 +21,11 @@ box-shadow: var(--shadow-md); } +:host-context(html[data-theme='dark']) .game-shell .game-card { + background: rgba(26, 47, 71, 0.88); + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.34); +} + header { margin-bottom: var(--size-xl); } @@ -103,6 +116,24 @@ h2 { container-type: size; } +:host-context(html[data-theme='dark']) .game-shell .board-section, +:host-context(html[data-theme='dark']) .game-shell .timer-card, +:host-context(html[data-theme='dark']) .game-shell .history-card, +:host-context(html[data-theme='dark']) .game-shell .export-card, +:host-context(html[data-theme='dark']) .game-shell .board-theme-card, +:host-context(html[data-theme='dark']) .game-shell .player-timer { + background: rgba(45, 74, 111, 0.72); +} + +:host-context(html[data-theme='dark']) .game-shell .export-text { + background: rgba(26, 47, 71, 0.9); +} + +:host-context(html[data-theme='dark']) .game-shell .game-completion-alert { + background: linear-gradient(135deg, rgba(74, 124, 124, 0.35) 0%, rgba(90, 111, 165, 0.35) 100%); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.25); +} + .timer-card, .history-card, .export-card { diff --git a/src/app/pages/game/game.component.html b/src/app/pages/game/game.component.html index 9d54af4..fc3a919 100644 --- a/src/app/pages/game/game.component.html +++ b/src/app/pages/game/game.component.html @@ -1,4 +1,4 @@ -
+
-
-
- - -
-
+
+
{{ modeBadge }}
- + -
-
- Player One -
-
- Plane - Raf -
-
-
- Player Two +
+
+
+
+
+
+
+
+
+
+
+
-
-
-

Welcome to NowChess

-

Pick a mode to begin.

-
+
+
+
-
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- -
- @if (showDifficultySelector) { -
-

Select difficulty:

-
- - - +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
JOIN
+
JOIN
GAME
+ +
+
+
+
+
- } - @if (showJoinGameForm) { -
-

Enter the game ID:

-
- - - +
+
+
+
+
+
+
+
+
+ OPEN 24/7 +
+
+
BOT
+
PLAY WITH
A BOT
+ +
+
+
+
+
- } - @if (showImportGameForm) { -
-

Import game

+
+ Player One +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
WELCOME
+
WELCOME TO
NOWCHESS
+
Play your next move from the skyline.
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ MORE +
+
+
OPTIONS
+
MORE
OPTIONS
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+ + @if (showDifficultyDialog) { +
+
+
SELECT DIFFICULTY
+
+ + + +
+
+
+ } + + @if (showOptionsDialog) { +
+
+
MORE OPTIONS
+
+ +
+
+
+ } + + @if (showJoinDialog) { +
+
+
JOIN GAME
+ +
+ + +
+
+
+ } + + @if (showImportDialog) { +
+
+
IMPORT GAME
- +
+ + +
- } +
+ } - @if (errorMessage) { -

{{ errorMessage }}

- } -
-
+ @if (errorMessage) { +

{{ errorMessage }}

+ } + diff --git a/src/app/pages/welcome/welcome.component.ts b/src/app/pages/welcome/welcome.component.ts index 0bb52c0..b660cc0 100644 --- a/src/app/pages/welcome/welcome.component.ts +++ b/src/app/pages/welcome/welcome.component.ts @@ -1,11 +1,27 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { finalize } from 'rxjs'; import { getErrorMessage } from '../../core/http/error-message.util'; import { GameApiService } from '../../services/game-api.service'; +type Difficulty = 'easy' | 'medium' | 'hard'; +type ImportMode = 'fen' | 'pgn'; + +interface Star { + style: Record; +} + +interface BackgroundBuilding { + style: Record; +} + +interface WindowCell { + state: 'off' | 'lc' | 'lw'; + style: Record; +} + @Component({ selector: 'app-welcome', standalone: true, @@ -13,18 +29,30 @@ import { GameApiService } from '../../services/game-api.service'; templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.css'] }) -export class WelcomeComponent { +export class WelcomeComponent implements OnInit, OnDestroy { creating = false; - errorMessage = ''; - showDifficultySelector = false; - showJoinGameForm = false; - showImportGameForm = false; - gameIdInput = ''; joiningGame = false; importing = false; - importMode: 'fen' | 'pgn' = 'fen'; + errorMessage = ''; + + showDifficultyDialog = false; + showOptionsDialog = false; + showJoinDialog = false; + showImportDialog = false; + + gameIdInput = ''; + importMode: ImportMode = 'fen'; importText = ''; + isSunsetMode = false; + modeBadge = 'NIGHT MODE'; + + stars: Star[] = []; + bgBuildings: BackgroundBuilding[] = []; + windows: Record = {}; + + private flickerIntervalId: ReturnType | undefined; + constructor( private readonly router: Router, private readonly gameApi: GameApiService @@ -32,11 +60,83 @@ export class WelcomeComponent { this.initTheme(); } - private initTheme(): void { - const savedTheme = localStorage.getItem('theme'); - if (savedTheme === 'dark') { + ngOnInit(): void { + this.generateStars(220); + this.generateBackgroundBuildings(); + this.generateWindowsForAllBuildings(); + this.startWindowFlicker(); + } + + ngOnDestroy(): void { + this.stopWindowFlicker(); + } + + toggleTheme(): void { + this.isSunsetMode = !this.isSunsetMode; + this.modeBadge = this.isSunsetMode ? 'SUNSET MODE' : 'NIGHT MODE'; + + if (!this.isSunsetMode) { document.documentElement.setAttribute('data-theme', 'dark'); + localStorage.setItem('theme', 'dark'); + return; } + + document.documentElement.removeAttribute('data-theme'); + localStorage.removeItem('theme'); + } + + openDifficultyDialog(): void { + this.closeAllDialogs(); + this.showDifficultyDialog = true; + } + + closeDifficultyDialog(): void { + this.showDifficultyDialog = false; + this.errorMessage = ''; + } + + openOptionsDialog(): void { + this.closeAllDialogs(); + this.showOptionsDialog = true; + } + + closeOptionsDialog(): void { + this.showOptionsDialog = false; + this.errorMessage = ''; + } + + openJoinDialog(): void { + this.closeAllDialogs(); + this.showJoinDialog = true; + } + + closeJoinDialog(): void { + if (this.joiningGame) { + return; + } + this.showJoinDialog = false; + this.gameIdInput = ''; + this.errorMessage = ''; + } + + openImportDialog(): void { + this.closeAllDialogs(); + this.showImportDialog = true; + } + + closeImportDialog(): void { + if (this.importing) { + return; + } + this.showImportDialog = false; + this.importText = ''; + this.importMode = 'fen'; + this.errorMessage = ''; + } + + setImportMode(mode: ImportMode): void { + this.importMode = mode; + this.errorMessage = ''; } startOneVsOne(): void { @@ -52,7 +152,9 @@ export class WelcomeComponent { .pipe(finalize(() => (this.creating = false))) .subscribe({ next: (game) => { - void this.router.navigate(['/game', game.gameId]); + void this.router.navigate(['/game', game.gameId], { + state: { theme: this.isSunsetMode ? 'light' : 'dark' } + }); }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Unable to create a game.'); @@ -60,21 +162,23 @@ export class WelcomeComponent { }); } - startVsBot(difficulty: 'easy' | 'medium' | 'hard'): void { + startVsBot(difficulty: Difficulty): void { if (this.creating) { return; } this.errorMessage = ''; this.creating = true; - this.showDifficultySelector = false; + this.showDifficultyDialog = false; this.gameApi .createGameVsBot(difficulty) .pipe(finalize(() => (this.creating = false))) .subscribe({ next: (game) => { - void this.router.navigate(['/game', game.gameId]); + void this.router.navigate(['/game', game.gameId], { + state: { theme: this.isSunsetMode ? 'light' : 'dark' } + }); }, error: (error) => { this.errorMessage = getErrorMessage(error, 'Unable to create a game against bot.'); @@ -82,39 +186,32 @@ export class WelcomeComponent { }); } - toggleDifficultySelector(): void { - this.showDifficultySelector = !this.showDifficultySelector; - this.showJoinGameForm = false; - this.showImportGameForm = false; - this.errorMessage = ''; - } - - toggleJoinGameForm(): void { - this.showJoinGameForm = !this.showJoinGameForm; - this.showDifficultySelector = false; - this.showImportGameForm = false; - this.errorMessage = ''; - this.gameIdInput = ''; - } - - toggleImportGameForm(): void { - this.showImportGameForm = !this.showImportGameForm; - this.showDifficultySelector = false; - this.showJoinGameForm = false; - this.errorMessage = ''; - - if (!this.showImportGameForm) { - this.importText = ''; - this.importMode = 'fen'; + submitJoinGame(): void { + const gameId = this.gameIdInput.trim(); + if (this.joiningGame || !gameId) { + return; } - } - setImportMode(mode: 'fen' | 'pgn'): void { - this.importMode = mode; this.errorMessage = ''; + this.joiningGame = true; + + this.gameApi + .getGame(gameId) + .pipe(finalize(() => (this.joiningGame = false))) + .subscribe({ + next: (game) => { + this.closeJoinDialog(); + void this.router.navigate(['/game', game.gameId], { + state: { theme: this.isSunsetMode ? 'light' : 'dark' } + }); + }, + error: (error) => { + this.errorMessage = getErrorMessage(error, 'Unable to find or join the game.'); + } + }); } - submitImportedGame(): void { + submitImportGame(): void { const trimmedImport = this.importText.trim(); if (this.importing || !trimmedImport) { return; @@ -128,9 +225,10 @@ export class WelcomeComponent { importRequest.pipe(finalize(() => (this.importing = false))).subscribe({ next: (game) => { - this.importText = ''; - this.showImportGameForm = false; - void this.router.navigate(['/game', game.gameId]); + this.closeImportDialog(); + void this.router.navigate(['/game', game.gameId], { + state: { theme: this.isSunsetMode ? 'light' : 'dark' } + }); }, error: (error) => { const defaultMessage = this.importMode === 'fen' ? 'Unable to import FEN.' : 'Unable to import PGN.'; @@ -139,47 +237,157 @@ export class WelcomeComponent { }); } - joinGame(): void { - if (this.joiningGame || !this.gameIdInput.trim()) { + private closeAllDialogs(): void { + this.showDifficultyDialog = false; + this.showOptionsDialog = false; + this.showJoinDialog = false; + this.showImportDialog = false; + this.errorMessage = ''; + } + + private initTheme(): void { + const savedTheme = localStorage.getItem('theme'); + this.isSunsetMode = savedTheme !== 'dark'; + this.modeBadge = this.isSunsetMode ? 'SUNSET MODE' : 'NIGHT MODE'; + + if (!this.isSunsetMode) { + document.documentElement.setAttribute('data-theme', 'dark'); return; } - this.errorMessage = ''; - this.joiningGame = true; + document.documentElement.removeAttribute('data-theme'); + } - this.gameApi - .getGame(this.gameIdInput.trim()) - .pipe(finalize(() => (this.joiningGame = false))) - .subscribe({ - next: (game) => { - void this.router.navigate(['/game', game.gameId]); - }, - error: (error) => { - this.errorMessage = getErrorMessage(error, 'Unable to find or join the game.'); + private generateStars(count: number): void { + this.stars = Array.from({ length: count }, () => { + const size = Math.random() * 2 + 0.5; + return { + style: { + width: `${size}px`, + height: `${size}px`, + left: `${Math.random() * 100}%`, + top: `${Math.random() * 62}%`, + '--d': `${(Math.random() * 3 + 1.5).toFixed(1)}s`, + '--dl': `${-(Math.random() * 6).toFixed(1)}s` } - }); + }; + }); } - clearJoinGameForm(): void { - this.showJoinGameForm = false; - this.gameIdInput = ''; - this.errorMessage = ''; + private generateBackgroundBuildings(): void { + const specs = [ + { l: '0%', w: '7%', h: '30vh' }, + { l: '3%', w: '4%', h: '18vh' }, // New building + { l: '7%', w: '5%', h: '22vh' }, + { l: '11%', w: '8%', h: '28vh' }, + { l: '15%', w: '6%', h: '20vh' }, + { l: '18.5%', w: '4%', h: '18vh' }, + { l: '22.5%', w: '6%', h: '26vh' }, + { l: '28%', w: '5%', h: '25vh' }, + { l: '32%', w: '4%', h: '15vh' }, + { l: '35.5%', w: '4.5%', h: '20vh' }, + { l: '42%', w: '5%', h: '28vh' }, + { l: '47%', w: '5%', h: '22vh' }, // New building + { l: '50%', w: '7%', h: '30vh' }, + { l: '55%', w: '6%', h: '27vh' }, + { l: '60.5%', w: '5%', h: '24vh' }, + { l: '64.5%', w: '3.5%', h: '17vh' }, + { l: '70%', w: '6%', h: '23vh' }, + { l: '75%', w: '4%', h: '19vh' }, + { l: '80.5%', w: '4%', h: '21vh' }, + { l: '85.5%', w: '9%', h: '32vh' }, + { l: '88%', w: '5%', h: '20vh' }, + { l: '91%', w: '3%', h: '16vh' }, // New building + { l: '94%', w: '6%', h: '27vh' } + ]; + + this.bgBuildings = specs.map((spec) => ({ + style: { left: spec.l, width: spec.w, height: spec.h } + })); } - toggleDarkMode(): void { - const htmlElement = document.documentElement; - const isDarkMode = htmlElement.getAttribute('data-theme') === 'dark'; - - if (isDarkMode) { - htmlElement.removeAttribute('data-theme'); - localStorage.removeItem('theme'); - } else { - htmlElement.setAttribute('data-theme', 'dark'); - localStorage.setItem('theme', 'dark'); + private generateWindowsForAllBuildings(): void { + this.windows = { + wA1: this.generateWindows(3, 4, 0.6), + wA2: this.generateWindows(4, 5, 0.55), + wA3: this.generateWindows(5, 18, 0.5), + wB1: this.generateWindows(4, 3, 0.6), + wB2: this.generateWindows(5, 20, 0.55), + wC1: this.generateWindows(5, 3, 0.7), + wC2: this.generateWindows(6, 5, 0.65), + wC3: this.generateWindows(7, 24, 0.6), + wD1: this.generateWindows(6, 3, 0.6), + wD2: this.generateWindows(6, 20, 0.5), + wE1: this.generateWindows(3, 16, 0.45) + }; + } + + private generateWindows(cols: number, rows: number, litRate: number): WindowCell[] { + const total = cols * rows; + return Array.from({ length: total }, () => this.createWindowCell(litRate)); + } + + private createWindowCell(litRate: number): WindowCell { + const random = Math.random(); + let state: WindowCell['state'] = 'off'; + if (random < litRate * 0.58) { + state = 'lc'; + } else if (random < litRate) { + state = 'lw'; + } + + if (state === 'off') { + return { state, style: {} }; + } + + const baseDuration = state === 'lc' ? 3 : 4; + return { + state, + style: { + '--wd': `${(Math.random() * 4 + baseDuration).toFixed(1)}s`, + '--wdl': `${-(Math.random() * 8).toFixed(1)}s` + } + }; + } + + private startWindowFlicker(): void { + this.flickerIntervalId = setInterval(() => { + this.randomFlicker(); + }, 2800); + } + + private stopWindowFlicker(): void { + if (this.flickerIntervalId === undefined) { + return; + } + clearInterval(this.flickerIntervalId); + this.flickerIntervalId = undefined; + } + + private randomFlicker(): void { + const allWindows = Object.values(this.windows).flat(); + if (allWindows.length === 0) { + return; + } + + const pickCount = Math.floor(Math.random() * 6) + 1; + for (let i = 0; i < pickCount; i += 1) { + const target = allWindows[Math.floor(Math.random() * allWindows.length)]; + if (!target) { + continue; + } + + if (target.state === 'off') { + const lit = Math.random() < 0.6 ? 'lc' : 'lw'; + target.state = lit; + target.style = { + '--wd': `${(Math.random() * 4 + (lit === 'lc' ? 3 : 4)).toFixed(1)}s`, + '--wdl': `${-(Math.random() * 8).toFixed(1)}s` + }; + } else { + target.state = 'off'; + target.style = {}; + } } } - - isDarkMode(): boolean { - return document.documentElement.getAttribute('data-theme') === 'dark'; - } } diff --git a/src/cityscape.html b/src/cityscape.html new file mode 100644 index 0000000..e69de29 diff --git a/src/styles-variables.css b/src/styles-variables.css index 32b9dc3..ac13142 100644 --- a/src/styles-variables.css +++ b/src/styles-variables.css @@ -22,11 +22,11 @@ --color-bg-input: #B9DAD1; --color-bg-input-focus: #B9C2DA; --color-bg-button: #C19EF5; - --color-bg-button-hover: #BA6D4B; + --color-bg-button-hover: #ba4ba7; --color-text-primary: #5A2C28; --color-text-button-hover: #F3C8A0; - --color-border: #5A2C28; + --color-border: #5a2843; } /* Dark Mode Colors */