init
This commit is contained in:
268
src/lib/board.ts
Normal file
268
src/lib/board.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
const boardFlipped = false;
|
||||
const currentMoveIndex = 0;
|
||||
const reportResults: any = undefined;
|
||||
|
||||
export async function drawBoard(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
fen = startingPositionFen
|
||||
) {
|
||||
// Draw surface of board
|
||||
let colours = ["#f6dfc0", "#b88767"];
|
||||
|
||||
for (let y = 0; y < 8; y++) {
|
||||
for (let x = 0; x < 8; x++) {
|
||||
ctx.fillStyle = colours[(x + y) % 2];
|
||||
|
||||
ctx.fillRect(x * 90, y * 90, 90, 90);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw coordinates
|
||||
ctx.font = "20px Arial";
|
||||
|
||||
let files = "abcdefgh".split("");
|
||||
for (let x = 0; x < 8; x++) {
|
||||
ctx.fillStyle = colours[x % 2];
|
||||
ctx.fillText(boardFlipped ? files[7 - x] : files[x], x * 90 + 5, 715);
|
||||
}
|
||||
for (let y = 0; y < 8; y++) {
|
||||
ctx.fillStyle = colours[(y + 1) % 2];
|
||||
ctx.fillText(
|
||||
boardFlipped ? (y + 1).toString() : (8 - y).toString(),
|
||||
5,
|
||||
y * 90 + 22
|
||||
);
|
||||
}
|
||||
|
||||
// Draw last move highlight and top move arrows
|
||||
let lastMove = reportResults?.positions[currentMoveIndex];
|
||||
|
||||
let lastMoveCoordinates = {
|
||||
from: { x: 0, y: 0 },
|
||||
to: { x: 0, y: 0 },
|
||||
};
|
||||
|
||||
if (currentMoveIndex > 0 && lastMove) {
|
||||
let lastMoveUCI = lastMove.move?.uci;
|
||||
if (!lastMoveUCI) return;
|
||||
|
||||
lastMoveCoordinates.from = getBoardCoordinates(lastMoveUCI.slice(0, 2));
|
||||
lastMoveCoordinates.to = getBoardCoordinates(lastMoveUCI.slice(2, 4));
|
||||
|
||||
ctx.globalAlpha = 0.7;
|
||||
ctx.fillStyle =
|
||||
classificationColours[
|
||||
reportResults?.positions[currentMoveIndex].classification ?? "book"
|
||||
];
|
||||
ctx.fillRect(
|
||||
lastMoveCoordinates.from.x * 90,
|
||||
lastMoveCoordinates.from.y * 90,
|
||||
90,
|
||||
90
|
||||
);
|
||||
ctx.fillRect(
|
||||
lastMoveCoordinates.to.x * 90,
|
||||
lastMoveCoordinates.to.y * 90,
|
||||
90,
|
||||
90
|
||||
);
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
// Draw pieces
|
||||
let fenBoard = fen.split(" ")[0];
|
||||
let x = boardFlipped ? 7 : 0,
|
||||
y = x;
|
||||
|
||||
for (let character of fenBoard) {
|
||||
if (character == "/") {
|
||||
x = boardFlipped ? 7 : 0;
|
||||
y += boardFlipped ? -1 : 1;
|
||||
} else if (/\d/g.test(character)) {
|
||||
x += parseInt(character) * (boardFlipped ? -1 : 1);
|
||||
} else {
|
||||
const pieceSrc = pieceImagesSrc[character];
|
||||
if (!pieceSrc) throw new Error(`No image source for piece ${character}`);
|
||||
const pieceImage = await loadImage(pieceSrc);
|
||||
|
||||
ctx.drawImage(pieceImage, x * 90, y * 90, 90, 90);
|
||||
x += boardFlipped ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw last move classification
|
||||
if (currentMoveIndex > 0 && reportResults) {
|
||||
let classification =
|
||||
reportResults?.positions[currentMoveIndex]?.classification;
|
||||
|
||||
if (!classification) return;
|
||||
|
||||
const iconSrc = classificationIconsSrc[classification];
|
||||
if (!iconSrc)
|
||||
throw new Error(`No image source for classification ${classification}`);
|
||||
const classificationIcon = await loadImage(iconSrc);
|
||||
|
||||
ctx.drawImage(
|
||||
classificationIcon,
|
||||
lastMoveCoordinates.to.x * 90 + 68,
|
||||
lastMoveCoordinates.to.y * 90 - 10,
|
||||
32,
|
||||
32
|
||||
);
|
||||
}
|
||||
|
||||
// Draw engine suggestion arrows
|
||||
if (true) {
|
||||
let arrowAttributes = [
|
||||
{
|
||||
width: 20,
|
||||
opacity: 0.8,
|
||||
},
|
||||
{
|
||||
width: 12,
|
||||
opacity: 0.55,
|
||||
},
|
||||
];
|
||||
|
||||
let topLineIndex = -1;
|
||||
for (let topLine of lastMove?.topLines ?? []) {
|
||||
topLineIndex++;
|
||||
|
||||
let from = getBoardCoordinates(topLine.moveUCI.slice(0, 2));
|
||||
let to = getBoardCoordinates(topLine.moveUCI.slice(2, 4));
|
||||
|
||||
let arrow = drawArrow(
|
||||
from.x * 90 + 45,
|
||||
from.y * 90 + 45,
|
||||
to.x * 90 + 45,
|
||||
to.y * 90 + 45,
|
||||
arrowAttributes[topLineIndex].width,
|
||||
ctx
|
||||
);
|
||||
if (!arrow) continue;
|
||||
|
||||
ctx.globalAlpha = arrowAttributes[topLineIndex].opacity;
|
||||
ctx.drawImage(arrow, 0, 0);
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBoardCoordinates(square: string): { x: number; y: number } {
|
||||
if (boardFlipped) {
|
||||
return {
|
||||
x: 7 - "abcdefgh".split("").indexOf(square.slice(0, 1)),
|
||||
y: parseInt(square.slice(1)) - 1,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
x: "abcdefgh".split("").indexOf(square.slice(0, 1)),
|
||||
y: 8 - parseInt(square.slice(1)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function drawArrow(
|
||||
fromX: number,
|
||||
fromY: number,
|
||||
toX: number,
|
||||
toY: number,
|
||||
width: number,
|
||||
arrowCtx: CanvasRenderingContext2D
|
||||
) {
|
||||
if (!arrowCtx) return;
|
||||
|
||||
arrowCtx.canvas.width = 720;
|
||||
arrowCtx.canvas.height = 720;
|
||||
|
||||
let headlen = 15;
|
||||
let angle = Math.atan2(toY - fromY, toX - fromX);
|
||||
toX -= Math.cos(angle) * (width * 1.15);
|
||||
toY -= Math.sin(angle) * (width * 1.15);
|
||||
|
||||
arrowCtx.beginPath();
|
||||
arrowCtx.moveTo(fromX, fromY);
|
||||
arrowCtx.lineTo(toX, toY);
|
||||
arrowCtx.strokeStyle = classificationColours.best;
|
||||
arrowCtx.lineWidth = width;
|
||||
arrowCtx.stroke();
|
||||
|
||||
arrowCtx.beginPath();
|
||||
arrowCtx.moveTo(toX, toY);
|
||||
arrowCtx.lineTo(
|
||||
toX - headlen * Math.cos(angle - Math.PI / 7),
|
||||
toY - headlen * Math.sin(angle - Math.PI / 7)
|
||||
);
|
||||
|
||||
arrowCtx.lineTo(
|
||||
toX - headlen * Math.cos(angle + Math.PI / 7),
|
||||
toY - headlen * Math.sin(angle + Math.PI / 7)
|
||||
);
|
||||
|
||||
arrowCtx.lineTo(toX, toY);
|
||||
arrowCtx.lineTo(
|
||||
toX - headlen * Math.cos(angle - Math.PI / 7),
|
||||
toY - headlen * Math.sin(angle - Math.PI / 7)
|
||||
);
|
||||
|
||||
arrowCtx.strokeStyle = classificationColours.best;
|
||||
arrowCtx.lineWidth = width;
|
||||
arrowCtx.stroke();
|
||||
arrowCtx.fillStyle = classificationColours.best;
|
||||
arrowCtx.fill();
|
||||
|
||||
return arrowCtx.canvas;
|
||||
}
|
||||
|
||||
const startingPositionFen =
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
const classificationColours: { [key: string]: string } = {
|
||||
brilliant: "#1baaa6",
|
||||
great: "#5b8baf",
|
||||
best: "#98bc49",
|
||||
excellent: "#98bc49",
|
||||
good: "#97af8b",
|
||||
inaccuracy: "#f4bf44",
|
||||
mistake: "#e28c28",
|
||||
blunder: "#c93230",
|
||||
forced: "#97af8b",
|
||||
book: "#a88764",
|
||||
};
|
||||
|
||||
const pieceImagesSrc: Record<string, string | undefined> = {
|
||||
P: "white_pawn.svg",
|
||||
N: "white_knight.svg",
|
||||
B: "white_bishop.svg",
|
||||
R: "white_rook.svg",
|
||||
Q: "white_queen.svg",
|
||||
K: "white_king.svg",
|
||||
p: "black_pawn.svg",
|
||||
n: "black_knight.svg",
|
||||
b: "black_bishop.svg",
|
||||
r: "black_rook.svg",
|
||||
q: "black_queen.svg",
|
||||
k: "black_king.svg",
|
||||
};
|
||||
|
||||
const classificationIconsSrc: Record<string, string | undefined> = {
|
||||
brilliant: "brilliant.png",
|
||||
great: "great.png",
|
||||
best: "best.png",
|
||||
excellent: "excellent.png",
|
||||
good: "good.png",
|
||||
inaccuracy: "inaccuracy.png",
|
||||
mistake: "mistake.png",
|
||||
blunder: "blunder.png",
|
||||
forced: "forced.png",
|
||||
book: "book.png",
|
||||
};
|
||||
|
||||
async function loadImage(filename: string): Promise<HTMLImageElement> {
|
||||
return new Promise((res) => {
|
||||
const image = new Image();
|
||||
image.src = filename;
|
||||
|
||||
image.addEventListener("load", () => res(image));
|
||||
});
|
||||
}
|
||||
55
src/lib/evalBar.ts
Normal file
55
src/lib/evalBar.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
export async function drawEvaluationBar(
|
||||
evaluationBarCtx: CanvasRenderingContext2D,
|
||||
evaluation = initialEvaluation
|
||||
) {
|
||||
evaluationBarCtx.clearRect(0, 0, 30, 720);
|
||||
evaluationBarCtx.font = "16px Arial";
|
||||
evaluationBarCtx.fillStyle = "#1e1e1e";
|
||||
|
||||
if (evaluation.type == "cp") {
|
||||
let height = Math.max(Math.min(360 - evaluation.value / 3, 680), 40);
|
||||
evaluationBarCtx.fillRect(0, 0, 30, height);
|
||||
|
||||
let evaluationText = Math.abs(evaluation.value / 100).toFixed(1);
|
||||
let evaluationTextWidth =
|
||||
evaluationBarCtx.measureText(evaluationText).width;
|
||||
|
||||
let evaluationTextY = evaluation.value >= 0 ? 710 : 20;
|
||||
evaluationBarCtx.fillStyle = evaluation.value >= 0 ? "#1e1e1e" : "#ffffff";
|
||||
evaluationBarCtx.fillText(
|
||||
evaluationText,
|
||||
15 - evaluationTextWidth / 2,
|
||||
evaluationTextY,
|
||||
30
|
||||
);
|
||||
} else {
|
||||
let evaluationText = "M" + Math.abs(evaluation.value).toString();
|
||||
let evaluationTextWidth =
|
||||
evaluationBarCtx.measureText(evaluationText).width;
|
||||
|
||||
if (evaluation.value > 0) {
|
||||
evaluationBarCtx.fillStyle = "#1e1e1e";
|
||||
evaluationBarCtx.fillText(
|
||||
evaluationText,
|
||||
15 - evaluationTextWidth / 2,
|
||||
710,
|
||||
30
|
||||
);
|
||||
} else if (evaluation.value < 0) {
|
||||
evaluationBarCtx.fillRect(0, 0, 30, 720);
|
||||
|
||||
evaluationBarCtx.fillStyle = "#ffffff";
|
||||
evaluationBarCtx.fillText(
|
||||
evaluationText,
|
||||
15 - evaluationTextWidth / 2,
|
||||
20,
|
||||
30
|
||||
);
|
||||
} else if (evaluation.value == 0) {
|
||||
evaluationBarCtx.fillStyle = "#676767";
|
||||
evaluationBarCtx.fillRect(0, 0, 30, 720);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initialEvaluation = { type: "cp", value: 0 };
|
||||
15
src/pages/_app.tsx
Normal file
15
src/pages/_app.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
import "../../styles/global.css";
|
||||
import "../../styles/index.css";
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Free Chess</title>
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
15
src/pages/_document.tsx
Normal file
15
src/pages/_document.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
24
src/pages/index.tsx
Normal file
24
src/pages/index.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import TopBar from "@/sections/index/topBar";
|
||||
import Board from "@/sections/index/board";
|
||||
import ReviewPanelBody from "@/sections/index/reviewPanelBody";
|
||||
import ReviewPanelToolBar from "@/sections/index/reviewPanelToolbar";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<TopBar />
|
||||
|
||||
<div className="center">
|
||||
<div id="review-container">
|
||||
<Board />
|
||||
|
||||
<div id="review-panel">
|
||||
<ReviewPanelBody />
|
||||
|
||||
<ReviewPanelToolBar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
37
src/sections/index/board.tsx
Normal file
37
src/sections/index/board.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { drawBoard } from "@/lib/board";
|
||||
import { drawEvaluationBar } from "@/lib/evalBar";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
export default function Board() {
|
||||
const boardRef = useRef<HTMLCanvasElement>(null);
|
||||
const evalBarRef = useRef<HTMLCanvasElement>(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 (
|
||||
<div id="board-outer-container" className="center">
|
||||
<canvas id="evaluation-bar" width="30" height="720" ref={evalBarRef} />
|
||||
|
||||
<div id="board-inner-container" className="center">
|
||||
<div id="top-player-profile" className="profile">
|
||||
Black Player (?)
|
||||
</div>
|
||||
|
||||
<canvas id="board" width="720" height="720" ref={boardRef} />
|
||||
|
||||
<div id="bottom-player-profile" className="profile">
|
||||
White Player (?)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
src/sections/index/reviewPanelBody.tsx
Normal file
30
src/sections/index/reviewPanelBody.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import ReviewResult from "./reviewResult";
|
||||
import SelectDepth from "./selectDepth";
|
||||
import SelectGameOrigin from "./selectGame/selectGameOrigin";
|
||||
|
||||
export default function ReviewPanelBody() {
|
||||
return (
|
||||
<div id="review-panel-main">
|
||||
<h1 id="review-panel-title" className="white">
|
||||
📑 Game Report
|
||||
</h1>
|
||||
|
||||
<SelectGameOrigin />
|
||||
|
||||
<button id="review-button" className="std-btn success-btn white">
|
||||
<img src="analysis_icon.png" height="25" />
|
||||
<b>Analyse</b>
|
||||
</button>
|
||||
|
||||
<SelectDepth />
|
||||
|
||||
{false && <progress id="evaluation-progress-bar" max="100" />}
|
||||
|
||||
<b id="status-message" />
|
||||
|
||||
<b id="secondary-message" className="white" />
|
||||
|
||||
{false && <ReviewResult />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
src/sections/index/reviewPanelToolbar.tsx
Normal file
43
src/sections/index/reviewPanelToolbar.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
export default function ReviewPanelToolBar() {
|
||||
return (
|
||||
<div id="review-panel-toolbar">
|
||||
<div id="review-panel-toolbar-buttons" className="center">
|
||||
<img
|
||||
id="flip-board-button"
|
||||
src="flip.png"
|
||||
alt="Flip Board"
|
||||
title="Flip board"
|
||||
/>
|
||||
<img
|
||||
id="back-start-move-button"
|
||||
src="back_to_start.png"
|
||||
alt="Back to start"
|
||||
title="Back to start"
|
||||
/>
|
||||
<img id="back-move-button" src="back.png" alt="Back" title="Back" />
|
||||
<img id="next-move-button" src="next.png" alt="Next" title="Next" />
|
||||
<img
|
||||
id="go-end-move-button"
|
||||
src="go_to_end.png"
|
||||
alt="Go to end"
|
||||
title="Go to end"
|
||||
/>
|
||||
<img
|
||||
id="save-analysis-button"
|
||||
src="save.png"
|
||||
alt="Save analysis"
|
||||
title="Save analysis"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="white" style={{ marginBottom: "10px" }}>
|
||||
<input
|
||||
id="suggestion-arrows-setting"
|
||||
type="checkbox"
|
||||
style={{ marginRight: "0.4rem" }}
|
||||
/>
|
||||
<span>Suggestion Arrows</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
src/sections/index/reviewResult.tsx
Normal file
28
src/sections/index/reviewResult.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
export default function ReviewResult() {
|
||||
return (
|
||||
<div id="report-cards">
|
||||
<h2 id="accuracies-title" className="white">
|
||||
Accuracies
|
||||
</h2>
|
||||
<div id="accuracies">
|
||||
<b id="white-accuracy">0%</b>
|
||||
<b id="black-accuracy">0%</b>
|
||||
</div>
|
||||
|
||||
<div id="classification-message-container">
|
||||
<img id="classification-icon" src="book.png" height="25" />
|
||||
<b id="classification-message" />
|
||||
</div>
|
||||
|
||||
<b id="top-alternative-message">Qxf2+ was best</b>
|
||||
|
||||
<div id="engine-suggestions">
|
||||
<h2 id="engine-suggestions-title" className="white">
|
||||
Engine
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<span id="opening-name" className="white" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
src/sections/index/selectDepth.tsx
Normal file
22
src/sections/index/selectDepth.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
export default function SelectDepth() {
|
||||
return (
|
||||
<>
|
||||
<b className="white">⚙️ Search depth</b>
|
||||
<div id="depth-slider-container">
|
||||
<input
|
||||
id="depth-slider"
|
||||
type="range"
|
||||
min="14"
|
||||
max="20"
|
||||
defaultValue="16"
|
||||
/>
|
||||
<span id="depth-counter" className="white">
|
||||
16 🐇
|
||||
</span>
|
||||
</div>
|
||||
<h6 id="depth-message" className="white">
|
||||
Lower depths recommended for slower devices.
|
||||
</h6>
|
||||
</>
|
||||
);
|
||||
}
|
||||
16
src/sections/index/selectGame/inputGame.tsx
Normal file
16
src/sections/index/selectGame/inputGame.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
interface Props {
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export default function InputGame({ placeholder }: Props) {
|
||||
return (
|
||||
<textarea
|
||||
id="pgn"
|
||||
className="white"
|
||||
cols={30}
|
||||
rows={10}
|
||||
spellCheck="false"
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
22
src/sections/index/selectGame/selectGameAccount.tsx
Normal file
22
src/sections/index/selectGame/selectGameAccount.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { GameOrigin } from "@/types";
|
||||
|
||||
interface Props {
|
||||
gameOrigin: GameOrigin;
|
||||
}
|
||||
|
||||
export default function SelectGameAccount({}: Props) {
|
||||
return (
|
||||
<div id="chess-site-username-container">
|
||||
<textarea
|
||||
id="chess-site-username"
|
||||
className="white"
|
||||
spellCheck="false"
|
||||
maxLength={48}
|
||||
placeholder="Username..."
|
||||
/>
|
||||
<button id="fetch-account-games-button" className="std-btn success-btn">
|
||||
<img src="next.png" alt=">" height="25" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
src/sections/index/selectGame/selectGameOrigin.tsx
Normal file
46
src/sections/index/selectGame/selectGameOrigin.tsx
Normal file
@@ -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 (
|
||||
<>
|
||||
<div id="load-type-dropdown-container" className="white">
|
||||
<span style={{ marginRight: "0.3rem" }}>Load game from</span>
|
||||
<select
|
||||
id="load-type-dropdown"
|
||||
value={gameOrigin}
|
||||
onChange={(e) => setGameOrigin(e.target.value as GameOrigin)}
|
||||
>
|
||||
{Object.values(GameOrigin).map((origin) => (
|
||||
<option key={origin} value={origin}>
|
||||
{gameOriginLabel[origin]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{renderSelectGameInfo(gameOrigin)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const gameOriginLabel: Record<GameOrigin, string> = {
|
||||
[GameOrigin.Pgn]: "PGN",
|
||||
[GameOrigin.ChessCom]: "Chess.com",
|
||||
[GameOrigin.Lichess]: "Lichess",
|
||||
[GameOrigin.Json]: "JSON",
|
||||
};
|
||||
|
||||
const renderSelectGameInfo = (gameOrigin: GameOrigin) => {
|
||||
switch (gameOrigin) {
|
||||
case GameOrigin.Pgn:
|
||||
return <InputGame placeholder="Enter PGN here..." />;
|
||||
case GameOrigin.Json:
|
||||
return <InputGame placeholder="Enter JSON here..." />;
|
||||
default:
|
||||
return <SelectGameAccount gameOrigin={gameOrigin} />;
|
||||
}
|
||||
};
|
||||
7
src/sections/index/topBar.tsx
Normal file
7
src/sections/index/topBar.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function TopBar() {
|
||||
return (
|
||||
<div id="announcement">
|
||||
<b>Welcome ❤️</b>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
6
src/types/index.tsx
Normal file
6
src/types/index.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum GameOrigin {
|
||||
Pgn = "pgn",
|
||||
ChessCom = "chesscom",
|
||||
Lichess = "lichess",
|
||||
Json = "json",
|
||||
}
|
||||
Reference in New Issue
Block a user