Docfile commited on
Commit
8dc0e70
·
verified ·
1 Parent(s): 85da2b3

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +316 -163
templates/index.html CHANGED
@@ -1,174 +1,327 @@
1
  <!DOCTYPE html>
2
- <html lang="en" data-bs-theme="dark">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Gemini Podcast Generator</title>
7
- <!-- Bootstrap CSS (Replit Theme) -->
8
- <link href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css" rel="stylesheet">
9
- <!-- Font Awesome for icons -->
10
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
- <!-- Custom CSS -->
12
- <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  </head>
14
- <body>
15
- <div class="container py-4">
16
- <header class="mb-5 text-center">
17
- <h1 class="display-4">Gemini Podcast Generator</h1>
18
- <p class="lead">Créez un podcast à partir d'un scénario avec des personnages multiples</p>
19
- </header>
20
-
21
- <div class="row justify-content-center">
22
- <div class="col-lg-10">
23
- <ul class="nav nav-tabs mb-3" id="myTab" role="tablist">
24
- <li class="nav-item" role="presentation">
25
- <button class="nav-link active" id="simple-tab" data-bs-toggle="tab" data-bs-target="#simple-tts" type="button" role="tab" aria-controls="simple-tts" aria-selected="true">
26
- <i class="fas fa-comment me-2"></i>Texte Simple
27
- </button>
28
- </li>
29
- <li class="nav-item" role="presentation">
30
- <button class="nav-link" id="scenario-tab" data-bs-toggle="tab" data-bs-target="#scenario-podcast" type="button" role="tab" aria-controls="scenario-podcast" aria-selected="false">
31
- <i class="fas fa-microphone-alt me-2"></i>Scénario Podcast
32
- </button>
33
- </li>
34
- </ul>
35
-
36
- <div class="tab-content" id="myTabContent">
37
- <!-- Simple Text-to-Speech Tab -->
38
- <div class="tab-pane fade show active" id="simple-tts" role="tabpanel" aria-labelledby="simple-tab">
39
- <div class="card shadow-sm">
40
- <div class="card-body">
41
- <form id="ttsForm">
42
- <div class="mb-3">
43
- <label for="textInput" class="form-label">Entrez votre texte</label>
44
- <textarea
45
- class="form-control"
46
- id="textInput"
47
- rows="5"
48
- placeholder="Saisissez le texte que vous souhaitez convertir en parole..."
49
- required
50
- ></textarea>
51
- </div>
52
-
53
- <div class="mb-3">
54
- <label for="voiceSelect" class="form-label">Sélectionnez une voix</label>
55
- <select class="form-select" id="voiceSelect">
56
- {% for voice in voices %}
57
- <option value="{{ voice }}">{{ voice }}</option>
58
- {% endfor %}
59
- </select>
60
- </div>
61
-
62
- <div class="d-grid">
63
- <button
64
- type="submit"
65
- class="btn btn-primary"
66
- id="generateBtn"
67
- >
68
- <i class="fas fa-microphone me-2"></i>Générer l'audio
69
- </button>
70
- </div>
71
- </form>
72
- </div>
73
- </div>
74
- </div>
75
-
76
- <!-- Scenario Podcast Tab -->
77
- <div class="tab-pane fade" id="scenario-podcast" role="tabpanel" aria-labelledby="scenario-tab">
78
- <div class="card shadow-sm">
79
- <div class="card-body">
80
- <form id="podcastForm">
81
- <div class="mb-3">
82
- <label for="scenarioInput" class="form-label">Entrez votre scénario (JSON)</label>
83
- <textarea
84
- class="form-control"
85
- id="scenarioInput"
86
- rows="12"
87
- placeholder='{"title": "Titre du podcast", "language": "fr-FR", "characters": [{"name": "Personnage1", "voice": "Kore", "text": "Texte du personnage 1"}, {"name": "Personnage2", "voice": "Puck", "text": "Texte du personnage 2"}]}'
88
- required
89
- ></textarea>
90
- </div>
91
-
92
- <div class="alert alert-info">
93
- <h6><i class="fas fa-info-circle me-2"></i>Format du scénario:</h6>
94
- <pre class="mb-0"><code>{
95
- "title": "Titre du podcast",
96
- "language": "fr-FR",
97
- "characters": [
98
- {
99
- "name": "Personnage1",
100
- "voice": "Kore",
101
- "text": "Texte du personnage 1"
102
- },
103
- {
104
- "name": "Personnage2",
105
- "voice": "Puck",
106
- "text": "Texte du personnage 2"
107
- }
108
- ]
109
- }</code></pre>
110
- </div>
111
-
112
- <div class="d-grid mt-4">
113
- <button
114
- type="submit"
115
- class="btn btn-primary"
116
- id="generatePodcastBtn"
117
- >
118
- <i class="fas fa-podcast me-2"></i>Générer le podcast
119
- </button>
120
- </div>
121
- </form>
122
- </div>
123
- </div>
124
- </div>
125
- </div>
126
-
127
- <div class="card mt-4 shadow-sm d-none" id="audioCard">
128
- <div class="card-body">
129
- <h5 class="card-title">Audio généré</h5>
130
- <div class="text-center my-4">
131
- <div id="audioPlayerContainer" class="mb-3">
132
- <audio id="audioPlayer" controls class="w-100"></audio>
133
- </div>
134
- <button class="btn btn-success me-2" id="downloadBtn">
135
- <i class="fas fa-download me-2"></i>Télécharger
136
- </button>
137
- <button class="btn btn-secondary" id="newGenerationBtn">
138
- <i class="fas fa-plus me-2"></i>Nouvelle génération
139
- </button>
140
- </div>
141
- </div>
142
- </div>
143
-
144
- <!-- Loading Indicator -->
145
- <div class="text-center mt-4 d-none" id="loadingIndicator">
146
- <div class="spinner-border text-primary" role="status">
147
- <span class="visually-hidden">Chargement...</span>
148
- </div>
149
- <p class="mt-2">Génération en cours avec Gemini AI...</p>
150
- <div class="progress mt-3 d-none" id="progressBar">
151
- <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
152
- </div>
153
- <p class="mt-2 d-none" id="progressText">Préparation du podcast...</p>
154
- </div>
155
-
156
- <!-- Error Alert -->
157
- <div class="alert alert-danger mt-4 d-none" id="errorAlert" role="alert">
158
- <i class="fas fa-exclamation-triangle me-2"></i>
159
- <span id="errorMessage"></span>
160
- </div>
161
  </div>
