Skip to content

Commit 0fe878f

Browse files
CopilotDevn913
andauthored
feat: larger board (50vw), sample query, slide-down SQL blocks, move sounds
- CSS: --board-sz variable (clamp 320px, 50vw, 760px); player bars/controls follow the same size; responsive breakpoints updated in-place - Animation: fadeSlideIn now slides DOWN (translateY -8px → 0) - SQL ordering: new blocks are prepended so each submitted query pushes older content downward; game-init SQL is appended at the end (atEnd=true) so it stays at the bottom ('at the end') after scrolling - Undo now removes firstChild (most-recent prepended block) - Auto-scroll scrolls to scrollTop=0 to reveal latest block at top - Sounds: Web Audio API playSound('move'|'capture'|'check') with AudioContext singleton; called in executeMove() - Sample query: textarea pre-filled on game start; ⚑ Sample button added - HTML: ⚑ Sample button wired in sql-input-actions Agent-Logs-Url: https://github.com/Devn913/SQL_Chess/sessions/d99e2bd3-f90d-47a3-9be1-466c214ff74b Co-authored-by: Devn913 <56478595+Devn913@users.noreply.github.com>
1 parent 5683590 commit 0fe878f

3 files changed

Lines changed: 101 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: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,53 @@ function showToast(msg, duration = 2500) {
6868
el._timer = setTimeout(() => el.classList.add('hidden'), duration);
6969
}
7070

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

470+
// Play move sound
471+
if (moveResult.captured) {
472+
playSound('capture');
473+
} else {
474+
playSound('move');
475+
}
476+
423477
// Clear SQL input template after a successful board-click move
424478
const sqlMoveInput = document.getElementById('sqlMoveInput');
425479
if (sqlMoveInput && state.sqlInputHasTemplate) {
@@ -438,6 +492,7 @@ function executeMove(from, to, promotion) {
438492
const endSQL = SQLGen.gameEnd(state.chess, state.gameId);
439493
appendSQL(endSQL, 'Game Over', null);
440494
} else if (state.chess.in_check()) {
495+
playSound('check');
441496
const checkSQL = SQLGen.check(state.chess.turn(), state.gameId);
442497
appendSQL(checkSQL, '⚠ Check', null);
443498
}
@@ -732,7 +787,7 @@ function highlightSQL(code) {
732787
.replace(kwRegex, '<span class="sql-kw">$1</span>');
733788
}
734789

735-
function appendSQL(code, label, moveNum) {
790+
function appendSQL(code, label, moveNum, atEnd) {
736791
const placeholder = document.getElementById('sqlPlaceholder');
737792
if (placeholder) placeholder.remove();
738793

@@ -766,12 +821,22 @@ function appendSQL(code, label, moveNum) {
766821

767822
block.appendChild(labelEl);
768823
block.appendChild(codeEl);
769-
content.appendChild(block);
824+
825+
// New blocks slide in at the TOP so each submitted query moves content DOWN;
826+
// only the initial game-setup block is appended at the end.
827+
if (atEnd) {
828+
content.appendChild(block);
829+
} else {
830+
content.prepend(block);
831+
}
770832

771833
state.sqlBlocks.push({ code, label, moveNum });
772834

773835
if (document.getElementById('chkAutoScroll').checked) {
774-
content.scrollTop = content.scrollHeight;
836+
// Scroll to top to reveal the freshly-prepended block
837+
if (!atEnd) {
838+
content.scrollTop = 0;
839+
}
775840
}
776841
}
777842

@@ -796,9 +861,13 @@ function startGame(whiteName, blackName, showSQL, existingPGN) {
796861
document.getElementById('whitePlayerName').textContent = state.whitePlayer;
797862
document.getElementById('blackPlayerName').textContent = state.blackPlayer;
798863

799-
// Clear SQL input
864+
// Clear SQL input and pre-fill with sample query
800865
const sqlMoveInput = document.getElementById('sqlMoveInput');
801-
if (sqlMoveInput) sqlMoveInput.value = '';
866+
if (sqlMoveInput) {
867+
sqlMoveInput.value =
868+
`-- Sample: move the e-pawn two squares forward\nUPDATE chess_piece\nSET position = 'e4'\nWHERE position = 'e2';`;
869+
}
870+
state.sqlInputHasTemplate = false;
802871
clearSQLRunError();
803872

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

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

836905
// Load from PGN if provided (invite link)
@@ -955,10 +1024,10 @@ function init() {
9551024
if (undone.color === 'w') state.capturedByWhite.pop();
9561025
else state.capturedByBlack.pop();
9571026
}
958-
// Remove last SQL block from UI
1027+
// Remove most-recent SQL block from UI (prepended = first child, no id)
9591028
const content = document.getElementById('sqlContent');
960-
if (content.lastChild && !content.lastChild.id) {
961-
content.removeChild(content.lastChild);
1029+
if (content.firstChild && !content.firstChild.id) {
1030+
content.removeChild(content.firstChild);
9621031
state.sqlBlocks.pop();
9631032
}
9641033
state.selectedSquare = null;
@@ -1021,6 +1090,15 @@ function init() {
10211090

10221091
// SQL Move Input
10231092
document.getElementById('btnRunSQL').addEventListener('click', runSQLMove);
1093+
document.getElementById('btnSampleSQL').addEventListener('click', () => {
1094+
const input = document.getElementById('sqlMoveInput');
1095+
if (input) {
1096+
input.value =
1097+
`-- Sample: move the e-pawn two squares forward\nUPDATE chess_piece\nSET position = 'e4'\nWHERE position = 'e2';`;
1098+
}
1099+
state.sqlInputHasTemplate = false;
1100+
clearSQLRunError();
1101+
});
10241102
document.getElementById('btnClearInput').addEventListener('click', () => {
10251103
const input = document.getElementById('sqlMoveInput');
10261104
if (input) input.value = '';

0 commit comments

Comments
 (0)