diff --git a/index.html b/index.html index c3096ad..b9406ff 100644 --- a/index.html +++ b/index.html @@ -24,7 +24,11 @@ diff --git a/public/scripts/copy-game-link.js b/public/scripts/copy-game-link.js index 1eaffc9..dc6a700 100644 --- a/public/scripts/copy-game-link.js +++ b/public/scripts/copy-game-link.js @@ -1,8 +1,9 @@ function copyGameLink() { const gameId = document.querySelector('meta[name="gameId"]').content; const gameLink = `${window.location.origin}${window.location.pathname}?gameId=${gameId}`; - - navigator.clipboard.writeText(gameLink) + + navigator.clipboard + .writeText(gameLink) .then(() => { const copyLinkButton = document.getElementById('copy-link-button'); const copyLinkText = document.getElementById('copy-link-text'); @@ -24,7 +25,7 @@ function copyGameLink() { window.copyButtonTimeoutId = null; }, 3000); }) - .catch(err => { + .catch((err) => { console.error('Failed to copy link: ', err); }); } diff --git a/public/scripts/display-ws-connection.js b/public/scripts/display-ws-connection.js index e173b14..e3618d2 100644 --- a/public/scripts/display-ws-connection.js +++ b/public/scripts/display-ws-connection.js @@ -19,7 +19,6 @@ gameContainer.setAttribute('ws-connect', wsUrl); // Tell HTMX to connect the WebSocket htmx.trigger(gameContainer, ' and connect'); - // Update the WebSocket status indicator const wsStatusDiv = document.getElementById('ws-status'); diff --git a/public/style.css b/public/style.css index 13055f2..8a5703d 100644 --- a/public/style.css +++ b/public/style.css @@ -1,61 +1,79 @@ - - :root { /* PRIMARY BRAND COLORS */ - --color-primary: #C12675; /* Main brand color - primary buttons, links, highlights */ - --color-primary-light: #D84A95; /* Hover states for primary elements, lighter accents */ - --color-primary-dark: #9A1C5B; /* Active states, pressed buttons, darker emphasis */ - --color-primary-subtle: #F4E6EE; /* Background tints, very light overlays, subtle highlights */ - + --color-primary: #c12675; /* Main brand color - primary buttons, links, highlights */ + --color-primary-light: #d84a95; /* Hover states for primary elements, lighter accents */ + --color-primary-dark: #9a1c5b; /* Active states, pressed buttons, darker emphasis */ + --color-primary-subtle: #f4e6ee; /* Background tints, very light overlays, subtle highlights */ + /* SECONDARY ACCENT COLORS */ - --color-secondary: #8B4A9C; /* Secondary buttons, alternative CTAs, complementary actions */ - --color-secondary-light: #A866B8; /* Hover states for secondary elements */ - --color-secondary-dark: #6D3B7A; /* Active states for secondary elements */ - + --color-secondary: #8b4a9c; /* Secondary buttons, alternative CTAs, complementary actions */ + --color-secondary-light: #a866b8; /* Hover states for secondary elements */ + --color-secondary-dark: #6d3b7a; /* Active states for secondary elements */ + /* NEUTRAL GRAYS */ - --color-neutral-50: #FAFAFA; /* Page backgrounds, card backgrounds */ - --color-neutral-100: #F5F5F5; /* Subtle backgrounds, disabled states */ - --color-neutral-200: #E5E5E5; /* Borders, dividers, subtle separators */ - --color-neutral-300: #D4D4D4; /* Input borders, inactive elements */ - --color-neutral-400: #A3A3A3; /* Placeholder text, muted elements */ - --color-neutral-500: #737373; /* Body text, secondary information */ - --color-neutral-600: #525252; /* Headings, important text */ - --color-neutral-700: #404040; /* Primary text, main content */ - --color-neutral-800: #262626; /* High contrast text, emphasis */ - --color-neutral-900: #171717; /* Maximum contrast, headers, navigation */ - + --color-neutral-50: #fafafa; /* Page backgrounds, card backgrounds */ + --color-neutral-100: #f5f5f5; /* Subtle backgrounds, disabled states */ + --color-neutral-200: #e5e5e5; /* Borders, dividers, subtle separators */ + --color-neutral-300: #d4d4d4; /* Input borders, inactive elements */ + --color-neutral-400: #a3a3a3; /* Placeholder text, muted elements */ + --color-neutral-500: #737373; /* Body text, secondary information */ + --color-neutral-600: #525252; /* Headings, important text */ + --color-neutral-700: #404040; /* Primary text, main content */ + --color-neutral-800: #262626; /* High contrast text, emphasis */ + --color-neutral-900: #171717; /* Maximum contrast, headers, navigation */ + /* STATUS & FEEDBACK COLORS */ - --color-success: #059669; /* Success messages, confirmations, positive states */ - --color-success-light: #10B981; /* Success backgrounds, subtle positive indicators */ - --color-warning: #D97706; /* Warning messages, caution states */ - --color-warning-light: #F59E0B; /* Warning backgrounds, attention grabbers */ - --color-error: #DC2626; /* Error messages, destructive actions */ - --color-error-light: #EF4444; /* Error backgrounds, validation errors */ - --color-info: #2563EB; /* Info messages, helpful tips */ - --color-info-light: #3B82F6; /* Info backgrounds, informational highlights */ - + --color-success: #059669; /* Success messages, confirmations, positive states */ + --color-success-light: #10b981; /* Success backgrounds, subtle positive indicators */ + --color-warning: #d97706; /* Warning messages, caution states */ + --color-warning-light: #f59e0b; /* Warning backgrounds, attention grabbers */ + --color-error: #dc2626; /* Error messages, destructive actions */ + --color-error-light: #ef4444; /* Error backgrounds, validation errors */ + --color-info: #2563eb; /* Info messages, helpful tips */ + --color-info-light: #3b82f6; /* Info backgrounds, informational highlights */ + /* SPECIAL PURPOSE COLORS */ - --color-gradient-start: #C12675; /* Start of brand gradients */ - --color-gradient-end: #8B4A9C; /* End of brand gradients, creates depth */ - --color-shadow: rgba(193, 38, 117, 0.15); /* Drop shadows with brand tint */ - --color-overlay: rgba(193, 38, 117, 0.08); /* Modal overlays, background tints */ - + --color-gradient-start: #c12675; /* Start of brand gradients */ + --color-gradient-end: #8b4a9c; /* End of brand gradients, creates depth */ + --color-shadow: rgba(193, 38, 117, 0.15); /* Drop shadows with brand tint */ + --color-overlay: rgba( + 193, + 38, + 117, + 0.08 + ); /* Modal overlays, background tints */ + /* TEXT ON COLORED BACKGROUNDS */ - --color-on-primary: #FFFFFF; /* Text/icons on primary color backgrounds */ - --color-on-secondary: #FFFFFF; /* Text/icons on secondary color backgrounds */ - --color-on-dark: #FFFFFF; /* Text/icons on dark backgrounds */ - --color-on-light: #171717; /* Text/icons on light backgrounds */ - + --color-on-primary: #ffffff; /* Text/icons on primary color backgrounds */ + --color-on-secondary: #ffffff; /* Text/icons on secondary color backgrounds */ + --color-on-dark: #ffffff; /* Text/icons on dark backgrounds */ + --color-on-light: #171717; /* Text/icons on light backgrounds */ + /* INTERACTIVE STATES */ - --color-hover-overlay: rgba(255, 255, 255, 0.1); /* Light overlay for hover states */ - --color-active-overlay: rgba(0, 0, 0, 0.1); /* Dark overlay for active/pressed states */ - --color-focus-ring: rgba(193, 38, 117, 0.3); /* Focus rings for accessibility */ - + --color-hover-overlay: rgba( + 255, + 255, + 255, + 0.1 + ); /* Light overlay for hover states */ + --color-active-overlay: rgba( + 0, + 0, + 0, + 0.1 + ); /* Dark overlay for active/pressed states */ + --color-focus-ring: rgba( + 193, + 38, + 117, + 0.3 + ); /* Focus rings for accessibility */ + /* BACKGROUND VARIATIONS */ - --color-bg-primary: #FFFFFF; /* Main page background */ - --color-bg-secondary: #FAFAFA; /* Secondary sections, cards */ - --color-bg-tertiary: #F5F5F5; /* Sidebar backgrounds, less prominent areas */ - --color-bg-accent: #FDF2F8; /* Very subtle pink background for special sections */ + --color-bg-primary: #ffffff; /* Main page background */ + --color-bg-secondary: #fafafa; /* Secondary sections, cards */ + --color-bg-tertiary: #f5f5f5; /* Sidebar backgrounds, less prominent areas */ + --color-bg-accent: #fdf2f8; /* Very subtle pink background for special sections */ } body { @@ -99,8 +117,8 @@ body { width: 100%; height: 100%; background-image: - linear-gradient(to right, var(--color-neutral-400) 1px, transparent 1px), - linear-gradient(to bottom, var(--color-neutral-400) 1px, transparent 1px); + linear-gradient(to right, var(--color-neutral-400) 1px, transparent 1px), + linear-gradient(to bottom, var(--color-neutral-400) 1px, transparent 1px); background-size: 30px 30px; background-position: -1px -1px; pointer-events: none; @@ -123,7 +141,8 @@ body { background-color: var(--color-hover-overlay); } -.stone-black-heart, .stone-white-heart { +.stone-black-heart, +.stone-white-heart { position: relative; width: 24px; height: 24px; @@ -155,11 +174,13 @@ body { transform-origin: 100% 100%; } -.stone-black-heart::before, .stone-black-heart::after { +.stone-black-heart::before, +.stone-black-heart::after { background-color: var(--color-primary-light); } -.stone-white-heart::before, .stone-white-heart::after { +.stone-white-heart::before, +.stone-white-heart::after { background-color: var(--color-on-primary); } diff --git a/src/view/board-renderer.ts b/src/view/board-renderer.ts index 92b0abe..51e5090 100644 --- a/src/view/board-renderer.ts +++ b/src/view/board-renderer.ts @@ -24,13 +24,14 @@ export function renderGameBoardHtml( const intersectionId = `intersection-${row}-${col}`; let stoneHtml = ''; if (stone) { - const colorClass = stone === 'black' ? 'stone-black-heart' : 'stone-white-heart'; + const colorClass = + stone === 'black' ? 'stone-black-heart' : 'stone-white-heart'; stoneHtml = `
`; } // Calculate top and left for absolute positioning, offset by half the intersection div size - const top = (row * 30); - const left = (col * 30); + const top = row * 30; + const left = col * 30; // HTMX attributes for making a move const wsAttrs = isPlayersTurn && !stone ? `ws-send="click"` : ''; diff --git a/src/web-socket-handler.ts b/src/web-socket-handler.ts index 7b39c79..5330b34 100644 --- a/src/web-socket-handler.ts +++ b/src/web-socket-handler.ts @@ -1,8 +1,6 @@ import { ElysiaWS } from 'elysia/dist/ws'; import { GameInstance } from './game/game-instance'; -import { - renderGameBoardHtml, -} from './view/board-renderer'; +import { renderGameBoardHtml } from './view/board-renderer'; interface MakeMoveMessage { gameId: string; @@ -108,7 +106,9 @@ export class WebSocketHandler { } this.connections.set( gameId, - connectionsInGame.filter((conn) => conn.data.query.playerId !== ws.data.query.playerId), + connectionsInGame.filter( + (conn) => conn.data.query.playerId !== ws.data.query.playerId, + ), ); if (this.connections.get(gameId)?.length === 0) { this.connections.delete(gameId); @@ -186,35 +186,43 @@ export class WebSocketHandler { private sendTitleBox(targetWs: WS, message: string): void { targetWs.send('
' + message + '
'); } - + private sendTitleBoxesForGame(gameId: string): void { const game = this.games.get(gameId); if (!game) { - console.error(`Tried to send title boxes for game ${gameId}, but it doesn't exist!`) + console.error( + `Tried to send title boxes for game ${gameId}, but it doesn't exist!`, + ); return; } const connections = this.connections.get(gameId); if (!connections) { - console.log(`Attempted to send title boxes for game ${gameId}, but no players are connected.`) + console.log( + `Attempted to send title boxes for game ${gameId}, but no players are connected.`, + ); return; } - var message = ""; + var message = ''; switch (game.status) { case 'waiting': { - message = "Waiting for players..."; + message = 'Waiting for players...'; break; } case 'playing': { - const blackTag = game.players.black ? this.playerTag(gameId, game.players.black) : 'Unknown'; - const whiteTag = game.players.white ? this.playerTag(gameId, game.players.white) : 'Unknown'; + const blackTag = game.players.black + ? this.playerTag(gameId, game.players.black) + : 'Unknown'; + const whiteTag = game.players.white + ? this.playerTag(gameId, game.players.white) + : 'Unknown'; message = `${blackTag} vs ${whiteTag}`; break; } case 'finished': { switch (game.winner) { case 'draw': { - message = "Game ended in draw."; + message = 'Game ended in draw.'; break; } case 'black': { @@ -229,12 +237,14 @@ export class WebSocketHandler { break; } } - - connections.forEach((connection) => {this.sendTitleBox(connection, message)}); + + connections.forEach((connection) => { + this.sendTitleBox(connection, message); + }); } private playerTag(gameId: string, playerId: string) { - var connectionIcon = `Disconnected` + var connectionIcon = `Disconnected`; const connections = this.connections.get(gameId); if (connections) { connections.forEach((ws) => { @@ -244,7 +254,7 @@ export class WebSocketHandler { } }); } - return `${playerId}${connectionIcon}` + return `${playerId}${connectionIcon}`; } public getGame(gameId: string): GameInstance | undefined {