162
  </div>
163
 
164
- <footer class="mt-5 pt-4 text-center text-muted">
165
- <p>Powered by Google Gemini AI &copy; <script>document.write(new Date().getFullYear())</script></p>
166
- </footer>
167
  </div>
168
 
169
- <!-- Bootstrap JS -->
170
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
171
- <!-- Custom JS -->
172
- <script src="{{ url_for('static', filename='js/main.js') }}"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  </body>
174
- </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="fr">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Jeu d'Échecs</title>
7
+ <!-- Utilisation du CDN de Tailwind CSS pour la simplicité -->
8
+ <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
9
+ <style>
10
+ /* Styles additionnels si besoin */
11
+ .piece-font { /* Optionnel: si vous voulez une police spécifique pour les pièces unicode */
12
+ font-family: 'Arial Unicode MS', 'DejaVu Sans', 'Symbola', sans-serif;
13
+ }
14
+ .selected-square {
15
+ background-color: #48bb78 !important; /* Vert pour la sélection */
16
+ outline: 2px solid #2f855a;
17
+ }
18
+ .possible-move {
19
+ background-color: rgba(72, 187, 120, 0.3) !important; /* Vert clair pour les coups possibles */
20
+ }
21
+ .last-move-highlight {
22
+ background-color: rgba(251, 191, 36, 0.4) !important; /* Jaune pour le dernier coup */
23
+ }
24
+ #chessboard {
25
+ display: grid;
26
+ grid-template-columns: repeat(8, minmax(0, 1fr));
27
+ grid-template-rows: repeat(8, minmax(0, 1fr));
28
+ width: 400px; /* Ajustez selon vos besoins */
29
+ height: 400px; /* Ajustez selon vos besoins */
30
+ border: 2px solid #4A5568; /* gray-700 */
31
+ }
32
+ .square {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ font-size: 2.25rem; /* text-4xl */
37
+ cursor: pointer;
38
+ user-select: none; /* Empêcher la sélection de texte des pièces */
39
+ }
40
+ .light-square {
41
+ background-color: #F7FAFC; /* gray-100 */
42
+ }
43
+ .dark-square {
44
+ background-color: #A0AEC0; /* gray-500 */
45
+ }
46
+ </style>
47
  </head>
