Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ dist-ssr
*.njsproj
*.sln
*.sw?

# Playwright reports
playwright-report
test-results


# 3D piece images
public/3d-pieces-png/*.png
2 changes: 1 addition & 1 deletion TODO.MD
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Liste d'améliorations inspirées de Chess.com pour rendre l'application plus co

## Expérience utilisateur
- **Notifications** *(implémenté)* : alertes pour les défis reçus, les débuts de tournois ou l'arrivée de nouveaux puzzles.
- **Personnalisation** : thèmes d'échiquier supplémentaires et options pour les pièces, inspirés de ceux de Chess.com.
- **Personnalisation** *(implémenté)* : thèmes d'échiquier supplémentaires et options pour les pièces, inspirés de ceux de Chess.com.
- **Progression et succès** : badges et objectifs à débloquer en fonction des activités (puzzles résolus, parties jouées, etc.).

## Autres idées
Expand Down
21 changes: 21 additions & 0 deletions public/3d-pieces-png/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 3D Piece Set Installation

Les images des pièces 3D ne sont pas incluses dans le dépôt pour alléger le projet. Téléchargez-les depuis le dépôt open source de Lichess ou une autre source libre, puis placez-les dans ce dossier.

## Téléchargement

Vous pouvez par exemple récupérer le set "Staunty" de Lichess :
https://github.com/lichess-org/lila/tree/master/public/piece/staunty/png/150

Téléchargez les fichiers suivants :

```
bB.png bK.png bN.png bP.png bQ.png bR.png
wB.png wK.png wN.png wP.png wQ.png wR.png
```

## Installation

1. Créez le dossier `public/3d-pieces-png` s'il n'existe pas.
2. Copiez les 12 fichiers PNG téléchargés dans ce dossier.
3. Relancez l'application puis choisissez « 3D » dans le sélecteur de sets de pièces.
2 changes: 2 additions & 0 deletions src/components/ChessMasterDatabase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LazyChessOpenings from './LazyChessOpenings';
import DragDropUpload from './DragDropUpload';
import ThemeSelector from './ThemeSelector';
import BoardThemeSelector from './BoardThemeSelector';
import PieceSetSelector from './PieceSetSelector';
import AccessibleModal from './AccessibleModal';
import { toast } from 'react-toastify';
import { ClipboardCheck as ChessBoard, Search, BookOpen, Clock, Download, Upload, BookOpen as OpeningIcon, HelpCircle, BarChart3, GitCompare, Users as PlayersIcon, Zap, Puzzle, Timer, Calendar, Swords, Trophy } from 'lucide-react';
Expand Down Expand Up @@ -137,6 +138,7 @@ const ChessMasterDatabase = () => {
<div className="flex gap-4">
<ThemeSelector />
<BoardThemeSelector />
<PieceSetSelector />
<button
onClick={() => setTutorialOpen(true)}
className="px-4 py-2 rounded-md flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"
Expand Down
20 changes: 19 additions & 1 deletion src/components/ChessboardDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const ChessboardDisplay: React.FC<ChessboardDisplayProps> = ({
const [squares, setSquares] = useState<CustomSquare>({});
const theme = useChessStore(state => state.theme);
const boardTheme = useChessStore(state => state.boardTheme);
const pieceSet = useChessStore(state => state.pieceSet);

// Update arrows and squares when custom props change
useEffect(() => {
Expand Down Expand Up @@ -115,6 +116,22 @@ const ChessboardDisplay: React.FC<ChessboardDisplayProps> = ({
}
}, [boardTheme]);

const customPieces = useMemo(() => {
if (pieceSet !== '3d') return undefined;
const pieces: Record<string, (props: { squareWidth: number }) => JSX.Element> = {};
const codes = ['bB','bK','bN','bP','bQ','bR','wB','wK','wN','wP','wQ','wR'];
codes.forEach(code => {
pieces[code] = ({ squareWidth }) => (
<img
src={`/3d-pieces-png/${code}.png`}
style={{ width: squareWidth, height: squareWidth }}
alt={code}
/>
);
});
return pieces;
}, [pieceSet]);

// Memoize board configuration to prevent re-renders
const containerRef = useRef<HTMLDivElement>(null);
const [boardWidth, setBoardWidth] = useState(480);
Expand Down Expand Up @@ -150,13 +167,14 @@ const ChessboardDisplay: React.FC<ChessboardDisplayProps> = ({
customLightSquareStyle: { backgroundColor: boardColors.light },
boardOrientation,
animationDuration: 150,
customPieces: customPieces,
customArrows: [...(customArrows || []), ...arrows],
customSquareStyles: {
...(customSquares || {}),
...squares,
...kingStyles
}
}), [fen, onPieceDrop, boardWidth, boardOrientation, customArrows, customSquares, arrows, squares, isCheckmate, isDraw, isCheck, kingStyles, darkMode, boardColors]);
}), [fen, onPieceDrop, boardWidth, boardOrientation, customArrows, customSquares, arrows, squares, isCheckmate, isDraw, isCheck, kingStyles, darkMode, boardColors, customPieces]);

// Update arrows and square highlights when FEN changes
useEffect(() => {
Expand Down
21 changes: 21 additions & 0 deletions src/components/PieceSetSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { useChessStore, type PieceSet } from '../store/useChessStore';

const PieceSetSelector: React.FC = () => {
const pieceSet = useChessStore(state => state.pieceSet);
const setPieceSet = useChessStore(state => state.setPieceSet);

return (
<select
aria-label="Set de pièces"
value={pieceSet}
onChange={e => setPieceSet(e.target.value as PieceSet)}
className="p-2 rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200"
>
<option value="default">Classique</option>
<option value="3d">3D</option>
</select>
);
};

export default PieceSetSelector;
19 changes: 17 additions & 2 deletions src/screens/Tournaments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ const Tournaments: React.FC = () => {
setAllPlayers(playersDatabase.getAllPlayers());
const data = localStorage.getItem('chess-tournaments');
if (data) {
try { setTournaments(JSON.parse(data)); } catch {}
try {
setTournaments(JSON.parse(data));
} catch {
// ignore invalid JSON
}
}
}, []);

Expand Down Expand Up @@ -112,7 +116,18 @@ const Tournaments: React.FC = () => {
<td>{allPlayers.find(pl => pl.id === p.white)?.fullName || p.white}</td>
<td>{allPlayers.find(pl => pl.id === p.black)?.fullName || p.black}</td>
<td>
<select value={p.result} onChange={e => updateResult(t.id, ri, pi, e.target.value as any)} className="border px-1 py-0.5 rounded">
<select
value={p.result}
onChange={e =>
updateResult(
t.id,
ri,
pi,
e.target.value as Tournament['rounds'][0]['pairings'][0]['result']
)
}
className="border px-1 py-0.5 rounded"
>
<option value="">--</option>
<option value="1-0">1-0</option>
<option value="0-1">0-1</option>
Expand Down
18 changes: 18 additions & 0 deletions src/store/useChessStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { CustomArrow, CustomSquare } from 'react-chessboard/dist/chessboard

export type Theme = 'light' | 'dark' | 'system';
export type BoardTheme = 'classic' | 'blue' | 'green' | 'wood';
export type PieceSet = 'default' | '3d';

export interface Notification {
id: string;
Expand All @@ -24,6 +25,7 @@ interface ChessState {
filter: string;
theme: Theme;
boardTheme: BoardTheme;
pieceSet: PieceSet;
notifications: Notification[];

// Game State
Expand Down Expand Up @@ -51,6 +53,7 @@ interface ChessState {
setFilter: (filter: string) => void;
setTheme: (theme: Theme) => void;
setBoardTheme: (theme: BoardTheme) => void;
setPieceSet: (set: PieceSet) => void;
setCurrentMove: (move: number) => void;
setMoveHistory: (history: string[]) => void;
setProcessedMoves: (moves: string[]) => void;
Expand All @@ -75,6 +78,7 @@ export const useChessStore = create<ChessState>()(
filter: '',
theme: 'system' as Theme,
boardTheme: 'classic' as BoardTheme,
pieceSet: 'default' as PieceSet,
notifications: [],
currentMove: 0,
moveHistory: [],
Expand Down Expand Up @@ -128,6 +132,15 @@ export const useChessStore = create<ChessState>()(
// ignore storage errors
}
},

setPieceSet: (pieceSet) => {
set({ pieceSet });
try {
localStorage.setItem('pieceSet', pieceSet);
} catch {
// ignore storage errors
}
},

setCurrentMove: (currentMove) => set({ currentMove }),
setMoveHistory: (moveHistory) => set({ moveHistory }),
Expand Down Expand Up @@ -183,4 +196,9 @@ if (savedTheme) {
const savedBoardTheme = localStorage.getItem('boardTheme') as BoardTheme | null;
if (savedBoardTheme) {
useChessStore.getState().setBoardTheme(savedBoardTheme);
}

const savedPieceSet = localStorage.getItem('pieceSet') as PieceSet | null;
if (savedPieceSet) {
useChessStore.getState().setPieceSet(savedPieceSet);
}