Update chess/public/js/app.js

This commit is contained in:
2026-08-22 18:08:35 +00:00
parent 226ad23f7b
commit 8dcc964d04
+109 -57
View File
@@ -98,6 +98,10 @@ const elements = {
partySummary: document.querySelector("#partySummary"),
copyInviteButton: document.querySelector("#copyInviteButton"),
// ДОБАВЛЕНЫ КНОПКИ "НОВАЯ ИГРА"
newGameToolbarButton: document.querySelector("#newGameToolbarButton"),
newPartyGameButton: document.querySelector("#newPartyGameButton"),
flipBoardButton: document.querySelector("#flipBoardButton"),
@@ -520,22 +524,22 @@ function formatStatus() {
const status = currentStatus();
if (status.phase === "checkmate") {
return "Checkmate";
return "Мат!";
}
if (status.phase === "draw") {
return "Draw";
return "Ничья";
}
if (status.check) {
return appState.engine.state.turn === "w"
? "White is in check"
: "Black is in check";
? "Шах белым"
: "Шах черным";
}
return appState.engine.state.turn === "w"
? "White to move"
: "Black to move";
? "Ход белых"
: "Ход черных";
}
@@ -546,7 +550,7 @@ function getGameOverMessage() {
if (!snapshot) {
return (
appState.gameOverMessage ||
"Game over."
"Игра окончена."
);
}
@@ -559,21 +563,21 @@ function getGameOverMessage() {
switch (reason) {
case "timeout":
return winner === "w"
? "White wins on time."
: "Black wins on time.";
? "Белые выиграли по времени."
: "Черные выиграли по времени.";
case "checkmate":
return winner === "w"
? "White wins by checkmate."
: "Black wins by checkmate.";
? "Белые выиграли матом."
: "Черные выиграли матом.";
case "draw":
return "Draw.";
return "Ничья.";
default:
return (
appState.gameOverMessage ||
"Game over."
"Игра окончена."
);
}
}
@@ -586,17 +590,17 @@ function getGameOverMessage() {
function renderRoster() {
if (appState.mode === "ai") {
const human =
appState.playerName || "You";
appState.playerName || "Вы";
const whiteName =
appState.playerColor === "w"
? human
: `Computer (${appState.aiLevel})`;
: `Компьютер (${appState.aiLevel})`;
const blackName =
appState.playerColor === "b"
? human
: `Computer (${appState.aiLevel})`;
: `Компьютер (${appState.aiLevel})`;
if (elements.whitePlayerLabel) {
elements.whitePlayerLabel.textContent =
@@ -620,7 +624,7 @@ function renderRoster() {
if (elements.spectatorList) {
elements.spectatorList.innerHTML =
"<li>None in AI mode</li>";
"<li class='empty-state'>В режиме ИИ зрителей нет</li>";
}
if (elements.spectatorCount) {
@@ -644,7 +648,7 @@ function renderRoster() {
? ""
: " · offline"
}`
: "Open seat";
: "Свободно";
const blackName =
black
@@ -653,7 +657,7 @@ function renderRoster() {
? ""
: " · offline"
}`
: "Open seat";
: "Свободно";
elements.whitePlayerLabel &&
(elements.whitePlayerLabel.textContent =
@@ -685,7 +689,7 @@ function renderRoster() {
if (!spectators.length) {
elements.spectatorList.innerHTML =
"<li>None yet</li>";
"<li class='empty-state'>Пока никого нет</li>";
return;
}
@@ -717,7 +721,7 @@ function renderPartySummary() {
if (!appState.party) {
elements.partySummary.textContent =
"Create a room to become White. The next player joins as Black. Up to 20 spectators can watch live.";
"Создайте комнату, чтобы играть белыми. Следующий игрок присоединится за черных.";
return;
}
@@ -727,18 +731,18 @@ function renderPartySummary() {
const roleText =
role === "white"
? "playing as White"
? "Играете за Белых"
: role === "black"
? "playing as Black"
: "watching as a spectator";
? "Играете за Черных"
: "Зритель";
const timeLabel =
appState.partySnapshot
?.timeControl?.label ||
"Без часов";
"Без лимита";
elements.partySummary.textContent =
`Party ${appState.party.code} · ${roleText}. ${timeLabel}.`;
`Комната ${appState.party.code} · ${roleText}. ${timeLabel}.`;
}
@@ -770,7 +774,7 @@ function renderClocks() {
elements.aiTimeControl
?.selectedOptions?.[0]
?.textContent ||
"Без часов",
"Без лимита",
};
}
@@ -784,7 +788,7 @@ function renderClocks() {
elements.whiteClock.textContent = "∞";
elements.whiteClock.classList.remove(
"active",
"low"
"danger"
);
}
@@ -792,7 +796,7 @@ function renderClocks() {
elements.blackClock.textContent = "∞";
elements.blackClock.classList.remove(
"active",
"low"
"danger"
);
}
@@ -816,8 +820,8 @@ function renderClocks() {
);
elements.whiteClock.classList.toggle(
"low",
white <= 10
"danger",
white <= 10 && white > 0
);
}
@@ -832,8 +836,8 @@ function renderClocks() {
);
elements.blackClock.classList.toggle(
"low",
black <= 10
"danger",
black <= 10 && black > 0
);
}
}
@@ -848,31 +852,37 @@ function renderConnectionBadge() {
return;
}
// Сбросим классы перед установкой
elements.connectionBadge.className = "status-chip";
if (appState.mode === "ai") {
elements.connectionBadge.classList.add("connection-solo");
elements.connectionBadge.textContent =
appState.isAiThinking
? "Computer thinking"
: `AI: ${appState.aiLevel}`;
? "Компьютер думает..."
: `Соло (${appState.aiLevel})`;
return;
}
if (!appState.party) {
elements.connectionBadge.classList.add("connection-solo");
elements.connectionBadge.textContent =
"Party idle";
"Вне комнаты";
return;
}
elements.connectionBadge.classList.add("connection-party");
const role =
appState.party.role === "white"
? "White"
? "Белые"
: appState.party.role === "black"
? "Black"
: "Spectator";
? "Черные"
: "Зритель";
elements.connectionBadge.textContent =
`Party ${appState.party.code} · ${role}`;
`Комната ${appState.party.code} · ${role}`;
}
@@ -891,7 +901,13 @@ function render() {
if (elements.turnBadge) {
elements.turnBadge.textContent =
status;
appState.engine.state.turn === "w" ? "Ход белых" : "Ход черных";
// Меняем класс цвета бейджа
elements.turnBadge.className =
appState.engine.state.turn === "w"
? "status-chip turn-white"
: "status-chip turn-black";
}
if (elements.statusMessage) {
@@ -908,6 +924,12 @@ function render() {
"hidden",
!appState.party
);
// Показываем кнопку "Новая игра" в мультиплеере только если мы в комнате (и опционально, если игра окончена)
elements.newPartyGameButton?.classList.toggle(
"hidden",
!appState.party
);
}
@@ -1013,7 +1035,7 @@ async function submitMove(move) {
showToast(
response.error ||
"Move rejected.",
"Ход отклонен.",
"error"
);
@@ -1039,7 +1061,7 @@ async function submitMove(move) {
if (!result.ok) {
showToast(
result.error ||
"Illegal move.",
"Недопустимый ход.",
"error"
);
@@ -1304,8 +1326,8 @@ function updateAiClock() {
appState.gameOverMessage =
color === "w"
? "Black wins on time."
: "White wins on time.";
? "Черные выиграли по времени."
: "Белые выиграли по времени.";
stopAiClock();
@@ -1377,6 +1399,32 @@ function resetLocalGame() {
startAiClock();
scheduleAiTurn();
render();
}
/* =========================================================
NEW GAME HANDLER (Добавлено)
========================================================= */
async function startNewGame() {
if (appState.mode === "ai") {
resetLocalGame();
showToast("Новая игра с ИИ начата!", "success");
} else if (appState.mode === "party" && appState.party) {
// Если вы играете с другом, мы отправляем запрос на сервер,
// чтобы он сбросил состояние комнаты. (Предполагается, что такой API существует)
const response = await postJson("/api/party/restart", {
partyCode: appState.party.code,
clientId: appState.party.clientId
});
if (!response.ok) {
showToast(response.error || "Не удалось перезапустить игру на сервере.", "error");
return;
}
showToast("Новая игра в комнате началась!", "success");
}
}
@@ -1581,7 +1629,7 @@ function connectPartyStream() {
elements.connectionBadge
) {
elements.connectionBadge.textContent =
`Party ${code} · reconnecting`;
`Комната ${code} · переподключение...`;
}
};
@@ -1624,7 +1672,7 @@ async function createParty() {
if (!response.ok) {
showToast(
response.error ||
"Unable to create party.",
"Не удалось создать комнату.",
"error"
);
@@ -1644,7 +1692,7 @@ async function createParty() {
connectPartyStream();
showToast(
`Party ${response.party.code} created.`,
`Комната ${response.party.code} создана.`,
"success"
);
}
@@ -1662,7 +1710,7 @@ async function joinParty(code) {
if (!normalizedCode) {
showToast(
"Enter a party code first.",
"Сначала введите код комнаты.",
"error"
);
@@ -1674,7 +1722,7 @@ async function joinParty(code) {
normalizedCode
) {
showToast(
`You are already in party ${normalizedCode}.`
`Вы уже находитесь в комнате ${normalizedCode}.`
);
return;
@@ -1706,7 +1754,7 @@ async function joinParty(code) {
if (!response.ok) {
showToast(
response.error ||
"Unable to join party.",
"Не удалось войти в комнату.",
"error"
);
@@ -1726,7 +1774,7 @@ async function joinParty(code) {
connectPartyStream();
showToast(
`Joined party ${response.party.code}.`,
`Вы присоединились к комнате ${response.party.code}.`,
"success"
);
}
@@ -1774,7 +1822,7 @@ async function leaveParty(
if (!silent) {
showToast(
"Left the party room."
"Вы покинули комнату."
);
}
}
@@ -1802,12 +1850,12 @@ async function copyInviteLink() {
);
showToast(
"Invite link copied.",
"Ссылка-инвайт скопирована!",
"success"
);
} catch {
showToast(
`Party code: ${appState.party.code}`
`Код комнаты: ${appState.party.code}`
);
}
}
@@ -1872,12 +1920,16 @@ function removeStoredPartyId(code) {
========================================================= */
function bindEvents() {
// ПРИВЯЗКА КНОПОК НОВОЙ ИГРЫ
elements.newGameToolbarButton?.addEventListener("click", startNewGame);
elements.newPartyGameButton?.addEventListener("click", startNewGame);
elements.playerNameInput?.addEventListener(
"input",
event => {
appState.playerName =
event.target.value.trim() ||
"Guest";
"Гость";
localStorage.setItem(
STORAGE_KEYS.name,
@@ -1960,7 +2012,7 @@ function bindEvents() {
resetLocalGame();
showToast(
"New AI game ready.",
"Новая игра с ИИ началась.",
"success"
);
}