48
+ <body class="bg-gray-200 flex flex-col items-center justify-center min-h-screen p-4">
49
+
50
+ <div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-lg">
51
+ <h1 class="text-3xl font-bold text-center text-gray-800 mb-6">Jeu d'Échecs</h1>
52
+
53
+ <div class="mb-4 flex justify-around">
54
+ <button id="new-game-human" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
55
+ Humain vs Humain
56
+ </button>
57
+ <button id="new-game-ai" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
58
+ Jouer vs IA
59
+ </button>
60
+ </div>
61
+
62
+ <div class="flex flex-col items-center">
63
+ <div id="chessboard" class="mb-4">
64
+ <!-- Les cases de l'échiquier seront générées ici par JavaScript -->
65
+ </div>
66
+
67
+ <div id="turn-indicator" class="text-xl font-semibold text-gray-700 mb-2">
68
+ Tour: Blancs
69
+ </div>
70
+ <div id="status-message" class="text-md text-gray-600 mb-4 h-6">
71
+ <!-- Les messages de statut apparaîtront ici -->
72
+ </div>
73
+ <div id="evaluation-indicator" class="text-sm text-gray-500 mb-4 h-5">
74
+ <!-- L'évaluation de Stockfish (optionnel) -->
75
+ </div>
76
+ </div>
77
+
78
+ <div class="mt-4 p-4 bg-gray-100 rounded max-h-48 overflow-y-auto">
79
+ <h3 class="font-semibold text-gray-700 mb-2">Historique des coups:</h3>
80
+ <div id="history-log" class="text-sm text-gray-600">
81
+ <!-- L'historique des coups sera affiché ici -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  </div>
83
  </div>
84
 
 
 
 
85
  </div>
86
 
