(null);
+
+ useEffect(() => {
+ const ctx = boardRef.current?.getContext("2d");
+ if (!ctx) return;
+
+ drawBoard(ctx);
+
+ const evalCtx = evalBarRef.current?.getContext("2d");
+ if (!evalCtx) return;
+ drawEvaluationBar(evalCtx);
+ }, []);
+
+ return (
+
+
+
+
+
+ Black Player (?)
+
+
+
+
+
+ White Player (?)
+
+
+
+ );
+}
diff --git a/src/sections/index/reviewPanelBody.tsx b/src/sections/index/reviewPanelBody.tsx
new file mode 100644
index 0000000..51c7249
--- /dev/null
+++ b/src/sections/index/reviewPanelBody.tsx
@@ -0,0 +1,30 @@
+import ReviewResult from "./reviewResult";
+import SelectDepth from "./selectDepth";
+import SelectGameOrigin from "./selectGame/selectGameOrigin";
+
+export default function ReviewPanelBody() {
+ return (
+
+
+ 📑 Game Report
+
+
+
+
+
+
+
+
+ {false &&
}
+
+
+
+
+
+ {false &&
}
+
+ );
+}
diff --git a/src/sections/index/reviewPanelToolbar.tsx b/src/sections/index/reviewPanelToolbar.tsx
new file mode 100644
index 0000000..2b8454f
--- /dev/null
+++ b/src/sections/index/reviewPanelToolbar.tsx
@@ -0,0 +1,43 @@
+export default function ReviewPanelToolBar() {
+ return (
+
+ );
+}
diff --git a/src/sections/index/reviewResult.tsx b/src/sections/index/reviewResult.tsx
new file mode 100644
index 0000000..5f23f8d
--- /dev/null
+++ b/src/sections/index/reviewResult.tsx
@@ -0,0 +1,28 @@
+export default function ReviewResult() {
+ return (
+
+
+ Accuracies
+
+
+ 0%
+ 0%
+
+
+
+

+
+
+
+
Qxf2+ was best
+
+
+
+ Engine
+
+
+
+
+
+ );
+}
diff --git a/src/sections/index/selectDepth.tsx b/src/sections/index/selectDepth.tsx
new file mode 100644
index 0000000..b87e445
--- /dev/null
+++ b/src/sections/index/selectDepth.tsx
@@ -0,0 +1,22 @@
+export default function SelectDepth() {
+ return (
+ <>
+ ⚙️ Search depth
+
+
+
+ 16 🐇
+
+
+
+ Lower depths recommended for slower devices.
+
+ >
+ );
+}
diff --git a/src/sections/index/selectGame/inputGame.tsx b/src/sections/index/selectGame/inputGame.tsx
new file mode 100644
index 0000000..d295f70
--- /dev/null
+++ b/src/sections/index/selectGame/inputGame.tsx
@@ -0,0 +1,16 @@
+interface Props {
+ placeholder?: string;
+}
+
+export default function InputGame({ placeholder }: Props) {
+ return (
+
+ );
+}
diff --git a/src/sections/index/selectGame/selectGameAccount.tsx b/src/sections/index/selectGame/selectGameAccount.tsx
new file mode 100644
index 0000000..9be1874
--- /dev/null
+++ b/src/sections/index/selectGame/selectGameAccount.tsx
@@ -0,0 +1,22 @@
+import { GameOrigin } from "@/types";
+
+interface Props {
+ gameOrigin: GameOrigin;
+}
+
+export default function SelectGameAccount({}: Props) {
+ return (
+
+
+
+
+ );
+}
diff --git a/src/sections/index/selectGame/selectGameOrigin.tsx b/src/sections/index/selectGame/selectGameOrigin.tsx
new file mode 100644
index 0000000..110418b
--- /dev/null
+++ b/src/sections/index/selectGame/selectGameOrigin.tsx
@@ -0,0 +1,46 @@
+import { useState } from "react";
+import InputGame from "./inputGame";
+import SelectGameAccount from "./selectGameAccount";
+import { GameOrigin } from "@/types";
+
+export default function SelectGameOrigin() {
+ const [gameOrigin, setGameOrigin] = useState(GameOrigin.Pgn);
+ return (
+ <>
+
+ Load game from
+
+
+
+ {renderSelectGameInfo(gameOrigin)}
+ >
+ );
+}
+
+const gameOriginLabel: Record = {
+ [GameOrigin.Pgn]: "PGN",
+ [GameOrigin.ChessCom]: "Chess.com",
+ [GameOrigin.Lichess]: "Lichess",
+ [GameOrigin.Json]: "JSON",
+};
+
+const renderSelectGameInfo = (gameOrigin: GameOrigin) => {
+ switch (gameOrigin) {
+ case GameOrigin.Pgn:
+ return ;
+ case GameOrigin.Json:
+ return ;
+ default:
+ return ;
+ }
+};
diff --git a/src/sections/index/topBar.tsx b/src/sections/index/topBar.tsx
new file mode 100644
index 0000000..adc4e24
--- /dev/null
+++ b/src/sections/index/topBar.tsx
@@ -0,0 +1,7 @@
+export default function TopBar() {
+ return (
+
+ Welcome ❤️
+
+ );
+}
diff --git a/src/types/index.tsx b/src/types/index.tsx
new file mode 100644
index 0000000..ff5b676
--- /dev/null
+++ b/src/types/index.tsx
@@ -0,0 +1,6 @@
+export enum GameOrigin {
+ Pgn = "pgn",
+ ChessCom = "chesscom",
+ Lichess = "lichess",
+ Json = "json",
+}
diff --git a/styles/global.css b/styles/global.css
new file mode 100644
index 0000000..9cd7a9a
--- /dev/null
+++ b/styles/global.css
@@ -0,0 +1,44 @@
+:root {
+ --border-color: rgb(83, 83, 83);
+
+ --primary-color: rgb(20, 20, 20);
+ --secondary-color: rgb(26, 26, 26);
+
+ --success-color: rgb(101, 255, 84);
+ --success-color-accent: rgb(91, 229, 75);
+}
+
+body {
+ margin: 0;
+ background-color: var(--primary-color);
+ overflow-x: hidden;
+}
+
+.center {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+* {
+ font-family: system-ui;
+}
+
+.std-btn {
+ border: none;
+ border-radius: 5px;
+
+ cursor: pointer;
+
+ transition: box-shadow 0.3s ease, transform 0.3s ease;
+}
+
+.success-btn {
+ background-color: var(--success-color);
+ box-shadow: 0px 4px var(--success-color-accent);
+}
+
+.success-btn:active {
+ box-shadow: 0px 2px var(--success-color-accent);
+ transform: translateY(2px);
+}
diff --git a/styles/index.css b/styles/index.css
new file mode 100644
index 0000000..1963de1
--- /dev/null
+++ b/styles/index.css
@@ -0,0 +1,510 @@
+.white {
+ color: white;
+}
+
+@media (min-width: 1000px) and (min-height: 860px) {
+ #review-container {
+ display: grid;
+
+ grid-template-columns: 68vw 28vw;
+ grid-template-rows: 90vh;
+ }
+
+ #review-panel {
+ margin: 20px;
+ }
+}
+
+@media (max-width: 999px), (max-height: 860px) {
+ #review-container {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+
+ #review-panel {
+ display: flex;
+ flex-direction: column;
+ }
+
+ #secondary-message {
+ width: 60%;
+ }
+
+ #review-panel-toolbar {
+ padding: 10px;
+ }
+}
+
+/*
+ REPORT CARDS CONTAINER
+*/
+#report-cards {
+ flex-direction: column;
+ align-items: center;
+
+ width: 100%;
+}
+
+/*
+ ACCURACY PERCENTAGES
+*/
+#accuracies-title {
+ margin-bottom: 5px;
+}
+
+#accuracies {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+}
+
+#accuracies b {
+ padding: 8px;
+ border: 2px solid var(--border-color);
+}
+
+#white-accuracy {
+ background-color: white;
+}
+
+#black-accuracy {
+ background-color: var(--primary-color);
+ color: white;
+}
+
+/*
+ CLASSIFICATION MESSAGE
+ e.g "Nd4 is a great move"
+*/
+#classification-message-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+
+ margin: 5px;
+}
+
+#classification-message {
+ font-size: 18px;
+}
+
+#top-alternative-message {
+ color: #98bc49;
+}
+
+/*
+ ENGINE SUGGESTIONS
+*/
+#engine-suggestions {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+#engine-suggestions-title {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+
+.engine-suggestion {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+
+ margin: 5px;
+
+ font-size: 18px;
+}
+
+.engine-suggestion b {
+ padding: 0 3px;
+ text-align: center;
+ border-radius: 5px;
+}
+
+/*
+ OPENING NAME
+*/
+#opening-name {
+ text-align: center;
+}
+
+.profile {
+ color: white;
+ font-size: 20px;
+}
+
+#board-outer-container {
+ gap: 30px;
+}
+
+#board-inner-container {
+ flex-direction: column;
+}
+
+#evaluation-bar {
+ background-color: white;
+ border: 2px solid var(--border-color);
+}
+
+#board {
+ grid-column: 1 / 2;
+
+ max-width: 100%;
+
+ border: 2px solid var(--border-color);
+ border-radius: 10px;
+}
+
+/*
+ DIALOG CONTAINER (SPANS ENTIRE SCREEN)
+*/
+#game-select-menu-container {
+ display: none;
+ justify-content: center;
+ align-items: center;
+ position: fixed;
+ top: 0;
+
+ width: 100vw;
+ height: 100vh;
+
+ background-color: rgba(0, 0, 0, 0.7);
+}
+
+/*
+ MODAL
+*/
+#game-select-menu {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 15px;
+
+ min-width: 400px;
+ width: 50vw;
+
+ border-radius: 10px;
+ background-color: var(--primary-color);
+ color: white;
+}
+
+/*
+ MODAL TITLE
+*/
+#game-select-menu h1 {
+ margin: 0 0 10px 0;
+}
+
+/*
+ GAMES LIST TIME PERIOD
+*/
+#game-select-period {
+ margin-bottom: 10px;
+ font-size: 24px;
+}
+
+/*
+ GAMES LIST CONTAINER
+*/
+#games-list {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 5px 20px;
+
+ width: 90%;
+ max-height: 420px;
+ overflow-x: hidden;
+ overflow-y: auto;
+
+ background-color: #232323;
+}
+
+#games-list::-webkit-scrollbar {
+ background-color: #0e0e0e;
+}
+
+#games-list::-webkit-scrollbar-thumb {
+ background-color: whitesmoke;
+}
+
+/*
+ GAME LISTING
+*/
+.game-listing {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 20px;
+ margin: 5px;
+ padding: 5px;
+
+ width: 100%;
+
+ background-color: var(--secondary-color);
+ cursor: pointer;
+}
+
+/*
+ PAGINATION BUTTONS
+*/
+#game-select-page-buttons button {
+ margin: 10px 5px 0 5px;
+}
+
+/*
+ CANCEL SELECTION BUTTON
+*/
+#game-select-cancel-button {
+ margin-top: 10px;
+
+ background-color: whitesmoke;
+ box-shadow: 0px 4px 0px #c7c7c7;
+
+ font-size: 18px;
+
+ transition: box-shadow 0.3s ease, transform 0.3s ease;
+}
+
+#game-select-cancel-button:active {
+ box-shadow: 0px 2px 0px #c7c7c7;
+ transform: translateY(2px);
+}
+
+/*
+ ENTIRE REVIEW PANEL
+*/
+#review-panel {
+ display: grid;
+
+ grid-template-columns: 100%;
+
+ background-color: var(--secondary-color);
+
+ border: 2px solid var(--border-color);
+}
+
+@media (min-width: 1264px) {
+ #review-panel {
+ grid-template-rows: 88% 12%;
+ }
+}
+
+@media (max-width: 1264px) and (min-width: 1000px) {
+ #review-panel {
+ grid-template-rows: 82% 18%;
+ }
+}
+
+@media (max-width: 999px) {
+ #review-panel {
+ grid-template-rows: 82% 18%;
+ }
+}
+
+@media (max-height: 860px) {
+ #review-panel {
+ grid-template-rows: 82% 18%;
+ }
+}
+
+/*
+ REVIEW PANEL NON-TOOLBAR SECTION
+*/
+#review-panel-main {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ height: 100%;
+
+ padding: 10px;
+}
+
+@media (max-width: 1265px), (max-height: 860px) {
+ #review-panel-main {
+ padding-bottom: 30px;
+ }
+}
+
+/*
+ REVIEW PANEL HEADING
+*/
+#review-panel-title {
+ margin: 0px 0px 10px 0px;
+ text-align: center;
+}
+
+@media (max-width: 1126px) {
+ #review-panel-title {
+ font-size: 27px;
+ }
+}
+
+/*
+ GAME LOAD TYPE DROPDOWN
+*/
+#load-type-dropdown-container {
+ margin-bottom: 5px;
+}
+
+#load-type-dropdown {
+ text-align: center;
+ border-radius: 5px;
+ background-color: var(--border-color);
+ color: white;
+}
+
+/*
+ PGN INPUT TEXTAREA
+*/
+#pgn {
+ width: 90%;
+ height: 130px;
+ resize: none;
+
+ border-radius: 10px;
+ background-color: var(--border-color);
+}
+
+/*
+ CHESS.COM / LICHES USERNAME INPUT TEXTAREA
+*/
+#chess-site-username-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 5px;
+ width: 90%;
+}
+
+#chess-site-username {
+ width: 100%;
+ height: 30px;
+
+ border-radius: 10px;
+ background-color: var(--border-color);
+
+ resize: none;
+ font-size: 16px;
+ overflow: hidden;
+}
+
+#fetch-account-games-button {
+ transform: translateY(-2px);
+}
+
+#fetch-account-games-button:active {
+ transform: translateY(0px);
+}
+
+/*
+ ANALYSE GREEN BUTTON
+*/
+#review-button {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ margin: 10px;
+ padding: 10px 50px;
+
+ font-size: 20px;
+
+ text-shadow: 0px 1px 2px black;
+}
+
+#review-button img {
+ filter: drop-shadow(0px 1px 1px black);
+}
+
+/*
+ SEARCH DEPTH SLIDER
+*/
+#depth-slider-container {
+ display: flex;
+ justify-content: center;
+ width: 80%;
+}
+
+#depth-slider {
+ width: 70%;
+}
+
+#depth-message {
+ margin: 0px;
+ text-align: center;
+}
+
+/*
+ ANALYSIS PROGRESS BAR
+*/
+#evaluation-progress-bar {
+ margin-top: 10px;
+ width: 80%;
+}
+
+/*
+ ANALYSIS STATUS MESSAGE (INFO/ERROR)
+*/
+#status-message {
+ text-align: center;
+ color: rgb(255, 53, 53);
+}
+
+/*
+ ANALYSIS SECONDARY MESSAGE
+*/
+#secondary-message {
+ text-align: center;
+ font-size: 14px;
+}
+
+/*
+ REVIEW PANEL BOTTOM TOOLBAR
+*/
+#review-panel-toolbar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+#review-panel-toolbar {
+ grid-row: 2 / 3;
+
+ border-top: 2px solid var(--border-color);
+}
+
+#review-panel-toolbar-buttons {
+ margin: 10px;
+ gap: 10px;
+ flex-wrap: wrap;
+ transition: gap 0.5s ease;
+}
+
+#review-panel-toolbar-buttons img {
+ cursor: pointer;
+ transition: transform 0.5s ease;
+}
+
+#review-panel-toolbar-buttons img:hover {
+ transform: translateY(-3px);
+}
+
+#announcement {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+
+ margin-bottom: 8px;
+ padding: 3px;
+
+ width: 100%;
+ min-height: 2.5rem;
+ background-color: rgb(104, 139, 255);
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9a5614d
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js"],
+ "exclude": ["node_modules"]
+}