Spaces:
Runtime error
Runtime error
| import { useEffect, useState } from 'react' | |
| export type ThemeMode = 'midnight' | 'garden' | 'forest' | 'arctic' | |
| export interface ThemeColors { | |
| bg: number | |
| bgStr: string | |
| bgPanel: string | |
| border: string | |
| text: string | |
| textMuted: string | |
| player1: number | |
| player1Str: string | |
| player2: number | |
| player2Str: string | |
| gridMinor: number | |
| gridMajor: number | |
| gridBorder: number | |
| win: string | |
| draw: string | |
| waiting: string | |
| playing: string | |
| error: string | |
| errorBg: string | |
| hoverFill: number | |
| hoverFillAlpha: number | |
| hoverStroke: number | |
| hoverStrokeAlpha: number | |
| rematchBg: string | |
| rematchBgHover: string | |
| effectGlow: number | |
| effectSpark: number | |
| effectGold: number | |
| winLineColor: number | |
| particleGold: number | |
| particleSpark: number | |
| screenFlash: number | |
| cellFill: number | |
| cellFillAlt: number | |
| } | |
| export interface ThemeInfo { | |
| label: string | |
| emoji: string | |
| colors: ThemeColors | |
| backgroundImage: string | null | |
| } | |
| const midnight: ThemeColors = { | |
| bg: 0x0d1117, | |
| bgStr: '#0d1117', | |
| bgPanel: 'rgba(22,27,34,0.92)', | |
| border: '#21262d', | |
| text: '#e6edf3', | |
| textMuted: '#8b949e', | |
| player1: 0x58a6ff, | |
| player1Str: '#58a6ff', | |
| player2: 0xf0883e, | |
| player2Str: '#f0883e', | |
| gridMinor: 0x1c2128, | |
| gridMajor: 0x2d333b, | |
| gridBorder: 0x444c5e, | |
| win: '#3fb950', | |
| draw: '#d29922', | |
| waiting: '#8b949e', | |
| playing: '#58a6ff', | |
| error: '#f85149', | |
| errorBg: 'rgba(35,10,10,0.92)', | |
| hoverFill: 0x58a6ff, | |
| hoverFillAlpha: 0.10, | |
| hoverStroke: 0x58a6ff, | |
| hoverStrokeAlpha: 0.25, | |
| rematchBg: '#238636', | |
| rematchBgHover: '#2ea043', | |
| effectGlow: 0x58a6ff, | |
| effectSpark: 0x58a6ff, | |
| effectGold: 0xffd700, | |
| winLineColor: 0x3fb950, | |
| particleGold: 0xffd700, | |
| particleSpark: 0xffffff, | |
| screenFlash: 0xffffff, | |
| cellFill: 0x0d1117, | |
| cellFillAlt: 0x0f141c, | |
| } | |
| const garden: ThemeColors = { | |
| bg: 0xf5f0e8, | |
| bgStr: '#f5f0e8', | |
| bgPanel: 'rgba(245,240,232,0.92)', | |
| border: '#e0d8c8', | |
| text: '#3a3a2e', | |
| textMuted: '#9a9a82', | |
| player1: 0x6a9e6a, | |
| player1Str: '#6a9e6a', | |
| player2: 0xd4869a, | |
| player2Str: '#d4869a', | |
| gridMinor: 0xd8d0ba, | |
| gridMajor: 0xc8c0aa, | |
| gridBorder: 0xb8b098, | |
| win: '#6a9e6a', | |
| draw: '#c9a84c', | |
| waiting: '#9a9a82', | |
| playing: '#6a9e6a', | |
| error: '#d47878', | |
| errorBg: 'rgba(200,80,80,0.12)', | |
| hoverFill: 0x6a9e6a, | |
| hoverFillAlpha: 0.10, | |
| hoverStroke: 0x6a9e6a, | |
| hoverStrokeAlpha: 0.25, | |
| rematchBg: '#6a9e6a', | |
| rematchBgHover: '#7ab07a', | |
| effectGlow: 0x6a9e6a, | |
| effectSpark: 0x6a9e6a, | |
| effectGold: 0xffd700, | |
| winLineColor: 0x6a9e6a, | |
| particleGold: 0xffd700, | |
| particleSpark: 0xd4869a, | |
| screenFlash: 0xffffff, | |
| cellFill: 0xf5f0e8, | |
| cellFillAlt: 0xf2ece0, | |
| } | |
| const forest: ThemeColors = { | |
| bg: 0x0a1f0d, | |
| bgStr: '#0a1f0d', | |
| bgPanel: 'rgba(10,31,13,0.92)', | |
| border: '#1a3d1f', | |
| text: '#d4edda', | |
| textMuted: '#8bb99a', | |
| player1: 0x8bde6d, | |
| player1Str: '#8bde6d', | |
| player2: 0xd4a54d, | |
| player2Str: '#d4a54d', | |
| gridMinor: 0x122a16, | |
| gridMajor: 0x1a3d1f, | |
| gridBorder: 0x2d5234, | |
| win: '#fbbf24', | |
| draw: '#f59e0b', | |
| waiting: '#8bb99a', | |
| playing: '#8bde6d', | |
| error: '#f87171', | |
| errorBg: 'rgba(35,10,10,0.92)', | |
| hoverFill: 0x8bde6d, | |
| hoverFillAlpha: 0.15, | |
| hoverStroke: 0x8bde6d, | |
| hoverStrokeAlpha: 0.30, | |
| rematchBg: '#2d7d3a', | |
| rematchBgHover: '#3a9c4a', | |
| effectGlow: 0x8bde6d, | |
| effectSpark: 0x8bde6d, | |
| effectGold: 0xffd700, | |
| winLineColor: 0xfbbf24, | |
| particleGold: 0xffd700, | |
| particleSpark: 0xd4a54d, | |
| screenFlash: 0xffffff, | |
| cellFill: 0x2a1c0c, | |
| cellFillAlt: 0x26190a, | |
| } | |
| const arctic: ThemeColors = { | |
| bg: 0x0b1620, | |
| bgStr: '#0b1620', | |
| bgPanel: 'rgba(11,22,32,0.92)', | |
| border: '#1a3348', | |
| text: '#deeaf5', | |
| textMuted: '#8aaac4', | |
| player1: 0x7ec8e3, | |
| player1Str: '#7ec8e3', | |
| player2: 0xe8d5b7, | |
| player2Str: '#e8d5b7', | |
| gridMinor: 0x102838, | |
| gridMajor: 0x1a3348, | |
| gridBorder: 0x2a4a62, | |
| win: '#a3d9ff', | |
| draw: '#d4a574', | |
| waiting: '#8aaac4', | |
| playing: '#7ec8e3', | |
| error: '#f87171', | |
| errorBg: 'rgba(35,10,10,0.92)', | |
| hoverFill: 0x7ec8e3, | |
| hoverFillAlpha: 0.15, | |
| hoverStroke: 0x7ec8e3, | |
| hoverStrokeAlpha: 0.30, | |
| rematchBg: '#2a7a9a', | |
| rematchBgHover: '#3a8aaa', | |
| effectGlow: 0x7ec8e3, | |
| effectSpark: 0x7ec8e3, | |
| effectGold: 0xffd700, | |
| winLineColor: 0xa3d9ff, | |
| particleGold: 0xffd700, | |
| particleSpark: 0xe8d5b7, | |
| screenFlash: 0xffffff, | |
| cellFill: 0x0c1e2c, | |
| cellFillAlt: 0x0e2232, | |
| } | |
| export const THEMES: Record<ThemeMode, ThemeInfo> = { | |
| midnight: { label: 'Midnight', emoji: '🌙', colors: midnight, backgroundImage: 'bg/night-bg.jpg?v=3' }, | |
| garden: { label: 'Garden', emoji: '🌷', colors: garden, backgroundImage: null }, | |
| forest: { label: 'Forest', emoji: '🌲', colors: forest, backgroundImage: 'bg/forest_bg.jpg?v=3' }, | |
| arctic: { label: 'Arctic', emoji: '🧊', colors: arctic, backgroundImage: null }, | |
| } | |
| const listeners = new Set<() => void>() | |
| const stored: string | null = null | |
| let currentMode: ThemeMode = stored !== null && stored in THEMES ? stored as ThemeMode : 'forest' | |
| let currentTheme: ThemeColors = THEMES[currentMode].colors | |
| export const theme = new Proxy<ThemeColors>({} as ThemeColors, { | |
| get(_, prop) { | |
| return currentTheme[prop as keyof ThemeColors] | |
| }, | |
| ownKeys() { | |
| return Reflect.ownKeys(currentTheme) as (keyof ThemeColors)[] | |
| }, | |
| getOwnPropertyDescriptor() { | |
| return { configurable: true, enumerable: true } | |
| }, | |
| }) | |
| export function setThemeMode(mode: ThemeMode): void { | |
| if (mode === currentMode) return | |
| currentMode = mode | |
| currentTheme = THEMES[mode].colors | |
| document.body.style.background = currentTheme.bgStr | |
| document.body.style.color = currentTheme.text | |
| listeners.forEach((fn) => fn()) | |
| } | |
| export function getThemeMode(): ThemeMode { | |
| return currentMode | |
| } | |
| export function getThemeBackgroundImage(mode: ThemeMode = currentMode): string | null { | |
| return THEMES[mode].backgroundImage | |
| } | |
| export function onThemeChange(fn: () => void): () => void { | |
| listeners.add(fn) | |
| return () => listeners.delete(fn) | |
| } | |
| export function useThemeMode(): [ThemeMode, (mode: ThemeMode) => void] { | |
| const [mode, setMode] = useState<ThemeMode>(getThemeMode) | |
| useEffect(() => { | |
| return onThemeChange(() => setMode(getThemeMode())) | |
| }, []) | |
| return [mode, setThemeMode] | |
| } | |
| document.body.style.background = currentTheme.bgStr | |
| document.body.style.color = currentTheme.text | |
| let _playerName: string | null = null | |
| let _player2Name: string | null = null | |
| let _playerSymbol: string = 'X' | |
| let _player2Symbol: string = 'O' | |
| let _playerColor: string | null = null | |
| let _player2Color: string | null = null | |
| let _playerDrawTower: boolean = true | |
| let _player2DrawTower: boolean = true | |
| export function setPlayerName(name: string): void { | |
| _playerName = name | |
| } | |
| export function getPlayerName(): string { | |
| return _playerName || 'Caro 1' | |
| } | |
| export function setPlayer2Name(name: string): void { | |
| _player2Name = name | |
| } | |
| export function getPlayer2Name(): string { | |
| return _player2Name || 'Caro 2' | |
| } | |
| export function setPlayerSymbol(symbol: string): void { | |
| _playerSymbol = symbol | |
| } | |
| export function getPlayerSymbol(): string { | |
| return _playerSymbol | |
| } | |
| export function setPlayer1Symbol(symbol: string): void { | |
| setPlayerSymbol(symbol) | |
| } | |
| export function getPlayer1Symbol(): string { | |
| return getPlayerSymbol() | |
| } | |
| export function setPlayer2Symbol(symbol: string): void { | |
| _player2Symbol = symbol | |
| } | |
| export function getPlayer2Symbol(): string { | |
| return _player2Symbol | |
| } | |
| export function setPlayerColor(color: string): void { | |
| _playerColor = color | |
| } | |
| export function getPlayerColor(): string | null { | |
| return _playerColor | |
| } | |
| export function setPlayer2Color(color: string): void { | |
| _player2Color = color | |
| } | |
| export function getPlayer2Color(): string | null { | |
| return _player2Color | |
| } | |
| export function setPlayerDrawTower(draw: boolean): void { | |
| _playerDrawTower = draw | |
| } | |
| export function getPlayerDrawTower(): boolean { | |
| return _playerDrawTower | |
| } | |
| export function setPlayer2DrawTower(draw: boolean): void { | |
| _player2DrawTower = draw | |
| } | |
| export function getPlayer2DrawTower(): boolean { | |
| return _player2DrawTower | |
| } | |