87
+ <script>
88
+ const boardElement = document.getElementById('chessboard');
89
+ const turnIndicatorElement = document.getElementById('turn-indicator');
90
+ const statusMessageElement = document.getElementById('status-message');
91
+ const evaluationIndicatorElement = document.getElementById('evaluation-indicator'); // Pour l'évaluation
92
+ const newGameAiButton = document.getElementById('new-game-ai');
93
+ const newGameHumanButton = document.getElementById('new-game-human');
94
+ const historyLogElement = document.getElementById('history-log');
95
+
96
+ let selectedSquareId = null;
97
+ let currentFen = '';
98
+ let currentPlayerTurn = 'w'; // 'w' for white, 'b' for black
99
+ let gameMode = 'human'; // 'human' or 'ai'
100
+ let lastMoveSquares = []; // Pour surligner le dernier coup [from, to]
101
+
102
+ const pieceUnicode = {
103
+ 'p': '♟', 'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚',
104
+ 'P': '♙', 'R': '♖', 'N': '♘', 'B': '♗', 'Q': '♕', 'K': '♔'
105
+ };
106
+ const pieceColor = {
107
+ 'p': 'b', 'r': 'b', 'n': 'b', 'b': 'b', 'q': 'b', 'k': 'b',
108
+ 'P': 'w', 'R': 'w', 'N': 'w', 'B': 'w', 'Q': 'w', 'K': 'w'
109
+ };
110
+
111
+ function getSquareId(rankIndex, fileIndex) {
112
+ return `${String.fromCharCode(97 + fileIndex)}${8 - rankIndex}`;
113
+ }
114
+
115
+ function clearHighlights() {
116
+ document.querySelectorAll('.selected-square').forEach(el => el.classList.remove('selected-square'));
117
+ document.querySelectorAll('.possible-move').forEach(el => el.classList.remove('possible-move'));
118
+ // Ne pas enlever last-move-highlight ici, on le gère au moment de renderBoard
119
+ }
120
+
121
+ function renderBoard(fen, lastHumanMove = null, lastAiMove = null) {
122
+ currentFen = fen;
123
+ boardElement.innerHTML = ''; // Clear previous board
124
+ const fenParts = fen.split(' ');
125
+ const boardState = fenParts[0];
126
+ currentPlayerTurn = fenParts[1]; // 'w' or 'b'
127
+
128
+ // Gestion du surlignage du dernier coup
129
+ document.querySelectorAll('.last-move-highlight').forEach(el => el.classList.remove('last-move-highlight'));
130
+ lastMoveSquares = [];
131
+ if (lastAiMove) { // Si l'IA a joué, son coup est le plus récent
132
+ lastMoveSquares = [lastAiMove.substring(0,2), lastAiMove.substring(2,4)];
133
+ } else if (lastHumanMove) { // Sinon, c'est le coup humain
134
+ lastMoveSquares = [lastHumanMove.substring(0,2), lastHumanMove.substring(2,4)];
135
+ }
136
+
137
+
138
+ const ranks = boardState.split('/');
139
+ for (let rankIndex = 0; rankIndex < 8; rankIndex++) {
140
+ const rank = ranks[rankIndex];
141
+ let fileIndex = 0;
142
+ for (const char of rank) {
143
+ if (isNaN(parseInt(char))) { // It's a piece
144
+ const squareId = getSquareId(rankIndex, fileIndex);
145
+ const squareElement = createSquareElement(squareId, rankIndex, fileIndex, pieceUnicode[char]);
146
+ boardElement.appendChild(squareElement);
147
+ fileIndex++;
148
+ } else { // It's a number for empty squares
149
+ for (let i = 0; i < parseInt(char); i++) {
150
+ const squareId = getSquareId(rankIndex, fileIndex);
151
+ const squareElement = createSquareElement(squareId, rankIndex, fileIndex, '');
152
+ boardElement.appendChild(squareElement);
153
+ fileIndex++;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ updateTurnIndicator();
159
+ }
160
+
161
+ function createSquareElement(id, rankIndex, fileIndex, pieceSymbol) {
162
+ const squareElement = document.createElement('div');
163
+ squareElement.id = id;
164
+ squareElement.classList.add('square', 'piece-font');
165
+ squareElement.classList.add((rankIndex + fileIndex) % 2 === 0 ? 'light-square' : 'dark-square');
166
+ squareElement.textContent = pieceSymbol;
167
+ squareElement.dataset.piece = pieceSymbol; // Stocker la pièce pour une vérification facile
168
+
169
+ if (lastMoveSquares.includes(id)) {
170
+ squareElement.classList.add('last-move-highlight');
171
+ }
172
+
173
+ squareElement.addEventListener('click', handleSquareClick);
174
+ return squareElement;
175
+ }
176
+
177
+
178
+ function updateTurnIndicator() {
179
+ turnIndicatorElement.textContent = `Tour: ${currentPlayerTurn === 'w' ? 'Blancs' : 'Noirs'}`;
180
+ }
181
+
182
+ function updateHistoryLog(historyArray) {
183
+ historyLogElement.innerHTML = '';
184
+ if (!historyArray || historyArray.length === 0) {
185
+ historyLogElement.textContent = "Aucun coup joué.";
186
+ return;
187
+ }
188
+ let moveNumber = 1;
189
+ for (let i = 0; i < historyArray.length; i += 2) {
190
+ const whiteMove = historyArray[i];
191
+ const blackMove = historyArray[i+1] ? historyArray[i+1] : '';
192
+ const logEntry = document.createElement('div');
193
+ logEntry.textContent = `${moveNumber}. ${whiteMove} ${blackMove}`;
194
+ historyLogElement.appendChild(logEntry);
195
+ moveNumber++;
196
+ }
197
+ historyLogElement.scrollTop = historyLogElement.scrollHeight; // Scroll to bottom
198
+ }
199
+
200
+ async function handleSquareClick(event) {
201
+ const clickedSquareElement = event.currentTarget; // L'élément div de la case
202
+ const clickedSquareId = clickedSquareElement.id;
203
+
204
+ // Si la partie est terminée, ne rien faire
205
+ if (statusMessageElement.textContent.includes("Mat!") || statusMessageElement.textContent.includes("Pat!")) {
206
+ return;
207
+ }
208
+
209
+ const pieceSymbol = clickedSquareElement.dataset.piece;
210
+ const isPieceOnSquare = pieceSymbol && pieceSymbol.trim() !== '';
211
+
212
+ if (!selectedSquareId) { // Premier clic: sélection d'une pièce
213
+ if (isPieceOnSquare) {
214
+ // Vérifier si c'est la pièce du joueur actuel
215
+ const colorOfPiece = pieceColor[pieceSymbol];
216
+ if (colorOfPiece === currentPlayerTurn) {
217
+ selectedSquareId = clickedSquareId;
218
+ clickedSquareElement.classList.add('selected-square');
219
+ // TODO: Optionnel - appeler le backend pour obtenir les coups légaux et les surligner
220
+ }
221
+ }
222
+ } else { // Deuxième clic: tentative de déplacement
223
+ const previousSelectedElement = document.getElementById(selectedSquareId);
224
+ previousSelectedElement?.classList.remove('selected-square');
225
+
226
+ if (selectedSquareId === clickedSquareId) { // Clic sur la même case pour désélectionner
227
+ selectedSquareId = null;
228
+ return;
229
+ }
230
+
231
+ const moveUci = `${selectedSquareId}${clickedSquareId}`;
232
+ // Vérification si c'est une promotion de pion (simplifié, suppose promotion en reine)
233
+ const fromSquare = document.getElementById(selectedSquareId);
234
+ const fromPiece = fromSquare?.dataset.piece;
235
+ const toRank = clickedSquareId.charAt(1);
236
+ let finalMoveUci = moveUci;
237
+
238
+ if (fromPiece === 'P' && toRank === '8') {
239
+ finalMoveUci += 'q'; // Promotion en Reine pour les blancs
240
+ } else if (fromPiece === 'p' && toRank === '1') {
241
+ finalMoveUci += 'q'; // Promotion en Reine pour les noirs
242
+ }
243
+
244
+ selectedSquareId = null; // Réinitialiser la sélection après la tentative de coup
245
+
246
+ statusMessageElement.textContent = 'Traitement du coup...';
247
+ try {
248
+ const response = await fetch('/move', {
249
+ method: 'POST',
250
+ headers: { 'Content-Type': 'application/json' },
251
+ body: JSON.stringify({ move: finalMoveUci })
252
+ });
253
+ const data = await response.json();
254
+
255
+ if (data.error) {
256
+ statusMessageElement.textContent = `Erreur: ${data.error}`;
257
+ // Re-render old fen and turn if move was illegal
258
+ if(data.fen) renderBoard(data.fen, data.last_move_human, data.last_move_ai); // S'assurer que le dernier coup est passé
259
+ if(data.turn) currentPlayerTurn = data.turn;
260
+ updateTurnIndicator();
261
+ } else {
262
+ renderBoard(data.fen, data.last_move_human, data.last_move_ai);
263
+ currentPlayerTurn = data.turn; // Le backend doit retourner le tour actuel
264
+ statusMessageElement.textContent = data.game_status || (data.last_move_ai ? `L'IA a joué: ${data.last_move_ai}` : "Coup joué.");
265
+ updateHistoryLog(data.history);
266
+ if (data.evaluation) { // Afficher l'évaluation si fournie
267
+ evaluationIndicatorElement.textContent = `Éval: ${data.evaluation.type} ${data.evaluation.value}`;
268
+ } else {
269
+ evaluationIndicatorElement.textContent = '';
270
+ }
271
+
272
+ if (data.game_status && (data.game_status.includes("Mat!") || data.game_status.includes("Pat!"))) {
273
+ // La partie est terminée, on pourrait désactiver les clics ou afficher un message plus grand
274
+ turnIndicatorElement.textContent = "Partie terminée";
275
+ } else if (gameMode === 'ai' && currentPlayerTurn !== 'w') { // Si c'est le tour de l'IA (assumant que l'IA joue noir pour l'instant)
276
+ // Le backend gère déjà le coup de l'IA dans la même réponse /move
277
+ }
278
+ }
279
+ } catch (error) {
280
+ console.error('Erreur lors de l\'envoi du coup:', error);
281
+ statusMessageElement.textContent = 'Erreur de communication avec le serveur.';
282
+ }
283
+ }
284
+ }
285
+
286
+ async function startNewGame(mode) {
287
+ gameMode = mode;
288
+ selectedSquareId = null;
289
+ statusMessageElement.textContent = `Démarrage d'une nouvelle partie (${mode === 'ai' ? 'vs IA' : 'Humain vs Humain'})...`;
290
+ evaluationIndicatorElement.textContent = '';
291
+ historyLogElement.innerHTML = "Aucun coup joué.";
292
+ lastMoveSquares = []; // Réinitialiser le surlignage du dernier coup
293
+
294
+ try {
295
+ const response = await fetch('/new_game', {
296
+ method: 'POST',
297
+ headers: { 'Content-Type': 'application/json' },
298
+ body: JSON.stringify({ mode: gameMode })
299
+ });
300
+ const data = await response.json();
301
+
302
+ if (data.error) {
303
+ statusMessageElement.textContent = `Erreur: ${data.error}`;
304
+ } else {
305
+ renderBoard(data.fen);
306
+ currentPlayerTurn = data.turn;
307
+ statusMessageElement.textContent = data.message || 'Partie commencée. Aux Blancs de jouer.';
308
+ updateTurnIndicator();
309
+ updateHistoryLog([]); // Réinitialise l'historique
310
+ }
311
+ } catch (error) {
312
+ console.error('Erreur lors du démarrage de la nouvelle partie:', error);
313
+ statusMessageElement.textContent = 'Erreur de communication avec le serveur.';
314
+ }
315
+ }
316
+
317
+ newGameAiButton.addEventListener('click', () => startNewGame('ai'));
318
+ newGameHumanButton.addEventListener('click', () => startNewGame('human'));
319
+
320
+ // Initialisation de la première partie au chargement de la page
321
+ document.addEventListener('DOMContentLoaded', () => {
322
+ startNewGame('human'); // Ou 'ai' si vous préférez
323
+ });
324
+
325
+ </script>
326
  </body>
327
+ </html>