Initial commit: Sudoku game with accounts, history, leaderboard, themes and i18n
Node/Express + SQLite backend, vanilla JS frontend, deployed via Docker Compose.
This commit is contained in:
227
app/index.html
Normal file
227
app/index.html
Normal file
@@ -0,0 +1,227 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>Sudoku</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script>
|
||||
(function(){
|
||||
try{
|
||||
var theme = localStorage.getItem('sudoku-theme');
|
||||
if(theme) document.documentElement.dataset.theme = theme;
|
||||
var lang = localStorage.getItem('sudoku-lang');
|
||||
if(lang) document.documentElement.lang = lang;
|
||||
}catch(e){}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<h1>🧩 Sudoku</h1>
|
||||
|
||||
<div class="settings-bar">
|
||||
<div class="lang-switch" id="langSwitch">
|
||||
<button class="lang-btn" data-lang="fr">FR</button>
|
||||
<button class="lang-btn" data-lang="en">EN</button>
|
||||
</div>
|
||||
<div class="theme-switch" id="themeSwitch">
|
||||
<button class="theme-swatch" data-theme="blue"></button>
|
||||
<button class="theme-swatch" data-theme="green"></button>
|
||||
<button class="theme-swatch" data-theme="amber"></button>
|
||||
<button class="theme-swatch" data-theme="mono"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="authbar" id="authbar"></div>
|
||||
|
||||
<div class="anon-banner" id="anonBanner" hidden>
|
||||
<div class="txt" data-i18n-html="banner.html"></div>
|
||||
<div class="btns">
|
||||
<button id="anonBannerRegister" class="primary" data-i18n="auth.register"></button>
|
||||
<button id="anonBannerLogin" data-i18n="auth.login"></button>
|
||||
</div>
|
||||
<button class="close-x" id="anonBannerClose" title="Fermer">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="topbar">
|
||||
<div class="stat"><span data-i18n="topbar.difficulty"></span>
|
||||
<select id="difficulty">
|
||||
<option value="easy" data-i18n="difficulty.easy"></option>
|
||||
<option value="medium" selected data-i18n="difficulty.medium"></option>
|
||||
<option value="hard" data-i18n="difficulty.hard"></option>
|
||||
<option value="expert" data-i18n="difficulty.expert"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="stat"><span data-i18n="topbar.time"></span> <span class="value" id="timer">00:00</span></div>
|
||||
<div class="stat"><span data-i18n="topbar.mistakes"></span> <span class="value" id="mistakes">0</span></div>
|
||||
</div>
|
||||
|
||||
<div class="board-wrap" id="boardWrap">
|
||||
<div class="board" id="board"></div>
|
||||
|
||||
<div class="board-overlay" id="startOverlay">
|
||||
<p data-i18n="board.ready"></p>
|
||||
<button class="big-btn" id="startBtn" data-i18n="board.start"></button>
|
||||
</div>
|
||||
|
||||
<div class="board-overlay" id="pauseOverlay" hidden>
|
||||
<p data-i18n="board.paused"></p>
|
||||
<button class="big-btn" id="resumeBtn" data-i18n="board.resume"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="keypad" id="keypad"></div>
|
||||
|
||||
<div class="actions" id="gameActions" hidden>
|
||||
<button id="pauseBtn" data-i18n="actions.pause"></button>
|
||||
<button id="stopBtn" data-i18n="actions.stop"></button>
|
||||
<button id="undoBtn" data-i18n="actions.undo"></button>
|
||||
<button id="eraseBtn" data-i18n="actions.erase"></button>
|
||||
<button id="notesBtn" data-i18n="actions.notes"></button>
|
||||
<button id="hintBtn" data-i18n="actions.hint"></button>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button id="rulesBtn" data-i18n="actions.rules"></button>
|
||||
<button id="historyBtn" data-i18n="actions.history"></button>
|
||||
<button id="leaderboardBtn" data-i18n="actions.leaderboard"></button>
|
||||
</div>
|
||||
|
||||
<div class="msg" id="msg"></div>
|
||||
<footer data-i18n="footer.text"></footer>
|
||||
</div>
|
||||
|
||||
<!-- Win modal -->
|
||||
<div class="modal-overlay" id="winModal">
|
||||
<div class="modal-card win">
|
||||
<button class="modal-close">✕</button>
|
||||
<h2 data-i18n="win.title"></h2>
|
||||
<p class="sub" id="winSubtitle"></p>
|
||||
<div class="win-stats">
|
||||
<div class="stat"><span class="v" id="winTime"></span><span class="l" data-i18n="win.time"></span></div>
|
||||
<div class="stat"><span class="v" id="winMistakes"></span><span class="l" data-i18n="win.mistakesLabel"></span></div>
|
||||
</div>
|
||||
<p class="sub" id="winPoints"></p>
|
||||
<div id="winCta" hidden style="margin-bottom:14px;">
|
||||
<p class="sub" style="margin-top:0;" data-i18n="win.ctaText"></p>
|
||||
<button class="full-btn" id="winRegisterBtn" data-i18n="win.ctaButton"></button>
|
||||
</div>
|
||||
<button class="full-btn" id="winCloseBtn" data-i18n="win.replay"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth modal -->
|
||||
<div class="modal-overlay" id="authModal">
|
||||
<div class="modal-card">
|
||||
<button class="modal-close">✕</button>
|
||||
<div class="tabs">
|
||||
<button id="authTabLogin" class="active" data-i18n="auth.loginTab"></button>
|
||||
<button id="authTabRegister" data-i18n="auth.registerTab"></button>
|
||||
</div>
|
||||
<p class="error-text" id="authError"></p>
|
||||
|
||||
<form id="loginForm" class="stack">
|
||||
<div class="field">
|
||||
<label for="loginIdentifier" data-i18n="auth.identifierLabel"></label>
|
||||
<input type="text" id="loginIdentifier" required autocomplete="username">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="loginPassword" data-i18n="auth.passwordLabel"></label>
|
||||
<input type="password" id="loginPassword" required autocomplete="current-password">
|
||||
</div>
|
||||
<button type="submit" class="full-btn" data-i18n="auth.submitLogin"></button>
|
||||
</form>
|
||||
|
||||
<form id="registerForm" class="stack" hidden>
|
||||
<ul class="benefits-list">
|
||||
<li data-i18n="auth.benefit1"></li>
|
||||
<li data-i18n="auth.benefit2"></li>
|
||||
<li data-i18n="auth.benefit3"></li>
|
||||
</ul>
|
||||
<div class="field">
|
||||
<label for="registerPseudo" data-i18n="auth.pseudoLabel"></label>
|
||||
<input type="text" id="registerPseudo" required minlength="3" maxlength="20" autocomplete="username">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="registerEmail" data-i18n="auth.emailLabel"></label>
|
||||
<input type="email" id="registerEmail" required autocomplete="email">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="registerPassword" data-i18n="auth.passwordLabel"></label>
|
||||
<input type="password" id="registerPassword" required minlength="6" autocomplete="new-password">
|
||||
</div>
|
||||
<button type="submit" class="full-btn" data-i18n="auth.submitRegister"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- History modal -->
|
||||
<div class="modal-overlay" id="historyModal">
|
||||
<div class="modal-card" style="max-width:480px;">
|
||||
<button class="modal-close">✕</button>
|
||||
<h2 data-i18n="history.title"></h2>
|
||||
<div id="historyBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leaderboard modal -->
|
||||
<div class="modal-overlay" id="leaderboardModal">
|
||||
<div class="modal-card" style="max-width:480px;">
|
||||
<button class="modal-close">✕</button>
|
||||
<h2 data-i18n="leaderboard.title"></h2>
|
||||
<div class="tabs">
|
||||
<button id="lbTabTimes" class="active" data-i18n="leaderboard.tabTimes"></button>
|
||||
<button id="lbTabPoints" data-i18n="leaderboard.tabPoints"></button>
|
||||
</div>
|
||||
<div class="diff-tabs" id="lbDiffTabs">
|
||||
<button data-diff="easy" data-i18n="difficulty.easy"></button>
|
||||
<button data-diff="medium" class="active" data-i18n="difficulty.medium"></button>
|
||||
<button data-diff="hard" data-i18n="difficulty.hard"></button>
|
||||
<button data-diff="expert" data-i18n="difficulty.expert"></button>
|
||||
</div>
|
||||
<div id="leaderboardBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rules / beginner's guide modal -->
|
||||
<div class="modal-overlay" id="rulesModal">
|
||||
<div class="modal-card rules" style="max-width:480px;">
|
||||
<button class="modal-close">✕</button>
|
||||
<h2 data-i18n="rules.title"></h2>
|
||||
|
||||
<h3 data-i18n="rules.objectiveTitle"></h3>
|
||||
<p data-i18n="rules.objectiveText"></p>
|
||||
|
||||
<h3 data-i18n="rules.goldenRulesTitle"></h3>
|
||||
<ol>
|
||||
<li data-i18n="rules.rule1"></li>
|
||||
<li data-i18n="rules.rule2"></li>
|
||||
<li data-i18n="rules.rule3"></li>
|
||||
</ol>
|
||||
|
||||
<h3 data-i18n="rules.howToTitle"></h3>
|
||||
<ul>
|
||||
<li data-i18n="rules.how1"></li>
|
||||
<li data-i18n="rules.how2"></li>
|
||||
<li data-i18n="rules.how3"></li>
|
||||
<li data-i18n="rules.how4"></li>
|
||||
<li data-i18n="rules.how5"></li>
|
||||
</ul>
|
||||
|
||||
<h3 data-i18n="rules.tipsTitle"></h3>
|
||||
<ul>
|
||||
<li data-i18n="rules.tip1"></li>
|
||||
<li data-i18n="rules.tip2"></li>
|
||||
<li data-i18n="rules.tip3"></li>
|
||||
<li data-i18n="rules.tip4"></li>
|
||||
<li data-i18n="rules.tip5"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="i18n.js"></script>
|
||||
<script src="game.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user