Skip to content

Commit 320374f

Browse files
authored
Merge pull request #3 from Devn913/copilot/update-chess-ui-element
UI overhaul: larger board, slide-down SQL log, move sounds, sample query
2 parents 5683590 + 11f0010 commit 320374f

3 files changed

Lines changed: 105 additions & 31 deletions

File tree

css/style.css

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
--bg-elevated: #21262d;
1111
--bg-border: #30363d;
1212

13+
/* Board size: at least 50 % of viewport width, capped at 760 px */
14+
--board-sz: clamp(320px, 50vw, 760px);
15+
1316
--text-primary: #e6edf3;
1417
--text-secondary: #8b949e;
1518
--text-muted: #484f58;
@@ -169,7 +172,7 @@ a { color: var(--accent-blue); }
169172
align-items: center;
170173
gap: .6rem;
171174
width: 100%;
172-
max-width: 480px;
175+
max-width: var(--board-sz);
173176
padding: .4rem .6rem;
174177
background: var(--bg-surface);
175178
border: 1px solid var(--bg-border);
@@ -261,9 +264,8 @@ a { color: var(--accent-blue); }
261264
border-radius: 3px;
262265
overflow: hidden;
263266
box-shadow: var(--shadow-lg);
264-
/* Size is set dynamically in JS but default here */
265-
width: min(57vw, 460px);
266-
height: min(57vw, 460px);
267+
width: var(--board-sz);
268+
height: var(--board-sz);
267269
}
268270

269271
/* Individual squares */
@@ -308,7 +310,7 @@ a { color: var(--accent-blue); }
308310

309311
/* Pieces */
310312
.piece {
311-
font-size: calc(min(57vw, 460px) / 10);
313+
font-size: calc(var(--board-sz) / 10);
312314
line-height: 1;
313315
pointer-events: none;
314316
display: flex;
@@ -327,7 +329,7 @@ a { color: var(--accent-blue); }
327329
align-items: center;
328330
gap: .5rem;
329331
width: 100%;
330-
max-width: 480px;
332+
max-width: var(--board-sz);
331333
}
332334

333335
.game-status-text {
@@ -340,7 +342,7 @@ a { color: var(--accent-blue); }
340342
/* ── Move History ───────────────────────────────────────────── */
341343
.move-history-box {
342344
width: 100%;
343-
max-width: 480px;
345+
max-width: var(--board-sz);
344346
background: var(--bg-surface);
345347
border: 1px solid var(--bg-border);
346348
border-radius: var(--radius);
@@ -472,7 +474,7 @@ a { color: var(--accent-blue); }
472474
animation: fadeSlideIn .2s ease;
473475
}
474476
@keyframes fadeSlideIn {
475-
from { opacity: 0; transform: translateY(6px); }
477+
from { opacity: 0; transform: translateY(-8px); }
476478
to { opacity: 1; transform: translateY(0); }
477479
}
478480

@@ -837,14 +839,7 @@ input:checked + .slider::before { transform: translateX(18px); }
837839
border-top: none;
838840
}
839841

840-
.board-grid {
841-
width: min(90vw, 420px);
842-
height: min(90vw, 420px);
843-
}
844-
845-
.piece {
846-
font-size: calc(min(90vw, 420px) / 10);
847-
}
842+
:root { --board-sz: min(90vw, 420px); }
848843
}
849844

850845
@media (max-width: 480px) {
@@ -853,11 +848,7 @@ input:checked + .slider::before { transform: translateX(18px); }
853848
.chess-panel { padding: .5rem; }
854849
.header-controls { gap: .3rem; }
855850

856-
.board-grid {
857-
width: min(92vw, 360px);
858-
height: min(92vw, 360px);
859-
}
860-
.piece { font-size: calc(min(92vw, 360px) / 10); }
851+
:root { --board-sz: min(92vw, 360px); }
861852

862853
#btnToggleSQL #sqlToggleLabel { display: none; }
863854
}

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ <h2 class="sql-title">
112112
placeholder="UPDATE chess_piece&#10;SET position = 'e4'&#10;WHERE position = 'e2';&#10;&#10;-- or shorthand: e2 e4"></textarea>
113113
<div class="sql-input-actions">
114114
<span class="sql-run-error hidden" id="sqlRunError"></span>
115+
<button type="button" id="btnSampleSQL" class="btn btn-xs" title="Load a sample move query">⚑ Sample</button>
115116
<button type="button" id="btnClearInput" class="btn btn-xs">Clear</button>
116117
<button type="button" id="btnRunSQL" class="btn btn-primary btn-sm">▶ Run</button>
117118
</div>

