From 1ceaa8185f3190184a4025ea1bfd75136a89fa08 Mon Sep 17 00:00:00 2001
From: titanium_machine <78664175+titaniummachine1@users.noreply.github.com>
Date: Sat, 14 Jun 2025 22:05:00 +0200
Subject: [PATCH 1/2] button added
---
src/sections/analysis/panelToolbar/index.tsx | 7 ++
.../analysis/panelToolbar/playButton.tsx | 109 ++++++++++++++++++
2 files changed, 116 insertions(+)
create mode 100644 src/sections/analysis/panelToolbar/playButton.tsx
diff --git a/src/sections/analysis/panelToolbar/index.tsx b/src/sections/analysis/panelToolbar/index.tsx
index 827d64ce..e57f04bf 100644
--- a/src/sections/analysis/panelToolbar/index.tsx
+++ b/src/sections/analysis/panelToolbar/index.tsx
@@ -5,6 +5,7 @@ import { boardAtom, gameAtom } from "../states";
import { useChessActions } from "@/hooks/useChessActions";
import FlipBoardButton from "./flipBoardButton";
import NextMoveButton from "./nextMoveButton";
+import PlayButton from "./playButton";
import GoToLastPositionButton from "./goToLastPositionButton";
import SaveButton from "./saveButton";
import { useEffect } from "react";
@@ -24,6 +25,10 @@ export default function PanelToolBar() {
undoBoardMove();
} else if (e.key === "ArrowDown") {
resetBoard();
+ } else if (e.key === " " || e.key === "Spacebar") {
+ // Space bar will be handled by PlayButton component
+ // We prevent default here to avoid page scrolling
+ e.preventDefault();
}
};
@@ -62,6 +67,8 @@ export default function PanelToolBar() {
+
+
diff --git a/src/sections/analysis/panelToolbar/playButton.tsx b/src/sections/analysis/panelToolbar/playButton.tsx
new file mode 100644
index 00000000..ebf8a0b3
--- /dev/null
+++ b/src/sections/analysis/panelToolbar/playButton.tsx
@@ -0,0 +1,109 @@
+import { Icon } from "@iconify/react";
+import { Grid2 as Grid, IconButton, Tooltip } from "@mui/material";
+import { useAtomValue } from "jotai";
+import { boardAtom, gameAtom } from "../states";
+import { useChessActions } from "@/hooks/useChessActions";
+import { useCallback, useEffect, useRef, useState } from "react";
+
+const PLAY_SPEED = 1000; // 1 second between moves
+
+export default function PlayButton() {
+ const { playMove: playBoardMove } = useChessActions(boardAtom);
+ const game = useAtomValue(gameAtom);
+ const board = useAtomValue(boardAtom);
+
+ const [isPlaying, setIsPlaying] = useState(false);
+ const intervalRef = useRef(null);
+
+ const gameHistory = game.history();
+ const boardHistory = board.history();
+
+ const isButtonEnabled =
+ boardHistory.length < gameHistory.length &&
+ gameHistory.slice(0, boardHistory.length).join() === boardHistory.join();
+
+ const playNextMove = useCallback(() => {
+ if (!isButtonEnabled) {
+ setIsPlaying(false);
+ return;
+ }
+
+ const nextMoveIndex = boardHistory.length;
+ const nextMove = game.history({ verbose: true })[nextMoveIndex];
+ const comment = game
+ .getComments()
+ .find((c) => c.fen === nextMove.after)?.comment;
+
+ if (nextMove) {
+ playBoardMove({
+ from: nextMove.from,
+ to: nextMove.to,
+ promotion: nextMove.promotion,
+ comment,
+ });
+ }
+ }, [isButtonEnabled, boardHistory, game, playBoardMove]);
+
+ const togglePlay = useCallback(() => {
+ if (isPlaying) {
+ setIsPlaying(false);
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = null;
+ }
+ } else {
+ setIsPlaying(true);
+ intervalRef.current = setInterval(playNextMove, PLAY_SPEED);
+ }
+ }, [isPlaying, playNextMove]);
+
+ // Cleanup interval on unmount or when playing stops
+ useEffect(() => {
+ if (!isPlaying && intervalRef.current) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = null;
+ }
+
+ return () => {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ }
+ };
+ }, [isPlaying]);
+
+ // Spacebar shortcut
+ useEffect(() => {
+ const onKeyDown = (e: KeyboardEvent) => {
+ if (e.key === " " || e.key === "Spacebar") {
+ e.preventDefault();
+ if (isButtonEnabled || isPlaying) {
+ togglePlay();
+ }
+ }
+ };
+
+ window.addEventListener("keydown", onKeyDown);
+ return () => window.removeEventListener("keydown", onKeyDown);
+ }, [togglePlay, isButtonEnabled, isPlaying]);
+
+ return (
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
From f574a8b9f1cf2b63007b89f1767c7c5129616fb9 Mon Sep 17 00:00:00 2001
From: titanium_machine <78664175+titaniummachine1@users.noreply.github.com>
Date: Sat, 14 Jun 2025 22:29:25 +0200
Subject: [PATCH 2/2] src: added working play button
---
src/lib/engine/shared.ts | 8 ++-
src/lib/engine/worker.ts | 5 ++
src/sections/analysis/panelToolbar/index.tsx | 5 +-
.../analysis/panelToolbar/playButton.tsx | 62 +++++++++++--------
4 files changed, 49 insertions(+), 31 deletions(-)
diff --git a/src/lib/engine/shared.ts b/src/lib/engine/shared.ts
index 328366a6..8b2ddc31 100644
--- a/src/lib/engine/shared.ts
+++ b/src/lib/engine/shared.ts
@@ -18,10 +18,14 @@ export const isMultiThreadSupported = () => {
}
};
-export const isIosDevice = () => /iPhone|iPad|iPod/i.test(navigator.userAgent);
+export const isIosDevice = () =>
+ typeof navigator !== "undefined" &&
+ /iPhone|iPad|iPod/i.test(navigator.userAgent);
export const isMobileDevice = () =>
- isIosDevice() || /Android|Opera Mini/i.test(navigator.userAgent);
+ isIosDevice() ||
+ (typeof navigator !== "undefined" &&
+ /Android|Opera Mini/i.test(navigator.userAgent));
export const isEngineSupported = (name: EngineName): boolean => {
switch (name) {
diff --git a/src/lib/engine/worker.ts b/src/lib/engine/worker.ts
index 224e0d9c..4b98c61a 100644
--- a/src/lib/engine/worker.ts
+++ b/src/lib/engine/worker.ts
@@ -45,6 +45,11 @@ export const sendCommandsToWorker = (
};
export const getRecommendedWorkersNb = (): number => {
+ // Return default value during SSR
+ if (typeof navigator === "undefined") {
+ return 4;
+ }
+
const maxWorkersNbFromThreads = Math.max(
1,
Math.round(navigator.hardwareConcurrency - 4),
diff --git a/src/sections/analysis/panelToolbar/index.tsx b/src/sections/analysis/panelToolbar/index.tsx
index e57f04bf..69156ac7 100644
--- a/src/sections/analysis/panelToolbar/index.tsx
+++ b/src/sections/analysis/panelToolbar/index.tsx
@@ -20,10 +20,9 @@ export default function PanelToolBar() {
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
- if (boardHistory.length === 0) return;
- if (e.key === "ArrowLeft") {
+ if (e.key === "ArrowLeft" && boardHistory.length > 0) {
undoBoardMove();
- } else if (e.key === "ArrowDown") {
+ } else if (e.key === "ArrowDown" && boardHistory.length > 0) {
resetBoard();
} else if (e.key === " " || e.key === "Spacebar") {
// Space bar will be handled by PlayButton component
diff --git a/src/sections/analysis/panelToolbar/playButton.tsx b/src/sections/analysis/panelToolbar/playButton.tsx
index ebf8a0b3..5f20c4af 100644
--- a/src/sections/analysis/panelToolbar/playButton.tsx
+++ b/src/sections/analysis/panelToolbar/playButton.tsx
@@ -11,13 +11,13 @@ export default function PlayButton() {
const { playMove: playBoardMove } = useChessActions(boardAtom);
const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom);
-
+
const [isPlaying, setIsPlaying] = useState(false);
const intervalRef = useRef(null);
-
+
const gameHistory = game.history();
const boardHistory = board.history();
-
+
const isButtonEnabled =
boardHistory.length < gameHistory.length &&
gameHistory.slice(0, boardHistory.length).join() === boardHistory.join();
@@ -30,46 +30,56 @@ export default function PlayButton() {
const nextMoveIndex = boardHistory.length;
const nextMove = game.history({ verbose: true })[nextMoveIndex];
- const comment = game
- .getComments()
- .find((c) => c.fen === nextMove.after)?.comment;
if (nextMove) {
+ const comment = game
+ .getComments()
+ .find((c) => c.fen === nextMove.after)?.comment;
+
playBoardMove({
from: nextMove.from,
to: nextMove.to,
promotion: nextMove.promotion,
comment,
});
+ } else {
+ setIsPlaying(false);
}
- }, [isButtonEnabled, boardHistory, game, playBoardMove]);
+ }, [isButtonEnabled, boardHistory.length, gameHistory, game, playBoardMove]);
const togglePlay = useCallback(() => {
if (isPlaying) {
setIsPlaying(false);
- if (intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = null;
- }
} else {
setIsPlaying(true);
- intervalRef.current = setInterval(playNextMove, PLAY_SPEED);
}
- }, [isPlaying, playNextMove]);
+ }, [isPlaying]);
- // Cleanup interval on unmount or when playing stops
+ // Handle interval management
useEffect(() => {
- if (!isPlaying && intervalRef.current) {
- clearInterval(intervalRef.current);
- intervalRef.current = null;
+ if (isPlaying) {
+ intervalRef.current = setInterval(playNextMove, PLAY_SPEED);
+ } else {
+ if (intervalRef.current) {
+ clearInterval(intervalRef.current);
+ intervalRef.current = null;
+ }
}
-
+
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
+ intervalRef.current = null;
}
};
- }, [isPlaying]);
+ }, [isPlaying, playNextMove]);
+
+ // Stop playing when no more moves available
+ useEffect(() => {
+ if (isPlaying && !isButtonEnabled) {
+ setIsPlaying(false);
+ }
+ }, [isPlaying, isButtonEnabled]);
// Spacebar shortcut
useEffect(() => {
@@ -92,18 +102,18 @@ export default function PlayButton() {
-
);
-}
\ No newline at end of file
+}