js/app.js

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
'use strict';
1717

1818
/* ─── Configuration ──────────────────────────────────────────── */
19+
const SAMPLE_SQL_QUERY =
20+
`-- Sample: move the e-pawn two squares forward\nUPDATE chess_piece\nSET position = 'e4'\nWHERE position = 'e2';`;
21+
1922
const PIECE_UNICODE = {
2023
wK: '♔', wQ: '♕', wR: '♖', wB: '♗', wN: '♘', wP: '♙',
2124
bK: '♚', bQ: '♛', bR: '♜', bB: '♝', bN: '♞', bP: '♟',
@@ -68,6 +71,53 @@ function showToast(msg, duration = 2500) {
6871
el._timer = setTimeout(() => el.classList.add('hidden'), duration);
6972
}
7073

74+
/* ─── Audio ───────────────────────────────────────────────────── */
75+
let _audioCtx = null;
76+
77+
function getAudioCtx() {
78+
if (!_audioCtx) {
79+
_audioCtx = new (window.AudioContext || window.webkitAudioContext)();
80+
}
81+
if (_audioCtx.state === 'suspended') { _audioCtx.resume(); }
82+
return _audioCtx;
83+
}
84+
85+
function playSound(type) {
86+
try {
87+
const ctx = getAudioCtx();
88+
const o = ctx.createOscillator();
89+
const g = ctx.createGain();
90+
o.connect(g);
91+
g.connect(ctx.destination);
92+
if (type === 'capture') {
93+
o.type = 'sawtooth';
94+
o.frequency.setValueAtTime(520, ctx.currentTime);
95+
o.frequency.exponentialRampToValueAtTime(180, ctx.currentTime + 0.18);
96+
g.gain.setValueAtTime(0.35, ctx.currentTime);
97+
g.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.18);
98+
o.start(ctx.currentTime);
99+
o.stop(ctx.currentTime + 0.18);
100+
} else if (type === 'check') {
101+
o.type = 'square';
102+
o.frequency.setValueAtTime(880, ctx.currentTime);
103+
o.frequency.setValueAtTime(660, ctx.currentTime + 0.08);
104+
g.gain.setValueAtTime(0.2, ctx.currentTime);
105+
g.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.25);
106+
o.start(ctx.currentTime);
107+
o.stop(ctx.currentTime + 0.25);
108+
} else {
109+
// normal move
110+
o.type = 'sine';
111+
o.frequency.setValueAtTime(900, ctx.currentTime);
112+
o.frequency.exponentialRampToValueAtTime(650, ctx.currentTime + 0.09);
113+
g.gain.setValueAtTime(0.22, ctx.currentTime);
114+
g.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.1);
115+
o.start(ctx.currentTime);
116+
o.stop(ctx.currentTime + 0.1);
117+
}
118+
} catch (e) { /* ignore — AudioContext may be unavailable */ }
119+
}
120+
71121
/* ─── Board Rendering ─────────────────────────────────────────── */
72122
function buildBoard() {
73123
const board = document.getElementById('board');
@@ -420,6 +470,13 @@ function executeMove(from, to, promotion) {
420470
state.validMoves = [];
421471
state.moveCount++;
422472

473+
// Play move sound
474+
if (moveResult.captured) {
475+
playSound('capture');
476+
} else {
477+
playSound('move');
478+
}
479+
423480
// Clear SQL input template after a successful board-click move
424481
const sqlMoveInput = document.getElementById('sqlMoveInput');
425482
if (sqlMoveInput && state.sqlInputHasTemplate) {
@@ -438,6 +495,7 @@ function executeMove(from, to, promotion) {
438495
const endSQL = SQLGen.gameEnd(state.chess, state.gameId);
439496
appendSQL(endSQL, 'Game Over', null);
440497
} else if (state.chess.in_check()) {
498+
playSound('check');
441499
const checkSQL = SQLGen.check(state.chess.turn(), state.gameId);
442500
appendSQL(checkSQL, '⚠ Check', null);
443501
}
@@ -732,7 +790,7 @@ function highlightSQL(code) {
732790
.replace(kwRegex, '<span class="sql-kw">$1</span>');
733791
}
734792

735-
function appendSQL(code, label, moveNum) {
793+
function appendSQL(code, label, moveNum, atEnd) {
736794
const placeholder = document.getElementById('sqlPlaceholder');
737795
if (placeholder) placeholder.remove();
738796

@@ -766,12 +824,25 @@ function appendSQL(code, label, moveNum) {
766824

767825
block.appendChild(labelEl);
768826
block.appendChild(codeEl);
769-
content.appendChild(block);
827+
828+
// New blocks slide in at the TOP so each submitted query moves content DOWN;
829+
// only the initial game-setup block is appended at the end.
830+
if (atEnd) {
831+
content.appendChild(block);
832+
} else {
833+
content.prepend(block);
834+
}
770835

771836
state.sqlBlocks.push({ code, label, moveNum });
772837

773838
if (document.getElementById('chkAutoScroll').checked) {
774-
content.scrollTop = content.scrollHeight;
839+
if (atEnd) {
840+
// Game-init block appended at bottom — scroll down to show it
841+
content.scrollTop = content.scrollHeight;
842+
} else {
843+
// Move blocks prepended at top — scroll up to reveal the new block
844+
content.scrollTop = 0;
845+
}
775846
}
776847
}
777848

@@ -796,9 +867,12 @@ function startGame(whiteName, blackName, showSQL, existingPGN) {
796867
document.getElementById('whitePlayerName').textContent = state.whitePlayer;
797868
document.getElementById('blackPlayerName').textContent = state.blackPlayer;
798869

799-
// Clear SQL input
870+
// Clear SQL input and pre-fill with sample query
800871
const sqlMoveInput = document.getElementById('sqlMoveInput');
801-
if (sqlMoveInput) sqlMoveInput.value = '';
872+
if (sqlMoveInput) {
873+
sqlMoveInput.value = SAMPLE_SQL_QUERY;
874+
}
875+
state.sqlInputHasTemplate = true;
802876
clearSQLRunError();
803877

804878
// SQL panel visibility
@@ -828,9 +902,9 @@ function startGame(whiteName, blackName, showSQL, existingPGN) {
828902
`<p class="placeholder-hint">Each chess move is translated into<br/>real SQL statements in real time.</p>`;
829903
sqlContent.appendChild(placeholder);
830904

831-
// Emit game-start SQL
905+
// Emit game-start SQL (placed at the end so move SQL slides in above it)
832906
const initSQL = SQLGen.gameStart(state.gameId, state.whitePlayer, state.blackPlayer);
833-
appendSQL(initSQL, 'Game Initialized', null);
907+
appendSQL(initSQL, 'Game Initialized', null, true);
834908
}
835909

836910
// Load from PGN if provided (invite link)
@@ -955,10 +1029,10 @@ function init() {
9551029
if (undone.color === 'w') state.capturedByWhite.pop();
9561030
else state.capturedByBlack.pop();
9571031
}
958-
// Remove last SQL block from UI
1032+
// Remove most-recent SQL block from UI (prepended = first child, no id)
9591033
const content = document.getElementById('sqlContent');
960-
if (content.lastChild && !content.lastChild.id) {
961-
content.removeChild(content.lastChild);
1034+
if (content.firstChild && !content.firstChild.id) {
1035+
content.removeChild(content.firstChild);
9621036
state.sqlBlocks.pop();
9631037
}
9641038
state.selectedSquare = null;
@@ -1021,6 +1095,14 @@ function init() {
10211095

10221096
// SQL Move Input
10231097
document.getElementById('btnRunSQL').addEventListener('click', runSQLMove);
1098+
document.getElementById('btnSampleSQL').addEventListener('click', () => {
1099+
const input = document.getElementById('sqlMoveInput');
1100+
if (input) {
1101+
input.value = SAMPLE_SQL_QUERY;
1102+
}
1103+
state.sqlInputHasTemplate = true;
1104+
clearSQLRunError();
1105+
});
10241106
document.getElementById('btnClearInput').addEventListener('click', () => {
10251107
const input = document.getElementById('sqlMoveInput');
10261108
if (input) input.value = '';

0 commit comments

Comments
 (0)